3.2 - Basic authorization

This section describes the default authorization functionality of ApacheDS 1.5, 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.

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 2829 - Authentication Methods for LDAP 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.

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.

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
...

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. Further examples in this guide are the Tomcat and Apache HTTPD integration sections.

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.

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:

Authorization Sample Entries

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

They are used in the following examples, in conjunction with o=sevenSeas, to describe the default authorization rules.

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. The same command is submitted three times with different users.

$ 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

$

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.

$ 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
...
$

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.

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.

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

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

Enable the ACI Subsystem

The authorization (ACI) subsystem is disabled by default. You have to enable it using the Apache Directory Studio configuration editor :

Enable access control

A restart of the server is necessary for this change to take effect.

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:

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
                     } 
                   }
                 }
               } 
             }"
  1. 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:

prescriptiveACI="{
               identificationTag "allUsersSearchAndCompareACI",
               precedence 10,
               authenticationLevel simple,
               itemOrUserFirst userFirst:
               {
                 userClasses
                 {
                   allUsers
                 },
                 userPermissions
                 { 
                   {
                     protectedItems
                     {
                       entry, allUserAttributeTypesAndValues
                     },
                     grantsAndDenials
                     {
                       grantRead, grantBrowse, grantReturnDN,
                       grantCompare, grantFilterMatch, grantDiscloseOnError 
                     } 
                   }
                 }
               } 
             }"
  1. 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:

prescriptiveACI="{
               identificationTag "preventAllUsersFromReadingUserPasswordAttributeACI",
               precedence 10,
               authenticationLevel simple,
               itemOrUserFirst userFirst:
               {
                 userClasses
                 {
                   allUsers
                 },
                 userPermissions
                 { 
                   {
                     protectedItems
                     {
                       attributeType { userPassword }
                     },
                     grantsAndDenials
                     {
                       denyRead, denyCompare, denyFilterMatch
                     } 
                   }
                 }
               } 
             }"

The two values given in 3 and 4 can be combined in a single value as:

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 }
                   }
                 }
               } 
             }"

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.

# 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 }
            }
        }   
    }
  }

To apply this configuration to the sample data partition, you can perform an ldapmodify with the LDIF as agrument:

$ 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
$

It is also possible to use graphical tools; some of them offer the feature to perform operations given in LDIF.

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.

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:

$ 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

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:

$ 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
...

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:

$ 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
...

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!

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):

# 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

An anonymous user is not allowed to create new entries, as the following error message shows:

$ 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
$

The same holds true for all "Seven Seas"-user other than "Horatio Nelson". The latter is permitted to do so:

$ 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
$

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.

$ 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: 20061203140109Z

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):

# 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
-

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".

$ 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=sevenSeas: 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

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) with an appropriate change entry, this can easily be accomplished, if the bind user is allowed to do so.

# File captain_hook_delete.ldif
dn: cn=James Hook,ou=people,o=sevenSeas
changetype: delete

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:

$ 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
$

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 end here. Learn more about it in the Advanced User's Guide.

Resources