public class LdapEntry
extends jespa.security.Properties
LdapSecurityProvider.getEntry(java.lang.Object, java.lang.String[])
and LdapSecurityProvider.getAccount(java.lang.String, java.lang.String[])
.
The following code fragment illustrates how to retrieve an entry from the directory bound to a provider and iterate through it's attributes.
LdapSecurityProvider.getEntry(java.lang.Object, java.lang.String[])
is an instance of LdapEntry.
However, only the create, update and delete methods require an LdapEntry type.
The remainder of the API implements the standard Map, Map.Entry and List interfaces.
HashMap props = new HashMap(); props.put("bindstr", "ldap://busicorp.local/CN=Users,DC=busicorp,DC=local"); props.put("dns.servers", "192.168.44.110,192.168.22.115"); props.put("dns.site", "Paris"); props.put("service.acctname", "scott@busicorp.local"); props.put("service.password", "tiger"); LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { Map entry = lsp.getEntry("CN=Wiki Users", LdapEntry.ALL_ATTRS); // Iterate over each entry attribute Iterator<Map.Entry<String,Object>> iter = entry.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String,Object> me = iter.next(); String key = me.getKey(); Object val = me.getValue(); if (val instanceof List) { // This attribute is multi-valued, iterate over each value System.out.println(key + ":"); List lval = (List)val; Iterator liter = lval.iterator(); while (liter.hasNext()) { System.out.println(" " + liter.next()); } } else { // Otherwise, this attribute is single-valued System.out.println(key + ": " + val); } } } finally { lsp.dispose(); } -- output -- name: Wiki Users instanceType: 4 groupType: -2147483646 objectSid: S-1-5-21-2779991729-2620583212-3498051119-29640 sAMAccountType: 268435456 member: CN=Edward Foley,CN=Users,DC=busicorp,DC=local CN=Bob Carter,CN=Users,DC=busicorp,DC=local uSNCreated: 396870 uSNChanged: 2834309 objectClass: top group distinguishedName: CN=Wiki Users,CN=Users,DC=busicorp,DC=local objectCategory: CN=Group,CN=Schema,CN=Configuration,DC=busicorp,DC=local sAMAccountName: Wiki Users objectGUID: QE7LWVIVlEC8yzuLC8mZfA== cn: Wiki Users whenCreated: 1200075597043 whenChanged: 1268270754043 dSCorePropagationData: 1253209645043 -11644473598957
As illustrated in the above output, attribute values are automatically converted to forms ideal for the developer and for manipulation using the Java language.
Times are automatically converted to milliseconds since 1970, binary attributes are automatically base 64 encoded, Windows SIDs are converted to their much more comprehensible text representation, and so on.
This "attribute intelligence" is provided by attribute definitions which can be customized using the LdapAttrDef
class.
The developer will usually need to know in advance what type of Object will be used to represent the value of a given attribute internally. However, as illustrated in the example above, instanceof List may be used to determine at runtime if an attribute is multi-valued. Similarly instanceof could be used to determine if the value is a String, byte[], jespa.util.SID or another type.
create(java.lang.String[])
method description for details and examples.
To update an entry, first retrieve it with LdapSecurityProvider.getEntry and cast it into an LdapEntry.
Then modify the attributes as desired using put(), remove(), List.add() and List.remove().
Internally, these transformations are reduced into a fully normalized list of LDAP add, delete and replace operations.
When update() is called, the list of LDAP operations is commited to the LDAP directory.
See the update(java.lang.String[])
method description for details and examples.
To delete an entry, simply retrieve the entry with getEntry and call delete.
See the delete()
method description for details and examples.
When creating and updating entries, the developer must have knowledge of what attributes are appropriate for each type of entry.
The LdapSearch
utility may help with this process.
The following truth table lists the result of every possible modification to an LdapEntry object using put(), remove() and List.add(). It should generally not be necessary to consult this table as modifications on LdapEntry objects are almost always very simple and when they are not, the result is what was expected by the developer. However, if there is any confusion, the below table is provided to explicitly state what the correct behavior is.
The below table also illustrates that all LdapEntry modifications are normalized before they are committed to the directory with a call to update(java.lang.String[])
.
The update() method submits all modifications within one LDAP operation to the directory.
Only calls to create(), update() and delete() result in submitting LDAP operations to the directory.
How a call to put() is translated into LDAP add or replace operations | ||
---|---|---|
Does a value already exist? | Is an operation already queued? | Resulting LDAP operation with update() |
no | no | add |
yes | no | replace unless value is exactly the same in which case none |
no | yes | add unless current op is delete in which case replace |
yes | yes | add unless current op is not add in which case replace |
How a call to remove() is translated into LDAP replace or delete operations | ||
Does a value already exist? | Is an operation already queued? | Resulting LDAP operation with update() |
no | no | none |
yes | no | delete |
no | yes | delete |
yes | yes | none unless current op is replace in which case delete |
How a call to List.add() is translated into LDAP add operations | ||
Does the same value already exist? | Is an operation already queued? | Resulting LDAP operation with update() |
no | no | add |
yes | no | none |
no | yes | add unless current op is delete for same value in which case none |
yes | yes | none |
For example, the first row of this table indicates that when an attribute is added to an entry with the put() method, if a value does not already exist in the directory and an LDAP operation for the named attribute does not already exist in the list of LDAP operations to be performed (when update() is called), an LDAP add operation is inserted into the list of LDAP operations to be performed. The second row indicates that if a value does exist but it is different from the value being set, an LDAP replace operation is inserted in the list of LDAP operations. And so on.
LDAP servers like Active Directory have sophisticated access controls. Some users may not have sufficient permission to perform the desired operation in which case an LdapException may be thrown with error codes like:
LDAP_INSUFFICIENT_ACCESS LDAP_UNWILLING_TO_PERFORM LDAP_STRONG_AUTH_REQUIRED ...
This might be remedied by using an account with greater permissions like an Account Operator but in some cases permissions are controlled at the attribute level. For example, the whenCreated attribute is a System-Only attribute and therefore it cannot be modified at all even by an Administrator.
Modifier and Type | Field and Description |
---|---|
static java.lang.String[] |
ALL_ATTRS
Used as a parameter with
create(String[]) and update(String[]) to indicate that "all attributes" of this entry should be created or updated. |
Constructor and Description |
---|
LdapEntry(LdapSecurityProvider provider,
java.lang.String distinguishedName)
Construct an LdapEntry object for the purpose of performing LDAP operations on individual entries.
|
Modifier and Type | Method and Description |
---|---|
void |
clear()
This method of the Map interface is currently not implemented.
|
boolean |
containsKey(java.lang.Object key)
Return true if this entry contains the named attribute.
|
boolean |
containsValue(java.lang.Object value)
This method of the Map interface is currently not implemented.
|
void |
create()
Create a new entry with this entry's Distinguished Name in the LDAP directory identified by the underlying LdapSecurityProvider.
|
void |
create(java.lang.String[] attrs)
Create a new entry with this entry's Distinguished Name in the LDAP directory identified by the underlying LdapSecurityProvider.
|
void |
delete()
Delete the existing entry with this entry's Distinguished Name from the LDAP directory identified by the underlying LdapSecurityProvider.
|
java.util.Set<java.util.Map.Entry<java.lang.String,java.lang.Object>> |
entrySet()
Return a Set of Map.Entry objects representing the attributes of this entry.
|
boolean |
equals(java.lang.Object o)
Return true only if the DN, attribute names and values of the supplied LdapEntry are equal to the DN, attribute names and values of this entry.
|
java.lang.Object |
get(java.lang.Object name)
Retrieve the named attribute value or return null if this entry does not contain the named attribute.
|
protected java.lang.Object |
get(java.lang.Object name,
LdapAttrDef def) |
java.lang.Object |
getProperty(java.lang.String name,
java.lang.Object def)
Return the named attribute or the supplied default value if this entry does not contain the named attribute.
|
int |
hashCode()
Returns a hash code for the underlying attribute list of this entry.
|
boolean |
isEmpty()
Return true if this entry contains no attributes.
|
java.util.Set<java.lang.String> |
keySet()
This method of the Map interface is currently not implemented.
|
java.lang.Object |
put(java.lang.Object name,
java.lang.Object value)
Put a named attribute value into this entry.
|
void |
putAll(java.util.Map m)
This method of the Map interface is currently not implemented.
|
java.lang.Object |
remove(java.lang.Object name)
Remove the named attribute value from this entry.
|
int |
size()
Return the number of attributes in this entry.
|
void |
update()
Update an entry with this entry's Distinguished Name in the LDAP directory identified by the underlying LdapSecurityProvider.
|
void |
update(java.lang.String[] attrs)
Update an existing entry with this entry's Distinguished Name in the LDAP directory identified by the underlying LdapSecurityProvider.
|
java.util.Collection<java.lang.Object> |
values()
This method of the Map interface is currently not implemented.
|
decodeObject, encodeObject, getEncryptedProperty, getFilteredProperties, getFilteredProperties, getProperty, getPropertyAsBoolean, getPropertyAsLong, setEncryptedProperty, setProperty
public static final java.lang.String[] ALL_ATTRS
create(String[])
and update(String[])
to indicate that "all attributes" of this entry should be created or updated.
This parameter is logically equal to the ALL_ATTRS constant of other classes like LdapAccount and may be used interchangably.public LdapEntry(LdapSecurityProvider provider, java.lang.String distinguishedName) throws SecurityProviderException
LdapSecurityProvider.getEntry(java.lang.Object, java.lang.String[])
.
However, when creating a new entry in the directory with create, this constructor must be used.
See the create(String[])
method description for examples.provider
- the LdapSecurityProvider representing the authority for this entrydistinguishedName
- the DN that uniquely identifies this entry (or RDN relative to the base in the provider bindstr)SecurityProviderException
- if the distinguishedName is not valid or if certain properties cannot be retrieved from the providerpublic java.lang.Object getProperty(java.lang.String name, java.lang.Object def) throws SecurityProviderException
get(java.lang.Object)
but adds the convenience of returning a default value if necessary.
See the get(java.lang.Object)
method description for details.getProperty
in class jespa.security.Properties
name
- the name of the attribute (such as "sAMAccountName" or "gidNumber")def
- the default value to return if this entry does not contain the named attributeSecurityProviderException
- if a catostrophic error occurs (although currently no such error can occur)public java.lang.Object get(java.lang.Object name)
Attribute names are defined by the directory with which this entry is bound through the underlying LdapSecurityProvider.
Possible attribute names may be determined by inspecting entries during the development process (such as with the LdapSearch
utility.
However, all LdapEntry objects will have a distinguishedName attribute with a value that is the full DN of the entry (regardless of what attributes are requested with LdapSecurityProvider.getEntry(java.lang.Object, java.lang.String[])
).
The type of the attribute value returned depends on the attribute and more specifically it's attribute definition.
Possible attribute types may be determined by querying the LdapAttrDef
for the named attribute, with the instanceof operator or more commonly by simply inspecting values returned during the development process.
The most common attribute values returned are String and byte[].
Multi-valued attribute values are represented as a List of the appropriate type.
The following example prints the current time on an Active Directory server.
HashMap props = new HashMap(); props.put("bindstr", "ldap://dc100.busicorp.local/RootDSE??base"); LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { LdapEntry entry = (LdapEntry)lsp.getEntry("", new String[] { "currentTime" }); String currentTime = (String)entry.get("currentTime"); Date date = new Date(Long.parseLong(currentTime)); System.out.println("currentTime: " + date); } finally { lsp.dispose(); }
It is necessary to cast the return value of get into the correct type. In this case the currentTime attribute definition defines the type as String (a long would be ideal but the getEntry method returns an Object).
Some incidental additional notes regarding the above example are:
LdapAttrDef
specifies a conversion to transform the standard UTC string directory represntation into milliseconds since 1970 which is more suitable for constructing a Java Date as illustrated in the example.
get
in interface java.util.Map
get
in class java.util.HashMap
name
- the name of the attributeprotected java.lang.Object get(java.lang.Object name, LdapAttrDef def)
public java.lang.Object put(java.lang.Object name, java.lang.Object value)
Put a named attribute value into this entry.
An approptiate LDAP modify operation will be added to the list of operations that should be committed to the directory when update(java.lang.String[])
is called.
The attribute value will be converted if the attribute definition specifies a conversion.
The data type of the attribute value must be appropriate for the named attribute's attribute definition.
Attribute types may be determined by querying the LdapAttrDef
for the named attribute and by simply inspecting values returned by this API during the development process (the data type of an attribute value is the same for both get and put methods).
The most common attribute value types are String and byte[].
Consider the following example which sets an attribute value.
LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { LdapEntry entry = (LdapEntry)lsp.getEntry("DC=openbook,DC=edu", String[] { "o" }); entry.put("o", "OpenBook University"); entry.update(); } finally { lsp.dispose(); }
It is important to retrieve all attributes that are to be updated so that the update method knows to submit an LDAP add or replace operation. The LdapSecurityProvider will need to be bound using credentials with privileges sufficient to set attribute values.
Multi-valued attribute values are represeted as a List of the appropriate type.
To set only one or a few elements of a multi-valued attribute, retrieve and modify the List as desired.
See the update(java.lang.String[])
method description for examples.
put
in interface java.util.Map
put
in class java.util.HashMap
name
- the name of the attribute (such as "sAMAccountName" or "gidNumber")value
- the value to set for the named attributepublic java.lang.Object remove(java.lang.Object name)
update(java.lang.String[])
is called.
As stated in the documentation for the get(java.lang.Object)
and put(java.lang.Object, java.lang.Object)
methods, attribute names are defined by the directory to which the underlying LdapSecurityProvider is bound and data types of values depend greatly on their corresponding attribute definitions.
Consider the following example which removes an attribute value.
LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { LdapEntry entry = (LdapEntry)lsp.getEntry("CN=Chris Davis", String[] { "jpegPhoto" }); entry.remove("jpegPhoto"); entry.update(); } finally { lsp.dispose(); }
remove
in interface java.util.Map
remove
in class java.util.HashMap
name
- the name of the attributepublic void update(java.lang.String[] attrs) throws SecurityProviderException
This method is identical to update()
but accepts a String[] array of attribute names that indicates the attributes that should be updated.
The attrs parameter is a mask that limits the update to a subset of attributes in this entry.
Other attributes in this entry will not be updated regardless of whether or not they have been modified.
To add values to a multi-valued attribute, retrieve the List with get(), add the element and update the group as illustrated by the following code fragment:
LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { LdapEntry group = (LdapEntry)lsp.getEntry("CN=Wiki Users", new String[] { "member" }); Account acct = lsp.getAccount(memberName, new String[0]); List members = (List)group.get("member"); members.add(acct.get("distinguishedName")); group.update(); } finally { lsp.dispose(); }
To remove values from a multi-valued attribute, retrieve the List with get(), remove the element and update the group as illustrated by the following code fragment:
LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { LdapEntry group = (LdapEntry)lsp.getEntry(groupName, new String[] { "member" }); Account acct = lsp.getAccount(memberName, new String[0]); List members = (List)group.get("member"); members.remove(acct.get("distinguishedName")); group.update(); } finally { lsp.dispose(); }
SecurityProviderException
public void update() throws SecurityProviderException
update(LdapEntry.ALL_ATTRS)
.
See the update(String[])
method description for details.SecurityProviderException
- if a catostrophic error occurspublic void create(java.lang.String[] attrs) throws SecurityProviderException
This method is identical to create()
but accepts a String[] array of attribute names that indicates the attributes that should be created.
The attrs parameter is a mask that limits the create operation to a subset of attributes in this entry.
Other attributes in this entry will not be created.
Consider the following code fragment which illustrates how to create a Security group with Global scope in Active Directory.
int SCOPE_GLOBAL = 0x00000002; int SCOPE_DOMAIN_LOCAL = 0x00000004; int SCOPE_UNIVERSAL = 0x00000008; int GROUP_SECURITY = 0x80000000; LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { String sAMAccountName = "Wiki Users"; LdapEntry group = new LdapEntry(lsp, "CN=" + sAMAccountName); group.put("objectClass", "group"); group.put("sAMAccountName", sAMAccountName); group.put("groupType", Integer.toString(GROUP_SECURITY | SCOPE_GLOBAL)); List member = (List)group.get("member"); group.add("CN=Alice Baker," + lsp.getProperty("ldap.base")); group.create(); } finally { lsp.dispose(); }
Consider the following code fragment which illustrates how to create a new User account in Active Directory.
int ACCOUNTDISABLE = 0x00000002; int PASSWD_NOTREQD = 0x00000020; int NORMAL_ACCOUNT = 0x00000200; int PASSWORD_EXPIRED = 0x00800000; LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { String sAMAccountName = "scott"; String userPrincipalName = acctname + "@" + lsp.getProperty("domain.dns.name"); String password = "tiger"; String mail = "Bruce.Scott@busicorp.com"; LdapAccount acct = new LdapAccount(lsp, "CN=" + acctname); acct.put("objectClass", "user"); acct.put("sAMAccountName", sAMAccountName); acct.put("userPrincipalName", userPrincipalName); acct.put("mail", mail); acct.put("userAccountControl", Integer.toString(ACCOUNTDISABLE | PASSWD_NOTREQD | NORMAL_ACCOUNT | PASSWORD_EXPIRED)); acct.create(); acct.setPassword(password.toCharArray()); } finally { lsp.dispose(); }
To create or update multi-valued attributes, the List interface must be used. Consider the following example which creates a group for an RFC2307-based LDAP implementation (such as a default OpenLDAP installation) with multiple values for both the objectClass and memberUid attributes:
LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { LdapEntry group = new LdapEntry(lsp, "CN=Wiki Users"); List objectClass = (List)group.get("objectClass"); objectClass.add("top"); objectClass.add("posixGroup"); List memberUid = (List)group.get("memberUid"); memberUid.add("user1"); memberUid.add("user2"); memberUid.add("user3"); group.put("gidNumber", "12345"); group.create(); } finally { lsp.dispose(); } -- Example output of LdapSearch on entry created -- C:\>java -cp jespa-1.1.0.jar;jcifs-1.3.12.jar jespa.ldap.LdapSearch -f openbook.prp \ "ldap://ldap101.openbook.edu/OU=Students,DC=openbook,DC=edu?*?sub?(cn=wiki users)" cn=Wiki Users,ou=Students,dc=openbook,dc=edu: gidNumber: 12345 memberUid: [user1,user2,user3] objectClass: [top,posixGroup] cn: Wiki Users
To create other types of entries, first use an LDAP browser (like the Microsoft ADSI Edit MMC snap-in) to inspect the attribute values of an existing entry of the desired type. Then craft your code to create an entry with the same values.
attrs
- a mask of attribute names that limits the update to a subset of attributes in the entrySecurityProviderException
- if a catostrophic error occurspublic void create() throws SecurityProviderException
create(LdapEntry.ALL_ATTRS)
.
See the create(String[])
method description for details.SecurityProviderException
- if a catostrophic error occurspublic void delete() throws SecurityProviderException
Consider the following example which deletes an entry from the directory. Some additional error handling is shown to illustrate that low-level LDAP protocol errors can be isolated by catching LdapException.
LdapSecurityProvider lsp = new LdapSecurityProvider(props); try { LdapEntry entry = (LdapEntry)provider.getEntry(distinguishedName, new String[0]); entry.delete(); } catch (LdapException le) { if (le.getCode() != LdapException.LDAP_NO_SUCH_OBJECT) throw le; System.err.println("The entry does not exist: " + distinguishedName); } finally { lsp.dispose(); }
new LdapEntry(lsp, distinguishedName)
, an LdapException will not be thrown if the entry does not exist because the underlying JNDI implementation does not consider this condition to be an error.
The above example uses getEntry to catch a LdapException.LDAP_NO_SUCH_OBJECT condition.
SecurityProviderException
- if the entry cannot be found or if a catostrophic error occurspublic int size()
size
in interface java.util.Map
size
in class java.util.HashMap
public boolean isEmpty()
isEmpty
in interface java.util.Map
isEmpty
in class java.util.HashMap
public boolean containsKey(java.lang.Object key)
containsKey
in interface java.util.Map
containsKey
in class java.util.HashMap
key
- the named attributepublic boolean containsValue(java.lang.Object value)
containsValue
in interface java.util.Map
containsValue
in class java.util.HashMap
public void putAll(java.util.Map m)
putAll
in interface java.util.Map
putAll
in class java.util.HashMap
public void clear()
clear
in interface java.util.Map
clear
in class java.util.HashMap
public java.util.Set<java.lang.String> keySet()
keySet
in interface java.util.Map
keySet
in class java.util.HashMap
public java.util.Collection<java.lang.Object> values()
values
in interface java.util.Map
values
in class java.util.HashMap
public java.util.Set<java.util.Map.Entry<java.lang.String,java.lang.Object>> entrySet()
LdapSecurityProvider lsp = new LdapSecurityProvider(props); LdapEntry entry = lsp.getEntry(distinguishedName, LdapEntry.ALL_ATTRS); Iterator iter = entry.entrySet().iterator(); while (iter.hasNext()) { Map.Entry e = (Map.Entry)iter.next(); System.out.println(e.getKey() + ": " + e.getValue()); }
entrySet
in interface java.util.Map
entrySet
in class java.util.HashMap
public boolean equals(java.lang.Object o)
equals
in interface java.util.Map
equals
in class java.util.AbstractMap
o
- the LdapEntry with which to compare this entry for equalitypublic int hashCode()
hashCode
in interface java.util.Map
hashCode
in class java.util.AbstractMap