001/****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one   *
003 * or more contributor license agreements.  See the NOTICE file *
004 * distributed with this work for additional information        *
005 * regarding copyright ownership.  The ASF licenses this file   *
006 * to you under the Apache License, Version 2.0 (the            *
007 * "License"); you may not use this file except in compliance   *
008 * with the License.  You may obtain a copy of the License at   *
009 *                                                              *
010 *   http://www.apache.org/licenses/LICENSE-2.0                 *
011 *                                                              *
012 * Unless required by applicable law or agreed to in writing,   *
013 * software distributed under the License is distributed on an  *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015 * KIND, either express or implied.  See the License for the    *
016 * specific language governing permissions and limitations      *
017 * under the License.                                           *
018 ****************************************************************/
019
020package org.apache.james.user.ldap;
021
022import java.io.Serializable;
023
024import javax.naming.Context;
025import javax.naming.NamingException;
026import javax.naming.ldap.LdapContext;
027
028import org.apache.james.user.api.model.User;
029import org.apache.james.user.ldap.api.LdapConstants;
030
031/**
032 * Encapsulates the details of a user as taken from an LDAP compliant directory.
033 * Instances of this class are only applicable to the
034 * {@link ReadOnlyUsersLDAPRepository} or its subclasses. Consequently it does
035 * not permit the mutation of user details. It is intended purely as an
036 * encapsulation of the user information as held in the LDAP directory, and as a
037 * means of authenticating the user against the LDAP server. Consequently
038 * invocations of the contract method {@link User#setPassword(String)} always
039 * returns <code>false</code>.
040 * 
041 * @see SimpleLDAPConnection
042 * @see ReadOnlyUsersLDAPRepository
043 * 
044 */
045public class ReadOnlyLDAPUser implements User, Serializable {
046    // private static final long serialVersionUID = -6712066073820393235L; 
047    private static final long serialVersionUID = -5201235065842464013L;
048
049    /**
050     * The user's identifier or name. This is the value that is returned by the
051     * method {@link User#getUserName()}. It is also from this value that the
052     * user's email address is formed, so for example: if the value of this
053     * field is <code>&quot;john.bold&quot;</code>, and the domain is
054     * <code>&quot;myorg.com&quot;</code>, the user's email address will be
055     * <code>&quot;john.bold&#64;myorg.com&quot;</code>.
056     */
057    private String _userName;
058
059    /**
060     * The distinguished name of the user-record in the LDAP directory.
061     */
062    private String _userDN;
063
064    /**
065     * The context for the LDAP server from which to retrieve the
066     * user's details.
067     */
068    private LdapContext _ldapContext = null;
069
070    /**
071     * Creates a new instance of ReadOnlyLDAPUser.
072     *
073     */
074    private ReadOnlyLDAPUser() {
075        super();
076    }
077
078    /**
079     * Constructs an instance for the given user-details, and which will
080     * authenticate against the given host.
081     * 
082     * @param userName
083     *            The user-identifier/name. This is the value with which the
084     *            field  will be initialised, and which will be
085     *            returned by invoking {@link #getUserName()}.
086     * @param userDN
087     *            The distinguished (unique-key) of the user details as stored
088     *            on the LDAP directory.
089     * @param ldapContext
090     *            The context for the LDAP server on which the user details are held.
091     *            This is also the host against which the user will be
092     *            authenticated, when {@link #verifyPassword(String)} is
093     *            invoked.
094     * @throws NamingException 
095     */
096    public ReadOnlyLDAPUser(String userName, String userDN, LdapContext ldapContext) {
097        this();
098        _userName = userName;
099        _userDN = userDN;
100        _ldapContext = ldapContext;
101    }
102
103    /**
104     * Fulfils the contract {@link User#getUserName()}. It returns the value of
105     * the field . This is generally the value from which the
106     * user email address is built, by appending the domain name to it.
107     * 
108     * @return The user's identifier or name.
109     */
110    public String getUserName() {
111        return _userName;
112    }
113
114    /**
115     * Implementation of contract {@link User#setPassword(String)}, which is
116     * provided for compliance purposes only. Instances of this type mirror LDAP
117     * data and do not perform any updates to the directory. Consequently, this
118     * method always returns <code>false</code> and does not do any work.
119     * 
120     * @return <code>False</code>
121     */
122    public boolean setPassword(String newPass) {
123        return false;
124    }
125
126    /**
127     * Verifies that the password supplied is actually the user's password, by
128     * attempting to rebind to a copy of the LDAP server context using the user's 
129     * username and the supplied password.
130     * 
131     * @param password
132     *            The password to validate.
133     * @return <code>True</code> if a connection can successfully be established
134     *         to the LDAP host using the user's id and the supplied password,
135     *         and <code>False</code> otherwise.
136     */
137    public boolean verifyPassword(String password) {
138        boolean result = false;
139        LdapContext ldapContext = null;
140        try {
141            ldapContext = _ldapContext.newInstance(null);
142            ldapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION,
143                    LdapConstants.SECURITY_AUTHENTICATION_SIMPLE);
144            ldapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, _userDN);
145            ldapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
146            ldapContext.reconnect(null);
147            result = true;
148        } catch (NamingException exception) {
149            // no-op
150        } finally {
151            if (null != ldapContext) {
152                try {
153                    ldapContext.close();
154                } catch (NamingException ex) {
155                    // no-op
156                }
157            }
158        }
159        return result;
160    }
161}