h2. Basic authorization This section describes the default authorization functionality of ApacheDS 2.0, which is very simple. On the other hand, it is inadequate for most serious deployments. Therefore a basic example to the "real" authorization subsystem is provided as well. h3. What is authorization? After authentication of a user or an application (or more generally an LDAP client) against the directory server (or attaining anonymous access respectively), certain LDAP operations will be granted or rejected, according to configuration and certain rules. This process of granting access is called authorization. Authorization for directory operations is not strictly standardized in the LDAP world, [RFC 4513|http://www.faqs.org/rfcs/rfc4513.html|www.faqs.org] describes various scenarios and concepts, but does not enforce a concrete implementation. Thus each product comes with its own authorization feature. So does ApacheDS. A powerful authorization subsystem is provided since version 0.9.3, but disabled as a default. h4. Authorization for directory operations vs. group membership In order to accomplish their authorization functionality, software components often take advantage of LDAP groups stored within the directory. _groupOfNames_ and _groupOfUniqueNames_ are common object classes for groups entries; they contain the DNs of their members (users, other groups) as attribute values. In order to illustrate this, the "Seven Seas" example partition contains such group entries below "ou=groups,o=sevenSeas". Here the entry of a group describing the HMS Bounty crew (before the mutiny) in LDIF format. {code:none} dn: cn=HMS Bounty,ou=crews,ou=groups,o=sevenSeas objectclass: groupOfUniqueNames objectclass: top cn: HMS Bounty uniquemember: cn=William Bligh,ou=people,o=sevenSeas uniquemember: cn=Fletcher Christian,ou=people,o=sevenSeas uniquemember: cn=John Fryer,ou=people,o=sevenSeas ... {code} In such a scenario, a user, who is directly or indirectly member of a certain group is permitted to do something. The software component acts as a normal LDAP client and determines group belonging with the help of ordinary search operations. This is widely used but has nothing to do with the authorization for directory operations as described in this section (except that the client needs the permission to search the data). Learn more about best practices in this area in the article [Practices in Directory Groups|http://middleware.internet2.edu/dir/groups/docs/internet2-mace-dir-groups-best-practices-200210.htm]. Further examples in this guide are the Tomcat and Apache HTTPD integration sections. h3. Default authorization behavior for directory operations Without access controls enabled all entries are accessible and alterable by all: even anonymous users. There are however some minimal built-in rules for protecting users and groups within the server without having to turn on the ACI subsystem. h4. Sample data within "ou=users,ou=system" In addition to our brave sailors below _ou=people,o=sevenSeas_, assume the following to entries present within _ou=users,ou=system_: {noformat} dn: cn=Tori Amos,ou=users,ou=system objectclass: person objectclass: top sn: Amos cn: Tori Amos userpassword: amos dn: cn=Kate Bush,ou=users,ou=system objectclass: person objectclass: top sn: Bush cn: Kate Bush userpassword: bush {noformat} Here is an LDAP Browser view of the entries from Apache Directory Studio !authorization_sample_entries.png! The two entries are used in the following examples, in conjunction with _o=sevenSeas_, to describe the default authorization rules. h4. Rules and sample operations Without ACIs the server automatically protects, hides, the admin user from everyone but the admin user. Here a sample search operation in order to demonstrate this protection. We use a [command line tool|2.2.2. Command line tools] for this task for the ease of documentation; you can perform the same operations with a UI tool like Apache Directory Studio. The same command is submitted three times with different users. {noformat} $ ldapsearch -h zanzibar -p 10389 -D "uid=admin,ou=system" -w secret \\ -b "ou=system" -s one "(uid=admin)" dn version: 1 dn: uid=admin,ou=system $ ldapsearch -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\ -b "ou=system" -s one "(uid=admin)" dn $ ldapsearch -h zanzibar -p 10389 -D "cn=Tori Amos,ou=users,ou=system" -w amos \\ -b "ou=system" -s one "(uid=admin)" dn $ {noformat} Users cannot see other user entries under the 'ou=users,ou=system' entry. So placing new users there automatically protects them. Placing new users anywhere else exposes them. {noformat} $ ldapsearch -h zanzibar -p 10389 -D "uid=admin,ou=system" -w secret \\ -b "ou=users,ou=system" -s one "(objectclass=*)" dn version: 1 dn: cn=Tori Amos,ou=users,ou=system dn: cn=Kate Bush,ou=users,ou=system $ ldapsearch -h zanzibar -p 10389 -D "cn=Kate Bush,ou=users,ou=system" -w bush \\ -b "ou=users,ou=system" -s one "(objectclass=*)" dn version: 1 dn: cn=Kate Bush,ou=users,ou=system $ ldapsearch -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\ -b "ou=users,ou=system" -s one "(objectclass=*)" dn $ ldapsearch -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\ -b "ou=people,o=sevenSeas" -s one "(objectclass=*)" dn version: 1 dn: cn=Horatio Hornblower,ou=people,o=sevenSeas dn: cn=William Bush,ou=people,o=sevenSeas dn: cn=Thomas Masterman Hardy,ou=people,o=sevenSeas dn: cn=Cornelius Buckley,ou=people,o=sevenSeas dn: cn=William Bligh,ou=people,o=sevenSeas ... $ {noformat} Groups defined using _groupOfNames_ or _groupOfUniqueNames_ under the 'ou=groups,ou=system' are also protected from access or alteration by anyone other than the admin user. Again this protection is not allowed anywhere else but under these entries. h4. Is this sufficient? For simple configurations the described rules should provide adequate protection but it lacks flexibility. For advanced configurations users should enable the ACI subsystem. This however shuts down access to everything by everyone except the admin user which bypasses the ACI subsystem. Directory administrators should look at the documentation on how to specify access control information in the Advanced User's Guide. h3. Simple example for the ACI subsystem As an appetizer for the stunning ACI subsystem (ACI = access control item) within ApacheDS, we provide a simple yet realistic example. It manifests the following requirements h4. Requirements met # Suffix "o=sevenSeas" used as Access Control Specific Area # User "cn=Horatio Nelson,ou=people,o=sevenSeas" should be able to perform all operations (delete, add, ...) below the base "o=sevenSeas" # Other users and anonymous users should only be able to search and compare (no add, modify etc.) # Other users and anonymous users should not be able to read the userPassword attribute h4. Enable the ACI Subsystem The authorization (ACI) subsystem is disabled by default. If you use the server standalone configured with a _server.xml_ file, you can enable it by changing the value for attribute_accessControlEnabled_ in the defaultDirectoryService element, as depicted in the following fragment: {noformat} ... ... ...{noformat} A restart of the server is necessary for this change to take effect. h4. Further configuration tasks to perform afterwards 1. Create an operational attribute _administrativeRole_ with value "accessControlSpecificArea" in the entry "o=sevenSeas". 2. Create a subentry subordinate to "o=sevenSeas" to grant all operations' permissions to "cn=Horatio Nelson,ou=people,o=sevenSeas", who acts as directory manager The subentry should contain the following attributes and values: {noformat} cn="sevenSeasAuthorizationRequirementsACISubentry" subtreeSpecification="{}" prescriptiveACI="{ identificationTag "directoryManagerFullAccessACI", precedence 11, authenticationLevel simple, itemOrUserFirst userFirst: { userClasses { name { "cn=Horatio Nelson,ou=people,o=sevenSeas" } }, userPermissions { { protectedItems { entry, allUserAttributeTypesAndValues }, grantsAndDenials { grantAdd, grantDiscloseOnError, grantRead, grantRemove, grantBrowse, grantExport, grantImport, grantModify, grantRename, grantReturnDN, grantCompare, grantFilterMatch, grantInvoke } } } } }" {noformat} 3. A new attribute value should added to the previously created Subentry's prescriptiveACI attribute to grant search and compare permissions to all users. The new value: {noformat} prescriptiveACI="{ identificationTag "allUsersSearchAndCompareACI", precedence 10, authenticationLevel simple, itemOrUserFirst userFirst: { userClasses { allUsers }, userPermissions { { protectedItems { entry, allUserAttributeTypesAndValues }, grantsAndDenials { grantRead, grantBrowse, grantReturnDN, grantCompare, grantFilterMatch, grantDiscloseOnError } } } } }" {noformat} 4. A new attribute value should added to the previously created Subentry's prescriptiveACI attribute to deny search and compare permissions for _userPassword_ attribute to all users. The new value: {noformat} prescriptiveACI="{ identificationTag "preventAllUsersFromReadingUserPasswordAttributeACI", precedence 10, authenticationLevel simple, itemOrUserFirst userFirst: { userClasses { allUsers }, userPermissions { { protectedItems { attributeType { userPassword } }, grantsAndDenials { denyRead, denyCompare, denyFilterMatch } } } } }" {noformat} The two values given in 3 and 4 can be combined in a single value as: {noformat} prescriptiveACI="{ identificationTag "allUsersACI", precedence 10, authenticationLevel none, itemOrUserFirst userFirst: { userClasses { allUsers }, userPermissions { { protectedItems { entry, allUserAttributeTypesAndValues }, grantsAndDenials { grantRead, grantBrowse, grantReturnDN, grantCompare, grantFilterMatch, grantDiscloseOnError } }, { protectedItems { attributeType { userPassword } }, grantsAndDenials { denyRead, denyCompare, denyFilterMatch } } } } }" {noformat} h4. LDIF for this configuration The following LDIF file ([^authz_sevenSeas.ldif]) provides a set of changes made to directory entries in the "Seven Seas" data. In total it performs the steps described above. {code:none} # File authz_sevenSeas.ldif # # Create an operational attribute "administrativeRole" # with value "accessControlSpecificArea" in the entry "o=sevenSeas". # dn: o=sevenSeas changetype: modify add: administrativeRole administrativeRole: accessControlSpecificArea # Create a subentry subordinate to "o=sevenSeas" to grant all operations' permissions # to "cn=Horatio Nelson,ou=people,o=sevenSeas", to grant search and compare permissions # to all users and to deny search and compare permissions for userPassword attribute to all users. # dn: cn=sevenSeasAuthorizationRequirementsACISubentry,o=sevenSeas changetype: add objectclass: top objectclass: subentry objectclass: accessControlSubentry cn: sevenSeasAuthorizationRequirementsACISubentry subtreeSpecification: {} prescriptiveACI: { identificationTag "directoryManagerFullAccessACI", precedence 11, authenticationLevel simple, itemOrUserFirst userFirst: { userClasses { name { "cn=Horatio Nelson,ou=people,o=sevenSeas" } }, userPermissions { { protectedItems { entry, allUserAttributeTypesAndValues }, grantsAndDenials { grantAdd, grantDiscloseOnError, grantRead, grantRemove, grantBrowse, grantExport, grantImport, grantModify, grantRename, grantReturnDN, grantCompare, grantFilterMatch, grantInvoke } } } } } prescriptiveACI: { identificationTag "allUsersACI", precedence 10, authenticationLevel none, itemOrUserFirst userFirst: { userClasses { allUsers }, userPermissions { { protectedItems { entry, allUserAttributeTypesAndValues }, grantsAndDenials { grantRead, grantBrowse, grantReturnDN, grantCompare, grantFilterMatch, grantDiscloseOnError } }, { protectedItems { attributeType { userPassword } }, grantsAndDenials { denyRead, denyCompare, denyFilterMatch } } } } } {code} To apply this configuration to the sample data partition, you can perform an _ldapmodify_ with the LDIF as argument: {noformat} $ ldapmodify -h zanzibar -p 10389 -D "uid=admin,ou=system" -w secret -f authz_sevenSeas.ldif modifying entry o=sevenSeas adding new entry cn=sevenSeasAuthorizationRequirementsACISubentry,o=sevenSeas $ {noformat} It is also possible to use graphical tools; some of them offer the feature to perform operations given in LDIF. h3. Verification, that it works After successfully applying the changes to the sample partition, one may ask how to check whether it works. We therefore perform some operations with the help of command line tools. Some will be permitted, some will not (and cause an appropriate error message). It would also be able to check this with the help of graphical tools (you might like to do this instead). But it is easier to document the parameters used with the help command line arguments. h4. Performing some search operations in order to read data Bind as user "William Bush" and search for entries which match "(uid=hhornblo)". Expected behavior: We are able to read the attributes of entry "cn=Horatio Hornblower,ou=people,o=sevenSeas" (the only entry which matches the filter). The password attribute should not be visible. It works as desired: {code:none} $ ldapsearch -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\ -b "o=sevenSeas" -s sub "(uid=hhornblo)" version: 1 dn: cn=Horatio Hornblower,ou=people,o=sevenSeas mail: hhornblo@royalnavy.mod.uk objectclass: person objectclass: organizationalPerson objectclass: inetOrgPerson objectclass: top cn: Horatio Hornblower uid: hhornblo givenname: Horatio description: Capt. Horatio Hornblower, R.N sn: Hornblower {code} In the described configuration, the user "Horatio Nelson" acts as a directory manager below "o=sevenSeas". Hence he should basically be allowed to do everything. He should even be able to see other users' _userPassword_ values. In our case, the hash function _SHA_ was applied to them: {code:none} $ ldapsearch -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\ -b "o=sevenSeas" -s sub "(objectclass=person)" uid userPassword version: 1 dn: cn=Horatio Hornblower,ou=people,o=sevenSeas userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= uid: hhornblo dn: cn=William Bush,ou=people,o=sevenSeas userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= uid: wbush dn: cn=Thomas Quist,ou=people,o=sevenSeas userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ= uid: tquist ... {code} But "Horation Nelson" is not able to perform searches in other areas than "o=sevenSeas" to see the entries. Of course our global ApacheDS administrator "uid=admin,ou=system" is still able to see them: {code:none} $ ldapsearch -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\ -b "ou=system" -s sub "(objectclass=person)" $ ldapsearch -h zanzibar -p 10389 -D "uid=admin,ou=system" -w secret \\ -b "ou=system" -s sub "(objectclass=person)" version: 1 dn: uid=admin,ou=system sn: administrator cn: system administrator objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson userpassword: secret uid: admin displayName: Directory Superuser dn: cn=Tori Amos,ou=users,ou=system cn: Tori Amos userpassword: amos objectclass: person objectclass: top sn: Amos ... {code} h4. Trying to manipulate data Until now the authorization only hided data (entries, attributes) from users with insufficient access rights. Let's perform some operations which try to manipulate the directory data! h5. Adding an entry First we try to add a new user to the "Seven Seas" partition. The data for the entry is inspired by "Peter Pan" and provided by this LDIF file ([^captain_hook.ldif]): {code:none} # File captain_hook.ldif dn: cn=James Hook,ou=people,o=sevenSeas objectclass: inetOrgPerson objectclass: organizationalPerson objectclass: person objectclass: top cn: James Hook description: A pirate captain and Peter Pan's nemesis sn: Hook mail: jhook@neverland userpassword: peterPan {code} An anonymous user is not allowed to create new entries, as the following error message shows: {code:none} $ ldapmodify -h zanzibar -p 10389 -a -f captain_hook.ldif adding new entry cn=James Hook,ou=people,o=sevenSeas ldap_add: Insufficient access ldap_add: additional info: failed to add entry cn=James Hook,ou=people,o=sevenSeas: null $ {code} Of course, the "Insufficient access" (LDAP result code 50) error would be presented by other client as wee, Apache Directory Studio, if you try to manipulate entries with an anonymous access, for instance. The same holds true for all "Seven Seas"-user other than "Horatio Nelson". The latter is permitted to do so: {code:none} $ ldapmodify -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\ -a -f captain_hook.ldif adding new entry cn=James Hook,ou=people,o=sevenSeas ldap_add: Insufficient access ldap_add: additional info: failed to add entry cn=James Hook,ou=people,o=sevenSeas: null $ ldapmodify -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\ -a -f captain_hook.ldif adding new entry cn=James Hook,ou=people,o=sevenSeas $ {code} Afterwards a new entry is successfully created within the "Seven Seas" partition by user "Horatio Nelson". The '+' sign in the attributes list of the _ldapsearch_ command causes ApacheDS to return the operational attributes, which demonstrate this. {code:none} $ ldapsearch -h zanzibar -p 10389 -b "o=sevenSeas" -s sub "(cn=James Hook)" + version: 1 dn: cn=James Hook,ou=people,o=sevenSeas accessControlSubentries: cn=sevenSeasAuthorizationRequirementsACISubentry,o=sevenSeas creatorsName: cn=Horatio Nelson,ou=people,o=sevenSeas createTimestamp: 20071228172613Z {code} h5. Modifying an entry As a further example which tries to write to the directory, we add a new value to the description attribute of the freshly created entry for Captain Hook. With a change entry in an LDIF file, it looks like this (file [^captain_hook_modify.ldif]): {code:none} # File captain_hook_modify.ldif dn: cn=James Hook,ou=people,o=sevenSeas changetype: modify add: description description: Wears an iron hook in place of his right hand - {code} Performing the modification with the _ldapmodify_ command line tool again fails for users other than "Horation Nelson" (who is allowed to due to the authorization configuration) and "uid=admin,ou=system". {code:none} $ ldapmodify -h zanzibar -p 10389 -f captain_hook_modify.ldif modifying entry cn=James Hook,ou=people,o=sevenSeas ldap_modify: Insufficient access ldap_modify: additional info: failed to modify entry cn=James Hook,ou=people,o=sevenSeas: null $ ldapmodify -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\ -f captain_hook_modify.ldif modifying entry cn=James Hook,ou=people,o=sevenSeas ldap_modify: Insufficient access ldap_modify: additional info: failed to modify entry cn=James Hook,ou=people,o=s evenSeas: null $ ldapmodify -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\ -f captain_hook_modify.ldif modifying entry cn=James Hook,ou=people,o=sevenSeas {code} h5. Deleting an entry Now it is finale time. A demonstration on how to delete the villain's entry from the directory. With an LDIF file ([^captain_hook_delete.ldif]) with an appropriate change entry, this can easily be accomplished, if the bind user is allowed to do so. {code:none} # File captain_hook_delete.ldif dn: cn=James Hook,ou=people,o=sevenSeas changetype: delete {code} Applying this file with the help of _ldapmodify_ results in a behavior comparable to the modification. Anonymous or "normal" users (like "William Bush") are not permitted to delete Captain Hook's entry. The user "Horatio Nelson", our directory manager for "Seven Seas", is: {code:none} $ ldapmodify -h zanzibar -p 10389 -f captain_hook_delete.ldif deleting entry cn=James Hook,ou=people,o=sevenSeas ldap_delete: Insufficient access ldap_delete: additional info: failed to delete entry cn=James Hook,ou=people,o=sevenSeas: null $ ldapmodify -h zanzibar -p 10389 -D "cn=William Bush,ou=people,o=sevenSeas" -w pass \\ -f captain_hook_delete.ldif deleting entry cn=James Hook,ou=people,o=sevenSeas ldap_delete: Insufficient access ldap_delete: additional info: failed to delete entry cn=James Hook,ou=people,o=sevenSeas: null $ ldapmodify -h zanzibar -p 10389 -D "cn=Horatio Nelson,ou=people,o=sevenSeas" -w pass \\ -f captain_hook_delete.ldif deleting entry cn=James Hook,ou=people,o=sevenSeas $ {code} The entry "cn=James Hook,ou=people,o=sevenSeas" has been successfully deleted from the partition. Our little demonstration on how the ACI subsystem with a realistic configuration behaves ends here. Learn more about it in the Advanced User's Guide. {anchor:Resources_1} h3. Resources * [Practices in Directory Groups|http://middleware.internet2.edu/dir/groups/docs/internet2-mace-dir-groups-best-practices-200210.htm] describes how to use groups within LDAP directories. Highly recommended. * [RFC 2849|http://www.faqs.org/rfcs/rfc2849.html] The LDAP Data Interchange Format (LDIF) is used extensively in this section * The ApacheDS v2.0 Advanced User's Guide will provide a detailed authorization chapter