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 */
019package org.apache.wiki.auth.user;
020
021import java.security.Principal;
022import java.util.Properties;
023
024import org.apache.wiki.WikiEngine;
025import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
026import org.apache.wiki.auth.NoSuchPrincipalException;
027import org.apache.wiki.auth.WikiSecurityException;
028
029/**
030 * Defines an interface for loading, persisting and storing users.
031 * @since 2.3
032 */
033public interface UserDatabase
034{
035
036    /**
037     * No-op method that in previous versions of JSPWiki was intended to
038     * atomically commit changes to the user database. Now, the {@link #rename(String, String)},
039     * {@link #save(UserProfile)} and {@link #deleteByLoginName(String)} methods
040     * are atomic themselves.
041     * @throws WikiSecurityException
042     * @deprecated there is no need to call this method because the save, rename and
043     * delete methods contain their own commit logic
044     */
045    void commit() throws WikiSecurityException;
046
047    /**
048     * Looks up and deletes the first {@link UserProfile} in the user database
049     * that matches a profile having a given login name. If the user database
050     * does not contain a user with a matching attribute, throws a
051     * {@link NoSuchPrincipalException}. This method is intended to be atomic;
052     * results cannot be partially committed. If the commit fails, it should
053     * roll back its state appropriately. Implementing classes that persist
054     * to the file system may wish to make this method <code>synchronized</code>.
055     * @param loginName the login name of the user profile that shall be deleted
056     */
057    void deleteByLoginName( String loginName ) throws NoSuchPrincipalException, WikiSecurityException;
058
059    /**
060     * <p>
061     * Looks up the Principals representing a user from the user database. These
062     * are defined as a set of Principals manufactured from the login name, full
063     * name, and wiki name. The order of the Principals returned is not
064     * significant. If the user database does not contain a user with the
065     * supplied identifier, throws a {@link NoSuchPrincipalException}.
066     * </p>
067     * <p>
068     * Note that if an implememtation wishes to mark one of the returned
069     * Principals as representing the user's common name, it should instantiate
070     * this Principal using
071     * {@link org.apache.wiki.auth.WikiPrincipal#WikiPrincipal(String, String)}
072     * with the <code>type</code> parameter set to
073     * {@link org.apache.wiki.auth.WikiPrincipal#WIKI_NAME}. The method
074     * {@link org.apache.wiki.WikiSession#getUserPrincipal()} will return this
075     * principal as the "primary" principal. Note that this method can also be
076     * used to mark a WikiPrincipal as a login name or a wiki name.
077     * </p>
078     * @param identifier the name of the user to retrieve; this corresponds to
079     *            value returned by the user profile's
080     *            {@link UserProfile#getLoginName()} method.
081     * @return the array of Principals representing the user's identities
082     */
083    Principal[] getPrincipals( String identifier ) throws NoSuchPrincipalException;
084
085    /**
086     * Returns all WikiNames that are stored in the UserDatabase
087     * as an array of Principal objects. If the database does not
088     * contain any profiles, this method will return a zero-length
089     * array.
090     * @return the WikiNames
091     */
092    Principal[] getWikiNames() throws WikiSecurityException;
093
094    /**
095     * Looks up and returns the first {@link UserProfile} in the user database
096     * that whose login name, full name, or wiki name matches the supplied
097     * string. This method provides a "forgiving" search algorithm for resolving
098     * Principal names when the exact profile attribute that supplied the name
099     * is unknown.
100     * @param index the login name, full name, or wiki name
101     */
102    UserProfile find( String index ) throws NoSuchPrincipalException;
103
104    /**
105     * Looks up and returns the first {@link UserProfile} in the user database
106     * that matches a profile having a given e-mail address. If the user
107     * database does not contain a user with a matching attribute, throws a
108     * {@link NoSuchPrincipalException}.
109     * @param index the e-mail address of the desired user profile
110     * @return the user profile
111     */
112    UserProfile findByEmail( String index ) throws NoSuchPrincipalException;
113
114    /**
115     * Looks up and returns the first {@link UserProfile} in the user database
116     * that matches a profile having a given login name. If the user database
117     * does not contain a user with a matching attribute, throws a
118     * {@link NoSuchPrincipalException}.
119     * @param index the login name of the desired user profile
120     * @return the user profile
121     */
122    UserProfile findByLoginName( String index ) throws NoSuchPrincipalException;
123
124    /**
125     * Looks up and returns the first {@link UserProfile} in the user database
126     * that matches a profile having a given unique ID (uid). If the user database
127     * does not contain a user with a unique ID, it throws a
128     * {@link NoSuchPrincipalException}.
129     * @param uid the unique identifier of the desired user profile
130     * @return the user profile
131     * @since 2.8
132     */
133    UserProfile findByUid( String uid ) throws NoSuchPrincipalException;
134    
135    /**
136     * Looks up and returns the first {@link UserProfile} in the user database
137     * that matches a profile having a given wiki name. If the user database
138     * does not contain a user with a matching attribute, throws a
139     * {@link NoSuchPrincipalException}.
140     * @param index the wiki name of the desired user profile
141     * @return the user profile
142     */
143    UserProfile findByWikiName( String index ) throws NoSuchPrincipalException;
144
145    /**
146     * Looks up and returns the first {@link UserProfile} in the user database
147     * that matches a profile having a given full name. If the user database
148     * does not contain a user with a matching attribute, throws a
149     * {@link NoSuchPrincipalException}.
150     * @param index the fill name of the desired user profile
151     * @return the user profile
152     */
153    UserProfile findByFullName( String index ) throws NoSuchPrincipalException;
154
155    /**
156     * Initializes the user database based on values from a Properties object.
157     */
158    void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException, WikiSecurityException;
159
160    /**
161     * Factory method that instantiates a new user profile.
162     * The {@link UserProfile#isNew()} method of profiles created using
163     * this method should return <code>true</code>.
164     */
165    UserProfile newProfile();
166
167    /**
168     * <p>Renames a {@link UserProfile} in the user database by changing
169     * the profile's login name. Because the login name is the profile's unique
170     * identifier, implementations should verify that the identifier is
171     * "safe" to change before actually changing it. Specifically: the profile
172     * with the supplied login name must already exist, and the proposed new
173     * name must not be in use by another profile.</p>
174     * <p>This method is intended to be atomic; results cannot be partially committed.
175     * If the commit fails, it should roll back its state appropriately.
176     * Implementing classes that persist to the file system may wish to make
177     * this method <code>synchronized</code>.</p>
178     * @param loginName the existing login name for the profile
179     * @param newName the proposed new login name
180     * @throws NoSuchPrincipalException if the user profile identified by
181     * <code>loginName</code> does not exist
182     * @throws DuplicateUserException if another user profile with the
183     * proposed new login name already exists
184     * @throws WikiSecurityException if the profile cannot be renamed for
185     * any reason, such as an I/O error, database connection failure
186     * or lack of support for renames.
187     */
188    void rename( String loginName, String newName ) throws NoSuchPrincipalException, DuplicateUserException, WikiSecurityException;
189
190    /**
191     * <p>
192     * Saves a {@link UserProfile}to the user database, overwriting the
193     * existing profile if it exists. The user name under which the profile
194     * should be saved is returned by the supplied profile's
195     * {@link UserProfile#getLoginName()} method.
196     * </p>
197     * <p>
198     * The database implementation is responsible for detecting potential
199     * duplicate user profiles; specifically, the login name, wiki name, and
200     * full name must be unique. The implementation is not required to check for
201     * validity of passwords or e-mail addresses. Special case: if the profile
202     * already exists and the password is null, it should retain its previous
203     * value, rather than being set to null.
204     * </p>
205     * <p>Implementations are <em>required</em> to time-stamp the creation
206     * or modification fields of the UserProfile./p>
207     * <p>This method is intended to be atomic; results cannot be partially committed.
208     * If the commit fails, it should roll back its state appropriately.
209     * Implementing classes that persist to the file system may wish to make
210     * this method <code>synchronized</code>.</p>
211     * @param profile the user profile to save
212     * @throws WikiSecurityException if the profile cannot be saved
213     */
214    void save( UserProfile profile ) throws WikiSecurityException;
215
216    /**
217     * Determines whether a supplied user password is valid, given a login name
218     * and password. It is up to the implementing class to determine how the
219     * comparison should be made. For example, the password might be hashed
220     * before comparing it to the value persisted in the back-end data store.
221     * @param loginName the login name
222     * @param password the password
223     * @return <code>true</code> if the password is valid, <code>false</code>
224     *         otherwise
225     */
226    boolean validatePassword( String loginName, String password );
227
228}