You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by no...@apache.org on 2010/01/23 12:13:30 UTC
svn commit: r902385 - in
/james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository:
ReadOnlyLDAPGroupRestriction.java ReadOnlyLDAPUser.java
ReadOnlyUsersLDAPRepository.java SimpleLDAPConnection.java
Author: norman
Date: Sat Jan 23 11:13:30 2010
New Revision: 902385
URL: http://svn.apache.org/viewvc?rev=902385&view=rev
Log:
Add ReadOnlyUsersLdapRepository, contributed by Obi Ezechukwu. Thx again (JAMES-934)
Added:
james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPGroupRestriction.java
james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPUser.java
james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyUsersLDAPRepository.java
james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/SimpleLDAPConnection.java
Added: james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPGroupRestriction.java
URL: http://svn.apache.org/viewvc/james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPGroupRestriction.java?rev=902385&view=auto
==============================================================================
--- james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPGroupRestriction.java (added)
+++ james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPGroupRestriction.java Sat Jan 23 11:13:30 2010
@@ -0,0 +1,178 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+package org.apache.james.userrepository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+
+/**
+ * <p>
+ * Encapsulates the information required to restrict users to LDAP groups or roles.
+ * Instances of this type are populated from the contents of the <code><users-store></code>
+ * configuration child-element <code><restriction><code>.
+ * </p>
+ *
+ * @see ReadOnlyUsersLDAPRepository
+ * @see ReadOnlyLDAPUser
+ *
+ * @author Obi Ezechukwu
+ */
+
+public class ReadOnlyLDAPGroupRestriction
+{
+ /**
+ * <p>
+ * The name of the LDAP attribute name which holds the unique names
+ * (distinguished-names/DNs) of the members of the group/role.
+ *
+ * </p>
+ */
+ private String memberAttribute;
+
+ /**
+ * <p>
+ * The distinguished-names of the LDAP groups/roles to which James users must
+ * belong. A user who is not a member of at least one of the groups or roles
+ * specified here will not be allowed to authenticate against James. If the
+ * list is empty, group/role restriction will be disabled.
+ * </p>
+ */
+ private List<String> groupDNs;
+
+
+ /**
+ * <p>
+ * Initialises an instance from the contents of
+ * a <code><restriction><code> configuration XML
+ * element.
+ * </p>
+ * @param configuration The avalon configuration instance that
+ * encapsulates the contents of the <code><restriction><code>
+ * XML element.
+ *
+ * @throws ConfigurationException If an error occurs extracting
+ * values from the configuration element.
+ */
+ @SuppressWarnings("unchecked")
+ public ReadOnlyLDAPGroupRestriction(HierarchicalConfiguration configuration) {
+ groupDNs = new ArrayList<String>();
+
+ if (configuration != null) {
+ memberAttribute = configuration.getString("[@memberAttribute]");
+
+ if (configuration.getKeys("group").hasNext()) {
+ List<String> groupNames = configuration
+ .getList("group");
+
+ for (int i = 0; i < groupNames.size(); i++) {
+ groupDNs.add(groupNames.get(i));
+ }
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Indicates if group/role-based restriction is enabled for the
+ * the user-store, based on the information encapsulated in the instance.
+ * </p>
+ * @return <code>True</code> If there list of group/role distinguished
+ * names is not empty, and <code>false</code> otherwise.
+ */
+ protected boolean isActivated() {
+ return !groupDNs.isEmpty();
+ }
+
+ /**
+ * <p>
+ * Converts an instance of this type to a string.
+ * </p>
+ * @return A string representation of the instance.
+ */
+ public String toString() {
+ return "Activated=" + isActivated() + "; Groups=" + groupDNs;
+ }
+
+ /**
+ * <p>
+ * Returns the distinguished-names (DNs) of all the members of the
+ * groups specified in the restriction list. The information is
+ * organised as a list of <code>"<groupDN>=<
+ * [userDN1,userDN2,...,userDNn]>"</code>. Put differently,
+ * each <code>groupDN</code> is associated to a list of <code>userDNs</code>.
+ * </p>
+ *
+ * @param connection The connection to the LDAP directory server.
+ * @return Returns a map of groupDNs to userDN lists.
+ * @throws NamingException Propagated from underlying
+ * LDAP communication layer.
+ */
+ protected Map<String,Collection<String>> getGroupMembershipLists(SimpleLDAPConnection connection)
+ throws NamingException {
+ Map<String,Collection<String>> result = new HashMap<String,Collection<String>>();
+
+ Iterator<String> groupDNsIterator = groupDNs.iterator();
+
+ Attributes groupAttributes;
+ while (groupDNsIterator.hasNext()) {
+ String groupDN = (String) groupDNsIterator.next();
+ groupAttributes = connection.getLdapContext()
+ .getAttributes(groupDN);
+ result.put(groupDN, extractMembers(groupAttributes));
+ }
+
+ return result;
+ }
+
+ /**
+ * <p>
+ * Extracts the DNs for members of the group with the given
+ * LDAP context attributes. This is achieved by extracting all the values
+ * of the LDAP attribute, with name equivalent to the field value
+ * {@link #memberAttribute}, from the attributes collection.
+ * </p>
+ *
+ * @param groupAttributes The attributes taken from the group's LDAP context.
+ * @return A collection of distinguished-names for the users belonging to
+ * the group with the specified attributes.
+ * @throws NamingException Propagated from underlying LDAP communication layer.
+ */
+ private Collection<String> extractMembers(Attributes groupAttributes)
+ throws NamingException {
+ Collection<String> result = new ArrayList<String>();
+ Attribute members = groupAttributes.get(memberAttribute);
+ NamingEnumeration<?> memberDNs = members.getAll();
+
+ while (memberDNs.hasMore())
+ result.add(memberDNs.next().toString());
+
+ return result;
+ }
+}
Added: james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPUser.java
URL: http://svn.apache.org/viewvc/james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPUser.java?rev=902385&view=auto
==============================================================================
--- james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPUser.java (added)
+++ james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyLDAPUser.java Sat Jan 23 11:13:30 2010
@@ -0,0 +1,153 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.userrepository;
+
+import java.io.Serializable;
+
+import javax.naming.NamingException;
+
+import org.apache.james.api.user.User;
+
+/**
+ * <p>
+ * Encapsulates the details of a user as taken from
+ * an LDAP compliant directory. Instances of this class
+ * are only applicable to the {@link ReadOnlyUsersLDAPRepository}
+ * or its subclasses. Consequently it does not permit the mutation
+ * of user details. It is intended purely as an encapsulation
+ * of the user information as held in the LDAP directory, and as a means
+ * of authenticating the user against the LDAP server. Consequently
+ * invocations of the contract method {@link User#setPassword(String)}
+ * always returns <code>false</code>.
+ * </p>
+ *
+ * @see SimpleLDAPConnection
+ * @see ReadOnlyUsersLDAPRepository
+ *
+ */
+public class ReadOnlyLDAPUser implements User, Serializable {
+ private static final long serialVersionUID = -6712066073820393235L;
+
+ /**
+ * <p>
+ * The user's identifier or name. This is the value
+ * that is returned by the method {@link User#getUserName()}.
+ * It is also from this value that the user's email
+ * address is formed, so for example: if the value of
+ * this field is <code>"john.bold"</code>, and the
+ * domain is <code>"myorg.com"</code>, the user's email
+ * address will be <code>"john.bold@myorg.com"</code>.
+ * </p>
+ */
+ private String userName;
+
+
+ /**
+ * <p>
+ * The distinguished name of the user-record in the
+ * LDAP directory.
+ * </p>
+ */
+ private String userDN;
+
+ /**
+ * <p>
+ * The URL for connecting to the LDAP server from which to
+ * retrieve the user's details.
+ * </p>
+ */
+ private String ldapURL;
+
+ /**
+ * <p>
+ * Constructs an instance for the given user-details,
+ * and which will authenticate against the given host.
+ * </p>
+ *
+ * @param userName The user-identifier/name. This is the
+ * value with which the field {@link #userName} will be initialised,
+ * and which will be returned by invoking {@link #getUserName()}.
+ * @param userDN The distinguished (unique-key) of the user details
+ * as stored on the LDAP directory.
+ * @param ldapURL The URL of the LDAP server on which the user
+ * details are held. This is also the host against which the
+ * user will be authenticated, when {@link #verifyPassword(String)}
+ * is invoked.
+ */
+ public ReadOnlyLDAPUser(String userName, String userDN, String ldapURL) {
+ this.userName = userName;
+ this.userDN = userDN;
+ this.ldapURL = ldapURL;
+ }
+
+ /**
+ * <p>
+ * Fulfils the contract {@link User#getUserName()}. It returns the value
+ * of the field {@link #userName}. This is generally the value from
+ * which the user email address is built, by appending the domain name
+ * to it.
+ * </p>
+ *
+ * @return The user's identifier or name.
+ */
+ public String getUserName() {
+ return userName;
+ }
+
+ /**
+ * <p>
+ * Implementation of contract {@link User#setPassword(String)}, which is
+ * provided for compliance purposes only. Instances of this type
+ * mirror LDAP data and do not perform any updates to the directory.
+ * Consequently, this method always returns <code>false</code> and does not do
+ * any work.
+ * </p>
+ * @return <code>False</code>
+ */
+ public boolean setPassword(String newPass) {
+ return false;
+ }
+
+
+ /**
+ * <p>
+ * Verifies that the password supplied is actually the user's password,
+ * by attempting to bind to the LDAP server using the user's username
+ * and the supplied password.
+ * </p>
+ * @param password The password to validate.
+ * @return <code>True</code> if a connection can successfully be established
+ * to the LDAP host using the user's id and the supplied password, and <code>False</code>
+ * otherwise. <b>Please note</b> that if the LDAP server has suffered a crash or failure
+ * in between the initialisation of the user repository and the invocation of this method,
+ * the result will still be <code>false</code>.
+ */
+ public boolean verifyPassword(String password) {
+ boolean result;
+ try {
+ SimpleLDAPConnection.openLDAPConnection(userDN, password, ldapURL);
+ result = true;
+ } catch (NamingException exception) {
+ result = false;
+ }
+ return result;
+ }
+
+}
Added: james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyUsersLDAPRepository.java
URL: http://svn.apache.org/viewvc/james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyUsersLDAPRepository.java?rev=902385&view=auto
==============================================================================
--- james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyUsersLDAPRepository.java (added)
+++ james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/ReadOnlyUsersLDAPRepository.java Sat Jan 23 11:13:30 2010
@@ -0,0 +1,591 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.userrepository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.PostConstruct;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.logging.Log;
+import org.apache.james.api.user.User;
+import org.apache.james.api.user.UsersRepository;
+import org.apache.james.lifecycle.Configurable;
+import org.apache.james.lifecycle.LogEnabled;
+
+/**
+ * <p>
+ * This repository implementation serves as a bridge between Apache James and
+ * LDAP. It allows James to authenticate users against an LDAP compliant server
+ * such as Apache DS or Microsoft AD. It also enables role/group based access
+ * restriction based on LDAP groups.
+ * </p>
+ * <p>
+ * It is intended for organisations that already have a user-authentication and
+ * authorisation mechanism in place, and want to leverage this when deploying
+ * James. The assumption inherent here is that such organisations would not want
+ * to manage user details via James, but will do so externally using whatever
+ * mechanism provided by, or built on top off, their LDAP implementation.
+ * </p>
+ * <p>
+ * Based on this assumption, this repository is strictly <b>read-only</b>. As a
+ * consequence, user modification, deletion and creation requests will be
+ * ignored when using this repository.
+ * </p>
+ * <p>
+ * The following fragment of XML provides an example configuration to enable
+ * this repository: </br>
+ *
+ * <pre>
+ * <users-store>
+ * <repository name="LDAPUsers"
+ * class="org.apache.james.userrepository.ReadOnlyUsersLDAPRepository"
+ * ldapHost="ldap://myldapserver:389"
+ * principal="uid=ldapUser,ou=system"
+ * credentials="password"
+ * userBase="ou=People,o=myorg.com,ou=system"
+ * userIdAttribute="uid"/>
+ * </users-store>
+ * </pre>
+ *
+ * </br>
+ *
+ * Its constituent attributes are defined as follows:
+ * <ul>
+ * <li><b>ldapHost:</b> The URL of the LDAP server to connect to.</li>
+ * <li>
+ * <b>principal:</b> The name (DN) of the user with which to initially bind to
+ * the LDAP server.</li>
+ * <li>
+ * <b>credentials:</b> The password with which to initially bind to the LDAP
+ * server.</li>
+ * <li>
+ * <b>userBase:</b>The context within which to search for user entities.</li>
+ * <li>
+ * <b>userIdAttribute:</b>The name of the LDAP attribute which holds user ids.
+ * For example "uid" for Apache DS, or "sAMAccountName" for
+ * Microsoft Active Directory.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * In order to enable group/role based access restrictions, you can use the
+ * "<restriction>" configuration element. An example of this is
+ * shown below: <br>
+ *
+ * <pre>
+ * <restriction
+ * memberAttribute="uniqueMember">
+ * <group>cn=PermanentStaff,ou=Groups,o=myorg.co.uk,ou=system</group>
+ * <group>cn=TemporaryStaff,ou=Groups,o=myorg.co.uk,ou=system</group>
+ * </restriction>
+ *</pre>
+ *
+ * <br>
+ * Its constituent attributes and elements are defined as follows:
+ * <ul>
+ * <li>
+ * <b>memberAttribute:</b> The LDAP attribute whose values indicate the DNs of
+ * the users which belong to the group or role.</li>
+ * <li>
+ * <b>group:</b> A valid group or role DN. A user is only authenticated
+ * (permitted access) if they belong to at least one of the groups listed under
+ * the "<restriction>" sections.</li>
+ * </ul>
+ * </p>
+ *
+ * @see SimpleLDAPConnection
+ * @see ReadOnlyLDAPUser
+ * @see ReadOnlyLDAPGroupRestriction
+ *
+ * @author Obi Ezechukwu
+ */
+public class ReadOnlyUsersLDAPRepository implements UsersRepository, Configurable, LogEnabled {
+
+ /**
+ * <p>
+ * The URL of the LDAP server against which users are to be authenticated.
+ * Note that users are actually authenticated by binding against the LDAP
+ * server using the users "dn" and "credentials".The
+ * value of this field is taken from the value of the configuration
+ * attribute "ldapHost".
+ * </p>
+ */
+ private String ldapHost;
+
+ /**
+ * <p>
+ * The value of this field is taken from the configuration attribute
+ * "userIdAttribute". This is the LDAP attribute type which holds
+ * the userId value. Note that this is not the same as the email address
+ * attribute.
+ * </p>
+ */
+ private String userIdAttribute;
+
+ /**
+ * <p>
+ * This is the LDAP context/sub-context within which to search for user
+ * entities. The value of this field is taken from the configuration
+ * attribute "userBase".
+ * </p>
+ */
+ private String userBase;
+
+ /**
+ * <p>
+ * The user with which to initially bind to the LDAP server. The value of
+ * this field is taken from the configuration attribute
+ * "principal".
+ * </p>
+ */
+ private String principal;
+
+ /**
+ * <p>
+ * The password/credentials with which to initially bind to the LDAP server.
+ * The value of this field is taken from the configuration attribute
+ * "credentials".
+ * </p>
+ */
+ private String credentials;
+
+ /**
+ * <p>
+ * Encapsulates the information required to restrict users to LDAP groups or
+ * roles. This object is populated from the contents of the configuration
+ * element <restriction>.
+ * </p>
+ */
+ private ReadOnlyLDAPGroupRestriction restriction;
+
+ /**
+ * <p>
+ * The connection handle to the LDAP server. This is the connection that is
+ * built from the configuration attributes "ldapHost",
+ * "principal" and "credentials".
+ * </p>
+ */
+ private SimpleLDAPConnection ldapConnection;
+
+ private Log log;
+
+ /**
+ * <p>
+ * Extracts the parameters required by the repository instance from the
+ * James server configuration data. The fields extracted include
+ * {@link #ldapHost}, {@link #userIdAttribute}, {@link #userBase},
+ * {@link #principal}, {@link #credentials} and {@link #restriction}.
+ * </p>
+ *
+ * @param configuration
+ * An encapsulation of the James server configuration data.
+ */
+ public void configure(HierarchicalConfiguration configuration) throws ConfigurationException {
+ ldapHost = configuration.getString("[@ldapHost]");
+ principal = configuration.getString("[@principal]");
+ credentials = configuration.getString("[@credentials]");
+ userBase = configuration.getString("[@userBase]");
+ userIdAttribute = configuration.getString("[@userIdAttribute]");
+
+ restriction = new ReadOnlyLDAPGroupRestriction(configuration.configurationAt("restriction"));
+
+ }
+
+ /**
+ * <p>
+ * Initialises the user-repository instance. It will create a connection to
+ * the LDAP host using the supplied configuration.
+ * </p>
+ *
+ * @throws Exception
+ * If an error occurs authenticating or connecting to the
+ * specified LDAP host.
+ */
+ @PostConstruct
+ public void init() throws Exception {
+ StringBuffer logBuffer;
+ if (log.isDebugEnabled()) {
+ logBuffer = new StringBuffer(128).append(this.getClass().getName()).append(".initialize()");
+ log.debug(logBuffer.toString());
+
+ logBuffer = new StringBuffer(256).append("Openning connection to LDAP host: ").append(ldapHost).append(".");
+ log.debug(logBuffer.toString());
+ }
+
+ ldapConnection = SimpleLDAPConnection.openLDAPConnection(principal, credentials, ldapHost);
+
+ if (log.isDebugEnabled()) {
+ logBuffer = new StringBuffer(256).append("Initialization complete. User baseDN=").append(userBase).append(" ; userIdAttribute=" + userIdAttribute).append("\n\tGroup restriction:" + restriction);
+ log.debug(logBuffer.toString());
+ }
+ }
+
+ /**
+ * <p>
+ * Indicates if the user with the specified DN can be found in the group
+ * membership map-as encapsulated by the specified parameter map.
+ * </p>
+ *
+ * @param userDN
+ * The DN of the user to search for.
+ * @param groupMembershipList
+ * A map containing the entire group membership lists for the
+ * configured groups. This is organised as a map of
+ *
+ * <code>"<groupDN>=<[userDN1,userDN2,...,userDNn]>"</code>
+ * pairs. In essence, each <code>groupDN</code> string is
+ * associated to a list of <code>userDNs</code>.
+ * @return <code>True</code> if the specified userDN is associated with at
+ * least one group in the parameter map, and <code>False</code>
+ * otherwise.
+ */
+ private boolean userInGroupsMembershipList(String userDN, Map<String, Collection<String>> groupMembershipList) {
+ boolean result = false;
+
+ Collection<Collection<String>> memberLists = groupMembershipList.values();
+ Iterator<Collection<String>> memberListsIterator = memberLists.iterator();
+
+ while (memberListsIterator.hasNext() && !result) {
+ Collection<String> groupMembers = memberListsIterator.next();
+ result = groupMembers.contains(userDN);
+ }
+
+ return result;
+ }
+
+ /**
+ * <p>
+ * Gets all the user entities taken from the LDAP server, as taken from the
+ * search-context given by the value of the attribute {@link #userBase}.
+ * </p>
+ *
+ * @return A set containing all the relevant users found in the LDAP
+ * directory.
+ * @throws NamingException
+ * Propagated from the LDAP communication layer.
+ */
+ private Set<String> getAllUsersFromLDAP() throws NamingException {
+ Set<String> result = new HashSet<String>();
+ NamingEnumeration<?> boundNames = ldapConnection.getLdapContext().list(userBase);
+
+ NameClassPair elementInfo;
+ while (boundNames.hasMore()) {
+ elementInfo = (NameClassPair) boundNames.next();
+ result.add(elementInfo.getNameInNamespace());
+ }
+
+ return result;
+ }
+
+ /**
+ * <p>
+ * Extract the user attributes for the given collection of userDNs, and
+ * encapsulates the user list as a collection of {@link ReadOnlyLDAPUser}s.
+ * This method delegates the extraction of a single user's details to the
+ * method {@link #buildUser(String)}.
+ * </p>
+ *
+ * @param userDNs
+ * The distinguished-names (DNs) of the users whose information
+ * is to be extracted from the LDAP repository.
+ * @return A collection of {@link ReadOnlyLDAPUser}s as taken from the LDAP
+ * server.
+ * @throws NamingException
+ * Propagated from the underlying LDAP communication layer.
+ */
+ private Collection<ReadOnlyLDAPUser> buildUserCollection(Collection<String> userDNs) throws NamingException {
+ List<ReadOnlyLDAPUser> results = new ArrayList<ReadOnlyLDAPUser>();
+
+ Iterator<String> userDNIterator = userDNs.iterator();
+
+ while (userDNIterator.hasNext()) {
+ ReadOnlyLDAPUser user = buildUser(userDNIterator.next());
+ results.add(user);
+ }
+
+ return results;
+ }
+
+ /**
+ * <p>
+ * Given a userDN, this method retrieves the user attributes from the LDAP
+ * server, so as to extract the items that are of interest to James.
+ * Specifically it extracts the userId, which is extracted from the LDAP
+ * attribute whose name is given by the value of the field
+ * {@link #userIdAttribute}.
+ * </p>
+ *
+ * @param userDN
+ * The distinguished-name of the user whose details are to be
+ * extracted from the LDAP repository.
+ * @return A {@link ReadOnlyLDAPUser} instance which is initialized with the
+ * userId of this user and ldap connection information with which
+ * the userDN and attributes were obtained.
+ * @throws NamingException
+ * Propagated by the underlying LDAP communication layer.
+ */
+ private ReadOnlyLDAPUser buildUser(String userDN) throws NamingException {
+ ReadOnlyLDAPUser result;
+
+ Attributes userAttributes = ldapConnection.getLdapContext().getAttributes(userDN);
+ Attribute userName = userAttributes.get(userIdAttribute);
+
+ result = new ReadOnlyLDAPUser(userName.get().toString(), userDN, ldapHost);
+
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.james.api.user.UsersRepository#contains(java.lang.String)
+ */
+ public boolean contains(String name) {
+ if (getUserByName(name) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.james.api.user.UsersRepository#containsCaseInsensitive(java
+ * .lang.String)
+ */
+ public boolean containsCaseInsensitive(String name) {
+ if (getUserByNameCaseInsensitive(name) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.james.api.user.UsersRepository#countUsers()
+ */
+ public int countUsers() {
+ try {
+ return getValidUsers().size();
+ } catch (NamingException e) {
+ log.error("Unable to retrieve user count from ldap", e);
+ }
+ return 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.james.api.user.UsersRepository#getRealName(java.lang.String)
+ */
+ public String getRealName(String name) {
+ User u = getUserByNameCaseInsensitive(name);
+ if (u != null) {
+ return u.getUserName();
+ }
+
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.james.api.user.UsersRepository#getUserByName(java.lang.String)
+ */
+ public User getUserByName(String name) {
+ try {
+ Iterator<ReadOnlyLDAPUser> userIt = buildUserCollection(getValidUsers()).iterator();
+ while (userIt.hasNext()) {
+ ReadOnlyLDAPUser u = userIt.next();
+ if (u.getUserName().equals(name)) {
+ return u;
+ }
+ }
+
+ } catch (NamingException e) {
+ log.error("Unable to retrieve user from ldap", e);
+ }
+ return null;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.james.api.user.UsersRepository#getUserByNameCaseInsensitive
+ * (java.lang.String)
+ */
+ public User getUserByNameCaseInsensitive(String name) {
+ try {
+ Iterator<ReadOnlyLDAPUser> userIt = buildUserCollection(getValidUsers()).iterator();
+ while (userIt.hasNext()) {
+ ReadOnlyLDAPUser u = userIt.next();
+ if (u.getUserName().equalsIgnoreCase(name)) {
+ return u;
+ }
+ }
+
+ } catch (NamingException e) {
+ log.error("Unable to retrieve user from ldap", e);
+ }
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.james.api.user.UsersRepository#list()
+ */
+ public Iterator<String> list() {
+ List<String> result = new ArrayList<String>();
+ try {
+
+ Iterator<ReadOnlyLDAPUser> userIt = buildUserCollection(getValidUsers()).iterator();
+
+ while (userIt.hasNext()) {
+ result.add(userIt.next().getUserName());
+ }
+ } catch (NamingException namingException) {
+ throw new RuntimeException("Unable to retrieve users list from LDAP due to unknown naming error.", namingException);
+ }
+
+ return result.iterator();
+ }
+
+ private Collection<String> getValidUsers() throws NamingException {
+ Set<String> userDNs = getAllUsersFromLDAP();
+ Collection<String> validUserDNs;
+
+ if (restriction.isActivated()) {
+ Map<String, Collection<String>> groupMembershipList = restriction.getGroupMembershipLists(ldapConnection);
+ validUserDNs = new ArrayList<String>();
+
+ Iterator<String> userDNIterator = userDNs.iterator();
+ String userDN;
+ while (userDNIterator.hasNext()) {
+ userDN = userDNIterator.next();
+ if (userInGroupsMembershipList(userDN, groupMembershipList))
+ validUserDNs.add(userDN);
+ }
+ } else {
+ validUserDNs = userDNs;
+ }
+ return validUserDNs;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.james.api.user.UsersRepository#removeUser(java.lang.String)
+ */
+ public void removeUser(String name) {
+ log.warn("This user-repository is read-only. Modifications are not permitted.");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.james.api.user.UsersRepository#test(java.lang.String,
+ * java.lang.String)
+ */
+ public boolean test(String name, String password) {
+ User u = getUserByName(name);
+ if (u != null) {
+ return u.verifyPassword(password);
+ }
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.james.api.user.UsersRepository#addUser(org.apache.james.api
+ * .user.User)
+ */
+ public boolean addUser(User user) {
+ log.warn("This user-repository is read-only. Modifications are not permitted.");
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.james.api.user.UsersRepository#addUser(java.lang.String,
+ * java.lang.Object)
+ */
+ public void addUser(String name, Object attributes) {
+ log.warn("This user-repository is read-only. Modifications are not permitted.");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.james.api.user.UsersRepository#addUser(java.lang.String,
+ * java.lang.String)
+ */
+ public boolean addUser(String username, String password) {
+ log.warn("This user-repository is read-only. Modifications are not permitted.");
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.james.api.user.UsersRepository#updateUser(org.apache.james
+ * .api.user.User)
+ */
+ public boolean updateUser(User user) {
+ log.warn("This user-repository is read-only. Modifications are not permitted.");
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.apache.james.lifecycle.LogEnabled#setLog(org.apache.commons.logging
+ * .Log)
+ */
+ public void setLog(Log log) {
+ this.log = log;
+ }
+
+}
Added: james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/SimpleLDAPConnection.java
URL: http://svn.apache.org/viewvc/james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/SimpleLDAPConnection.java?rev=902385&view=auto
==============================================================================
--- james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/SimpleLDAPConnection.java (added)
+++ james/server/trunk/avalon-user-function/src/main/java/org/apache/james/userrepository/SimpleLDAPConnection.java Sat Jan 23 11:13:30 2010
@@ -0,0 +1,141 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.userrepository;
+
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+
+/**
+ * <p>
+ * A connection handle to an LDAP server which is created by means
+ * of a simple principal and password/credentials
+ * authentication against the LDAP server.
+ * </p>
+ *
+ * @author Obi Ezechukwu
+ */
+public class SimpleLDAPConnection {
+
+ /**
+ * <p>
+ * The distinguished-name/DN of the principal to authenticate
+ * against the LDAP server.
+ * </p>
+ */
+ private String principal;
+
+ /**
+ * <p>
+ * The credentials with which to authenticate against the LDAP server.
+ * </p>
+ */
+ private String credentials;
+
+ /**
+ * <p>The URL of the LDAP server.</p>
+ */
+ private String ldapURL;
+
+ /**
+ * <p>
+ * The root directory context that is visible to the
+ * authenticated user.
+ * </p>
+ */
+ private DirContext ldapContext;
+
+ /**
+ * <p>
+ * Creates an instance with the given login details.
+ * </p>
+ * @param principal The distinguished-name (DN) of the user
+ * to authenticate against the server.
+ * @param credentials The credentials with which to authenticate
+ * the user e.g. the user's password.
+ * @param ldapURL The URL of the LDAP server against which to
+ * authenticate the user.
+ */
+ private SimpleLDAPConnection(String principal, String credentials,
+ String ldapURL) {
+ super();
+ this.principal = principal;
+ this.credentials = credentials;
+ this.ldapURL = ldapURL;
+ }
+
+ /**
+ * <p>
+ * Opens a connection to the specified LDAP server using
+ * the specified user details.
+ * </p>
+ * @param principal The distinguished-name (DN) of the user
+ * to authenticate against the server.
+ * @param credentials The credentials with which to authenticate
+ * the user e.g. the user's password.
+ * @param ldapURL The URL of the LDAP server against which to
+ * authenticate the user.
+ * @return A connection to the LDAP host.
+ * @throws NamingException Propagated from the underlying LDAP connection.
+ */
+ public static SimpleLDAPConnection openLDAPConnection(String principal,
+ String credentials, String ldapURL) throws NamingException {
+ SimpleLDAPConnection result = new SimpleLDAPConnection(principal,
+ credentials, ldapURL);
+ result.initializeContext();
+ return result;
+ }
+
+ /**
+ * <p>
+ * Returns the root directory context that is visible to the
+ * authenticated user.
+ * </p>
+ * @return The directory context that is visible to the
+ * authenticated user.
+ */
+ public DirContext getLdapContext() {
+ return ldapContext;
+ }
+
+ /**
+ * <p>
+ * Internal helper method which creates an LDAP/JNDI context using the
+ * specified user credentials.
+ * </p>
+ * @throws NamingException Propagated from underlying
+ * LDAP communication API.
+ */
+ private void initializeContext() throws NamingException {
+ Properties props = new Properties();
+ props.put(Context.INITIAL_CONTEXT_FACTORY,
+ "com.sun.jndi.ldap.LdapCtxFactory");
+ props.put(Context.PROVIDER_URL, ldapURL);
+
+ props.put(Context.SECURITY_AUTHENTICATION, "simple");
+ props.put(Context.SECURITY_PRINCIPAL, principal);
+ props.put(Context.SECURITY_CREDENTIALS, credentials);
+
+ ldapContext = new InitialDirContext(props);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org