You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by aj...@apache.org on 2008/02/13 06:54:24 UTC
svn commit: r627255 [17/41] - in
/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src: ./ com/ com/ecyrd/
com/ecyrd/jspwiki/ com/ecyrd/jspwiki/action/ com/ecyrd/jspwiki/attachment/
com/ecyrd/jspwiki/auth/ com/ecyrd/jspwiki/auth/acl/ com/ecyrd/jspwiki...
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/JDBCUserDatabase.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/JDBCUserDatabase.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/JDBCUserDatabase.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/JDBCUserDatabase.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,749 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2007 JSPWiki Development Group
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.auth.user;
+
+import java.security.Principal;
+import java.sql.*;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+
+import com.ecyrd.jspwiki.NoRequiredPropertyException;
+import com.ecyrd.jspwiki.TextUtil;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
+import com.ecyrd.jspwiki.auth.WikiPrincipal;
+import com.ecyrd.jspwiki.auth.WikiSecurityException;
+
+/**
+ * <p>Implementation of UserDatabase that persists {@link DefaultUserProfile}
+ * objects to a JDBC DataSource, as might typically be provided by a web
+ * container. This implementation looks up the JDBC DataSource using JNDI.
+ * The JNDI name of the datasource, backing table and mapped columns used
+ * by this class are configured via settings in <code>jspwiki.properties</code>.</p>
+ * <p>Configurable properties are these:</p>
+ * <table>
+ * <tr>
+ * <thead>
+ * <th>Property</th>
+ * <th>Default</th>
+ * <th>Definition</th>
+ * <thead>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.datasource</code></td>
+ * <td><code>jdbc/UserDatabase</code></td>
+ * <td>The JNDI name of the DataSource</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.table</code></td>
+ * <td><code>users</code></td>
+ * <td>The table that stores the user profiles</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.created</code></td>
+ * <td><code>created</code></td>
+ * <td>The column containing the profile's creation timestamp</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.email</code></td>
+ * <td><code>email</code></td>
+ * <td>The column containing the user's e-mail address</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.fullName</code></td>
+ * <td><code>full_name</code></td>
+ * <td>The column containing the user's full name</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.loginName</code></td>
+ * <td><code>login_name</code></td>
+ * <td>The column containing the user's login id</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.password</code></td>
+ * <td><code>password</code></td>
+ * <td>The column containing the user's password</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.modified</code></td>
+ * <td><code>modified</code></td>
+ * <td>The column containing the profile's last-modified timestamp</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.wikiName</code></td>
+ * <td><code>wiki_name</code></td>
+ * <td>The column containing the user's wiki name</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.roleTable</code></td>
+ * <td><code>roles</code></td>
+ * <td>The table that stores user roles. When a new user is created,
+ * a new record is inserted containing user's initial role. The
+ * table will have an ID column whose name and values correspond
+ * to the contents of the user table's login name column. It will
+ * also contain a role column (see next row).</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.role</code></td>
+ * <td><code>role</code></td>
+ * <td>The column in the role table that stores user roles. When a new user
+ * is created, this column will be populated with the value
+ * <code>Authenticated</code>. Once created, JDBCUserDatabase does not
+ * use this column again; it is provided strictly for the convenience
+ * of container-managed authentication services.</td>
+ * </tr>
+ * <tr>
+ * <td><code>jspwiki.userdatabase.hashPrefix</code></td>
+ * <td><code>true</code></td>
+ * <td>Whether or not to prepend a prefix for the hash algorithm, <em>e.g.</em>,
+ * <code>{SHA}</code>.</td>
+ * </tr>
+ * </table>
+ * <p>This class hashes passwords using SHA-1. All of the underying SQL commands used by this class are implemented using
+ * prepared statements, so it is immune to SQL injection attacks.</p>
+ * <p>This class is typically used in conjunction with a web container's JNDI resource
+ * factory. For example, Tomcat versions 4 and higher provide a basic JNDI factory
+ * for registering DataSources. To give JSPWiki access to the JNDI resource named
+ * by <code></code>, you would declare the datasource resource similar to this:</p>
+ * <blockquote><code><Context ...><br/>
+ * ...<br/>
+ * <Resource name="jdbc/UserDatabase" auth="Container"<br/>
+ * type="javax.sql.DataSource" username="dbusername" password="dbpassword"<br/>
+ * driverClassName="org.hsql.jdbcDriver" url="jdbc:HypersonicSQL:database"<br/>
+ * maxActive="8" maxIdle="4"/><br/>
+ * ...<br/>
+ * </Context></code></blockquote>
+ * <p>JDBC driver JARs should be added to Tomcat's <code>common/lib</code> directory.
+ * For more Tomcat 5.5 JNDI configuration examples,
+ * see <a href="http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html">
+ * http://tomcat.apache.org/tomcat-5.5-doc/jndi-resources-howto.html</a>.</p>
+ * <p>JDBCUserDatabase commits changes as transactions if the back-end database supports them.
+ * If the database supports transactions, user profile changes are saved
+ * to permanent storage only when the {@link #commit()} method is called. If the database does <em>not</em>
+ * support transactions, then changes are made immediately (during the {@link #save(UserProfile)}
+ * method), and the {@linkplain #commit()} method no-ops. Thus, callers should always call the
+ * {@linkplain #commit()} method after saving a profile to guarantee that changes are applied.</p>
+ * @author Andrew R. Jaquith
+ * @since 2.3
+ */public class JDBCUserDatabase extends AbstractUserDatabase
+{
+
+ private static final String NOTHING = "";
+
+ public static final String DEFAULT_DB_CREATED = "created";
+
+ public static final String DEFAULT_DB_EMAIL = "email";
+
+ public static final String DEFAULT_DB_FULL_NAME = "full_name";
+
+ public static final String DEFAULT_DB_HASH_PREFIX = "true";
+
+ public static final String DEFAULT_DB_JNDI_NAME = "jdbc/UserDatabase";
+
+ public static final String DEFAULT_DB_MODIFIED = "modified";
+
+ public static final String DEFAULT_DB_ROLE = "role";
+
+ public static final String DEFAULT_DB_ROLE_TABLE = "roles";
+
+ public static final String DEFAULT_DB_TABLE = "users";
+
+ public static final String DEFAULT_DB_LOGIN_NAME = "login_name";
+
+ public static final String DEFAULT_DB_PASSWORD = "password";
+
+ public static final String DEFAULT_DB_WIKI_NAME = "wiki_name";
+
+ public static final String PROP_DB_CREATED = "jspwiki.userdatabase.created";
+
+ public static final String PROP_DB_EMAIL = "jspwiki.userdatabase.email";
+
+ public static final String PROP_DB_FULL_NAME = "jspwiki.userdatabase.fullName";
+
+ public static final String PROP_DB_DATASOURCE = "jspwiki.userdatabase.datasource";
+
+ public static final String PROP_DB_HASH_PREFIX = "jspwiki.userdatabase.hashPrefix";
+
+ public static final String PROP_DB_LOGIN_NAME = "jspwiki.userdatabase.loginName";
+
+ public static final String PROP_DB_MODIFIED = "jspwiki.userdatabase.modified";
+
+ public static final String PROP_DB_PASSWORD = "jspwiki.userdatabase.password";
+
+ public static final String PROP_DB_ROLE = "jspwiki.userdatabase.role";
+
+ public static final String PROP_DB_ROLE_TABLE = "jspwiki.userdatabase.roleTable";
+
+ public static final String PROP_DB_TABLE = "jspwiki.userdatabase.table";
+
+ public static final String PROP_DB_WIKI_NAME = "jspwiki.userdatabase.wikiName";
+
+ private DataSource m_ds = null;
+ private String m_deleteUserByLoginName = null;
+ private String m_deleteRoleByLoginName = null;
+ private String m_findByEmail = null;
+ private String m_findByFullName = null;
+ private String m_findByLoginName = null;
+ private String m_findByWikiName = null;
+ private String m_renameProfile = null;
+ private String m_renameRoles = null;
+ private String m_updateProfile = null;
+ private String m_findAll = null;
+ private String m_findRoles = null;
+ private String m_initialRole = "Authenticated";
+ private String m_insertProfile = null;
+ private String m_insertRole = null;
+ private String m_userTable = null;
+ private String m_email = null;
+ private String m_fullName = null;
+ private boolean m_hashPrefix = true;
+ private String m_loginName = null;
+ private String m_password = null;
+ private String m_role = null;
+ private String m_roleTable = null;
+ private String m_wikiName = null;
+ private String m_created = null;
+ private String m_modified = null;
+ private boolean m_sharedWithContainer = false;
+ private boolean m_supportsCommits = false;
+
+ /**
+ * Looks up and deletes the first {@link UserProfile} in the user database
+ * that matches a profile having a given login name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}. This method is intended to be atomic;
+ * results cannot be partially committed. If the commit fails, it should
+ * roll back its state appropriately. Implementing classes that persist
+ * to the file system may wish to make this method <code>synchronized</code>.
+ * @param loginName the login name of the user profile that shall be deleted
+ */
+ public void deleteByLoginName( String loginName ) throws NoSuchPrincipalException, WikiSecurityException
+ {
+ // Get the existing user; if not found, throws NoSuchPrincipalException
+ findByLoginName( loginName );
+ Connection conn = null;
+
+ try
+ {
+ // Open the database connection
+ conn = m_ds.getConnection();
+ if ( m_supportsCommits )
+ {
+ conn.setAutoCommit( false );
+ }
+
+ PreparedStatement ps;
+ // Delete user record
+ ps = conn.prepareStatement( m_deleteUserByLoginName );
+ ps.setString(1, loginName );
+ ps.execute();
+ ps.close();
+
+ // Delete role record
+ ps = conn.prepareStatement( m_deleteRoleByLoginName );
+ ps.setString(1, loginName );
+ ps.execute();
+ ps.close();
+
+ // Commit and close connection
+ if ( m_supportsCommits )
+ {
+ conn.commit();
+ }
+ }
+ catch ( SQLException e )
+ {
+ throw new WikiSecurityException( e.getMessage() );
+ }
+ finally
+ {
+ try { conn.close(); } catch (Exception e) {}
+ }
+ }
+
+ /**
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByEmail(java.lang.String)
+ */
+ public UserProfile findByEmail( String index ) throws NoSuchPrincipalException
+ {
+ return findByPreparedStatement( m_findByEmail, index );
+ }
+
+ /**
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByFullName(java.lang.String)
+ */
+ public UserProfile findByFullName( String index ) throws NoSuchPrincipalException
+ {
+ return findByPreparedStatement( m_findByFullName, index );
+ }
+
+ /**
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByLoginName(java.lang.String)
+ */
+ public UserProfile findByLoginName( String index ) throws NoSuchPrincipalException
+ {
+ return findByPreparedStatement( m_findByLoginName, index );
+ }
+
+ /**
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByWikiName(String)
+ */
+ public UserProfile findByWikiName( String index ) throws NoSuchPrincipalException
+ {
+ return findByPreparedStatement( m_findByWikiName, index );
+ }
+
+ /**
+ * Returns all WikiNames that are stored in the UserDatabase
+ * as an array of WikiPrincipal objects. If the database does not
+ * contain any profiles, this method will return a zero-length
+ * array.
+ * @return the WikiNames
+ */
+ public Principal[] getWikiNames() throws WikiSecurityException
+ {
+ Set principals = new HashSet();
+ Connection conn = null;
+ try
+ {
+ conn = m_ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement( m_findAll );
+ ResultSet rs = ps.executeQuery();
+ while ( rs.next() )
+ {
+ String wikiName = rs.getString( m_wikiName );
+ if ( wikiName == null )
+ {
+ log.warn( "Detected null wiki name in XMLUserDataBase. Check your user database." );
+ }
+ else
+ {
+ Principal principal = new WikiPrincipal( wikiName, WikiPrincipal.WIKI_NAME );
+ principals.add( principal );
+ }
+ }
+ ps.close();
+ }
+ catch ( SQLException e )
+ {
+ throw new WikiSecurityException( e.getMessage() );
+ }
+ finally
+ {
+ try { conn.close(); } catch (Exception e) {}
+ }
+
+ return (Principal[])principals.toArray( new Principal[principals.size()] );
+ }
+
+ /**
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#initialize(com.ecyrd.jspwiki.WikiEngine,
+ * java.util.Properties)
+ */
+ public void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException
+ {
+ String jndiName = props.getProperty( PROP_DB_DATASOURCE, DEFAULT_DB_JNDI_NAME );
+ try
+ {
+ Context initCtx = new InitialContext();
+ Context ctx = (Context) initCtx.lookup("java:comp/env");
+ m_ds = (DataSource) ctx.lookup( jndiName );
+
+ // Prepare the SQL selectors
+ m_userTable = props.getProperty( PROP_DB_TABLE, DEFAULT_DB_TABLE );
+ m_email = props.getProperty( PROP_DB_EMAIL, DEFAULT_DB_EMAIL );
+ m_fullName = props.getProperty( PROP_DB_FULL_NAME, DEFAULT_DB_FULL_NAME );
+ m_hashPrefix = Boolean.valueOf( props.getProperty( PROP_DB_HASH_PREFIX, DEFAULT_DB_HASH_PREFIX ) ).booleanValue();
+ m_loginName = props.getProperty( PROP_DB_LOGIN_NAME, DEFAULT_DB_LOGIN_NAME );
+ m_password = props.getProperty( PROP_DB_PASSWORD, DEFAULT_DB_PASSWORD );
+ m_wikiName = props.getProperty( PROP_DB_WIKI_NAME, DEFAULT_DB_WIKI_NAME );
+ m_created = props.getProperty( PROP_DB_CREATED, DEFAULT_DB_CREATED );
+ m_modified = props.getProperty( PROP_DB_MODIFIED, DEFAULT_DB_MODIFIED );
+
+ m_findAll = "SELECT * FROM " + m_userTable;
+ m_findByEmail = "SELECT * FROM " + m_userTable + " WHERE " + m_email + "=?";
+ m_findByFullName = "SELECT * FROM " + m_userTable + " WHERE " + m_fullName + "=?";
+ m_findByLoginName = "SELECT * FROM " + m_userTable + " WHERE " + m_loginName + "=?";
+ m_findByWikiName = "SELECT * FROM " + m_userTable + " WHERE " + m_wikiName + "=?";
+
+ // Prepare the user isert/update SQL
+ m_insertProfile = "INSERT INTO " + m_userTable + " ("
+ + m_email + ","
+ + m_fullName + ","
+ + m_password + ","
+ + m_wikiName + ","
+ + m_modified + ","
+ + m_loginName + ","
+ + m_created
+ + ") VALUES (?,?,?,?,?,?,?)";
+ m_updateProfile = "UPDATE " + m_userTable + " SET "
+ + m_email + "=?,"
+ + m_fullName + "=?,"
+ + m_password + "=?,"
+ + m_wikiName + "=?,"
+ + m_modified + "=? WHERE " + m_loginName + "=?";
+
+ // Prepare the role insert SQL
+ m_roleTable = props.getProperty( PROP_DB_ROLE_TABLE, DEFAULT_DB_ROLE_TABLE );
+ m_role = props.getProperty( PROP_DB_ROLE, DEFAULT_DB_ROLE );
+ m_insertRole = "INSERT INTO " + m_roleTable + " ("
+ + m_loginName + ","
+ + m_role
+ + ") VALUES (?,?)";
+ m_findRoles = "SELECT * FROM " + m_roleTable + " WHERE " + m_loginName + "=?";
+
+ // Prepare the user delete SQL
+ m_deleteUserByLoginName = "DELETE FROM " + m_userTable + " WHERE " + m_loginName + "=?";
+
+ // Prepare the role delete SQL
+ m_deleteRoleByLoginName = "DELETE FROM " + m_roleTable + " WHERE " + m_loginName + "=?";
+
+ // Prepare the rename user/roles SQL
+ m_renameProfile = "UPDATE " + m_userTable + " SET "
+ + m_loginName + "=?,"
+ + m_modified + "=? WHERE " + m_loginName + "=?";
+ m_renameRoles = "UPDATE " + m_roleTable + " SET "
+ + m_loginName + "=? WHERE " + m_loginName + "=?";
+
+ // Set the "share users with container flag"
+ m_sharedWithContainer = TextUtil.isPositive( props.getProperty( PROP_SHARED_WITH_CONTAINER, "false" ) );
+ }
+ catch( NamingException e )
+ {
+ log.error( "JDBCUserDatabase initialization error: " + e.getMessage() );
+ throw new NoRequiredPropertyException( PROP_DB_DATASOURCE, "JDBCUserDatabase initialization error: " + e.getMessage() );
+ }
+
+ // Test connection by doing a quickie select
+ Connection conn = null;
+ try
+ {
+ conn = m_ds.getConnection();
+ PreparedStatement ps = conn.prepareStatement( m_findAll );
+ ps.executeQuery();
+ ps.close();
+ }
+ catch ( SQLException e )
+ {
+ log.error( "JDBCUserDatabase initialization error: " + e.getMessage() );
+ throw new NoRequiredPropertyException( PROP_DB_DATASOURCE, "JDBCUserDatabase initialization error: " + e.getMessage() );
+ }
+ finally
+ {
+ try { conn.close(); } catch (Exception e) {}
+ }
+ log.info( "JDBCUserDatabase initialized from JNDI DataSource: " + jndiName );
+
+ // Determine if the datasource supports commits
+ try
+ {
+ conn = m_ds.getConnection();
+ DatabaseMetaData dmd = conn.getMetaData();
+ if ( dmd.supportsTransactions() )
+ {
+ m_supportsCommits = true;
+ conn.setAutoCommit( false );
+ log.info("JDBCUserDatabase supports transactions. Good; we will use them." );
+ }
+ }
+ catch ( SQLException e )
+ {
+ log.warn("JDBCUserDatabase warning: user database doesn't seem to support transactions. Reason: " + e.getMessage() );
+ throw new NoRequiredPropertyException( PROP_DB_DATASOURCE, "JDBCUserDatabase initialization error: " + e.getMessage() );
+ }
+ finally
+ {
+ try { conn.close(); } catch (Exception e) {}
+ }
+ }
+
+ /**
+ * Determines whether the user database shares user/password data with the
+ * web container; returns <code>true</code> if the JSPWiki property
+ * <code>jspwiki.userdatabase.isSharedWithContainer</code> is <code>true</code>.
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#isSharedWithContainer()
+ */
+ public boolean isSharedWithContainer()
+ {
+ return m_sharedWithContainer;
+ }
+
+ /**
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#rename(String, String)
+ */
+ public void rename(String loginName, String newName)
+ throws NoSuchPrincipalException, DuplicateUserException, WikiSecurityException
+ {
+ // Get the existing user; if not found, throws NoSuchPrincipalException
+ UserProfile profile = findByLoginName( loginName );
+
+ // Get user with the proposed name; if found, it's a collision
+ try
+ {
+ UserProfile otherProfile = findByLoginName( newName );
+ if ( otherProfile != null )
+ {
+ throw new DuplicateUserException( "Cannot rename: the login name '" + newName + "' is already taken." );
+ }
+ }
+ catch ( NoSuchPrincipalException e )
+ {
+ // Good! That means it's safe to save using the new name
+ }
+
+ Connection conn = null;
+ try
+ {
+ // Open the database connection
+ conn = m_ds.getConnection();
+ if ( m_supportsCommits )
+ {
+ conn.setAutoCommit( false );
+ }
+
+ Timestamp ts = new Timestamp( System.currentTimeMillis() );
+ Date modDate = new Date( ts.getTime() );
+
+ // Change the login ID for the user record
+ PreparedStatement ps = conn.prepareStatement( m_renameProfile );
+ ps.setString( 1, newName );
+ ps.setTimestamp( 2, ts );
+ ps.setString( 3, loginName );
+ ps.execute();
+ ps.close();
+
+ // Change the login ID for the role records
+ ps = conn.prepareStatement( m_renameRoles );
+ ps.setString( 1, newName );
+ ps.setString( 2, loginName );
+ ps.execute();
+ ps.close();
+
+ // Set the profile name and mod time
+ profile.setLoginName( newName );
+ profile.setLastModified( modDate );
+
+ // Commit and close connection
+ if ( m_supportsCommits )
+ {
+ conn.commit();
+ }
+ }
+ catch ( SQLException e )
+ {
+ throw new WikiSecurityException( e.getMessage() );
+ }
+ finally
+ {
+ try { conn.close(); } catch (Exception e) {}
+ }
+ }
+
+ /**
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#save(com.ecyrd.jspwiki.auth.user.UserProfile)
+ */
+ public void save( UserProfile profile ) throws WikiSecurityException
+ {
+ // Figure out which prepared statement to use & execute it
+ String loginName = profile.getLoginName();
+ PreparedStatement ps = null;
+ UserProfile existingProfile = null;
+ try
+ {
+ existingProfile = findByLoginName( loginName );
+ }
+ catch ( NoSuchPrincipalException e )
+ {
+ // Existing profile will be null
+ }
+
+ // Get a clean password from the passed profile.
+ // Blank password is the same as null, which means we re-use the existing one.
+ String password = profile.getPassword();
+ String existingPassword = ( existingProfile == null ) ? null : existingProfile.getPassword();
+ if ( NOTHING.equals( password ) )
+ {
+ password = null;
+ }
+ if ( password == null)
+ {
+ password = existingPassword;
+ }
+
+ // If password changed, hash it before we save
+ if ( !password.equals( existingPassword ) )
+ {
+ password = m_hashPrefix ? SHA_PREFIX + getHash( password ) : getHash( password );
+ }
+
+ Connection conn = null;
+ try
+ {
+ // Open the database connection
+ conn = m_ds.getConnection();
+ if ( m_supportsCommits )
+ {
+ conn.setAutoCommit( false );
+ }
+
+ Timestamp ts = new Timestamp( System.currentTimeMillis() );
+ Date modDate = new Date( ts.getTime() );
+ if ( existingProfile == null )
+ {
+ // User is new: insert new user record
+ ps = conn.prepareStatement( m_insertProfile );
+ ps.setString(1, profile.getEmail() );
+ ps.setString(2, profile.getFullname() );
+ ps.setString(3, password );
+ ps.setString(4, profile.getWikiName() );
+ ps.setTimestamp(5, ts );
+ ps.setString(6, profile.getLoginName() );
+ ps.setTimestamp(7, ts );
+ ps.execute();
+ ps.close();
+
+ // Insert role record if no roles yet
+ if ( m_sharedWithContainer )
+ {
+ ps = conn.prepareStatement( m_findRoles );
+ ps.setString( 1, profile.getLoginName() );
+ ResultSet rs = ps.executeQuery();
+ int roles = 0;
+ while ( rs.next() )
+ {
+ roles++;
+ }
+ ps.close();
+ if ( roles == 0 )
+ {
+ ps = conn.prepareStatement( m_insertRole );
+ ps.setString( 1, profile.getLoginName() );
+ ps.setString( 2, m_initialRole );
+ ps.execute();
+ ps.close();
+ }
+ }
+
+ // Set the profile creation time
+ profile.setCreated( modDate );
+ }
+ else
+ {
+ // User exists: modify existing record
+ ps = conn.prepareStatement( m_updateProfile );
+ ps.setString(1, profile.getEmail() );
+ ps.setString(2, profile.getFullname() );
+ ps.setString(3, password );
+ ps.setString(4, profile.getWikiName() );
+ ps.setTimestamp(5, ts );
+ ps.setString(6, profile.getLoginName() );
+ ps.execute();
+ ps.close();
+ }
+ // Set the profile mod time
+ profile.setLastModified( modDate );
+
+ // Commit and close connection
+ if ( m_supportsCommits )
+ {
+ conn.commit();
+ }
+ }
+ catch ( SQLException e )
+ {
+ throw new WikiSecurityException( e.getMessage() );
+ }
+ finally
+ {
+ try { conn.close(); } catch (Exception e) {}
+ }
+ }
+
+ /**
+ *
+ * @param rs
+ * @return
+ * @throws SQLException
+ */
+ private UserProfile findByPreparedStatement( String sql, String index ) throws NoSuchPrincipalException
+ {
+ UserProfile profile = null;
+ boolean found = false;
+ boolean unique = true;
+ Connection conn = null;
+ try
+ {
+ // Open the database connection
+ conn = m_ds.getConnection();
+ if ( m_supportsCommits )
+ {
+ conn.setAutoCommit( false );
+ }
+
+ PreparedStatement ps = conn.prepareStatement( sql );
+ ps.setString( 1, index );
+ ResultSet rs = ps.executeQuery();
+ while ( rs.next() )
+ {
+ if ( profile != null )
+ {
+ unique = false;
+ break;
+ }
+ profile = new DefaultUserProfile();
+ profile.setCreated( rs.getTimestamp( m_created ) );
+ profile.setEmail( rs.getString( m_email ) );
+ profile.setFullname( rs.getString( m_fullName) );
+ profile.setLastModified( rs.getTimestamp( m_modified ) );
+ profile.setLoginName( rs.getString( m_loginName ) ) ;
+ profile.setPassword( rs.getString( m_password ) );
+ found = true;
+ }
+ ps.close();
+ }
+ catch ( SQLException e )
+ {
+ throw new NoSuchPrincipalException( e.getMessage() );
+ }
+ finally
+ {
+ try { conn.close(); } catch (Exception e) {}
+ }
+
+ if ( !found )
+ {
+ throw new NoSuchPrincipalException("Could not find profile in database!");
+ }
+ if ( !unique )
+ {
+ throw new NoSuchPrincipalException("More than one profile in database!");
+ }
+ return profile;
+
+ }
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserDatabase.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserDatabase.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserDatabase.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserDatabase.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,227 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2005 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.auth.user;
+
+import java.security.Principal;
+import java.util.Properties;
+
+import com.ecyrd.jspwiki.NoRequiredPropertyException;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
+import com.ecyrd.jspwiki.auth.WikiSecurityException;
+
+/**
+ * Defines an interface for loading, persisting and storing users.
+ * @author Janne Jalkanen
+ * @author Andrew Jaquith
+ * @since 2.3
+ */
+public interface UserDatabase
+{
+
+ /**
+ * No-op method that in previous versions of JSPWiki was intended to
+ * atomically commit changes to the user database. Now, the {@link #rename(String, String)},
+ * {@link #save(UserProfile)} and {@link #deleteByLoginName(String)} methods
+ * are atomic themselves.
+ * @throws WikiSecurityException
+ * @deprecated there is no need to call this method because the save, rename and
+ * delete methods contain their own commit logic
+ */
+ public void commit() throws WikiSecurityException;
+
+ /**
+ * Looks up and deletes the first {@link UserProfile} in the user database
+ * that matches a profile having a given login name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}. This method is intended to be atomic;
+ * results cannot be partially committed. If the commit fails, it should
+ * roll back its state appropriately. Implementing classes that persist
+ * to the file system may wish to make this method <code>synchronized</code>.
+ * @param loginName the login name of the user profile that shall be deleted
+ */
+ public void deleteByLoginName( String loginName ) throws NoSuchPrincipalException, WikiSecurityException;
+
+ /**
+ * <p>
+ * Looks up the Principals representing a user from the user database. These
+ * are defined as a set of Principals manufactured from the login name, full
+ * name, and wiki name. The order of the Principals returned is not
+ * significant. If the user database does not contain a user with the
+ * supplied identifier, throws a {@link NoSuchPrincipalException}.
+ * </p>
+ * <p>
+ * Note that if an implememtation wishes to mark one of the returned
+ * Principals as representing the user's common name, it should instantiate
+ * this Principal using
+ * {@link com.ecyrd.jspwiki.auth.WikiPrincipal#WikiPrincipal(String, String)}
+ * with the <code>type</code> parameter set to
+ * {@link com.ecyrd.jspwiki.auth.WikiPrincipal#WIKI_NAME}. The method
+ * {@link com.ecyrd.jspwiki.WikiSession#getUserPrincipal()} will return this
+ * principal as the "primary" principal. Note that this method can also be
+ * used to mark a WikiPrincipal as a login name or a wiki name.
+ * </p>
+ * @param identifier the name of the user to retrieve; this corresponds to
+ * value returned by the user profile's
+ * {@link UserProfile#getLoginName()} method.
+ * @return the array of Principals representing the user's identities
+ */
+ public Principal[] getPrincipals( String identifier ) throws NoSuchPrincipalException;
+
+ /**
+ * Returns all WikiNames that are stored in the UserDatabase
+ * as an array of Principal objects. If the database does not
+ * contain any profiles, this method will return a zero-length
+ * array.
+ * @return the WikiNames
+ */
+ public Principal[] getWikiNames() throws WikiSecurityException;
+
+ /**
+ * Looks up and returns the first {@link UserProfile} in the user database
+ * that whose login name, full name, or wiki name matches the supplied
+ * string. This method provides a "forgiving" search algorithm for resolving
+ * Principal names when the exact profile attribute that supplied the name
+ * is unknown.
+ * @param index the login name, full name, or wiki name
+ */
+ public UserProfile find( String index ) throws NoSuchPrincipalException;
+
+ /**
+ * Looks up and returns the first {@link UserProfile} in the user database
+ * that matches a profile having a given e-mail address. If the user
+ * database does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param index the e-mail address of the desired user profile
+ * @return the user profile
+ */
+ public UserProfile findByEmail( String index ) throws NoSuchPrincipalException;
+
+ /**
+ * Looks up and returns the first {@link UserProfile} in the user database
+ * that matches a profile having a given login name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param index the login name of the desired user profile
+ * @return the user profile
+ */
+ public UserProfile findByLoginName( String index ) throws NoSuchPrincipalException;
+
+ /**
+ * Looks up and returns the first {@link UserProfile} in the user database
+ * that matches a profile having a given wiki name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param index the wiki name of the desired user profile
+ * @return the user profile
+ */
+ public UserProfile findByWikiName( String index ) throws NoSuchPrincipalException;
+
+ /**
+ * Looks up and returns the first {@link UserProfile} in the user database
+ * that matches a profile having a given full name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param index the fill name of the desired user profile
+ * @return the user profile
+ */
+ public UserProfile findByFullName( String index ) throws NoSuchPrincipalException;
+
+ /**
+ * Initializes the user database based on values from a Properties object.
+ */
+ public void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException;
+
+ /**
+ * Returns <code>true</code> if this user database shares user/password data with the
+ * web container; <code>false</false> otherwise.
+ * @return the result
+ */
+ public boolean isSharedWithContainer();
+
+ /**
+ * Factory method that instantiates a new user profile.
+ * The {@link UserProfile#isNew()} method of profiles created using
+ * this method should return <code>true</code>.
+ */
+ public UserProfile newProfile();
+
+ /**
+ * <p>Renames a {@link UserProfile} in the user database by changing
+ * the profile's login name. Because the login name is the profile's unique
+ * identifier, implementations should verify that the identifier is
+ * "safe" to change before actually changing it. Specifically: the profile
+ * with the supplied login name must already exist, and the proposed new
+ * name must not be in use by another profile.</p>
+ * <p>This method is intended to be atomic; results cannot be partially committed.
+ * If the commit fails, it should roll back its state appropriately.
+ * Implementing classes that persist to the file system may wish to make
+ * this method <code>synchronized</code>.</p>
+ * @param loginName the existing login name for the profile
+ * @param newName the proposed new login name
+ * @throws NoSuchPrincipalException if the user profile identified by
+ * <code>loginName</code> does not exist
+ * @throws DuplicateUserException if another user profile with the
+ * proposed new login name already exists
+ * @throws WikiSecurityException if the profile cannot be renamed for
+ * any reason, such as an I/O error, database connection failure
+ * or lack of support for renames.
+ */
+ public void rename( String loginName, String newName ) throws NoSuchPrincipalException, DuplicateUserException, WikiSecurityException;
+
+ /**
+ * <p>
+ * Saves a {@link UserProfile}to the user database, overwriting the
+ * existing profile if it exists. The user name under which the profile
+ * should be saved is returned by the supplied profile's
+ * {@link UserProfile#getLoginName()} method.
+ * </p>
+ * <p>
+ * The database implementation is responsible for detecting potential
+ * duplicate user profiles; specifically, the login name, wiki name, and
+ * full name must be unique. The implementation is not required to check for
+ * validity of passwords or e-mail addresses. Special case: if the profile
+ * already exists and the password is null, it should retain its previous
+ * value, rather than being set to null.
+ * </p>
+ * <p>Implementations are <em>required</em> to time-stamp the creation
+ * or modification fields of the UserProfile./p>
+ * <p>This method is intended to be atomic; results cannot be partially committed.
+ * If the commit fails, it should roll back its state appropriately.
+ * Implementing classes that persist to the file system may wish to make
+ * this method <code>synchronized</code>.</p>
+ * @param profile the user profile to save
+ * @throws WikiSecurityException if the profile cannot be saved
+ */
+ public void save( UserProfile profile ) throws WikiSecurityException;
+
+ /**
+ * Determines whether a supplied user password is valid, given a login name
+ * and password. It is up to the implementing class to determine how the
+ * comparison should be made. For example, the password might be hashed
+ * before comparing it to the value persisted in the back-end data store.
+ * @param loginName the login name
+ * @param password the password
+ * @return <code>true</code> if the password is valid, <code>false</code>
+ * otherwise
+ */
+ public boolean validatePassword( String loginName, String password );
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserProfile.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserProfile.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserProfile.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserProfile.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,151 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.auth.user;
+
+import java.util.Date;
+
+/**
+ * Class for representing wiki user information, such as the login name, full
+ * name, wiki name, and e-mail address. Note that since 2.6 the wiki name is
+ * required to be automatically computed from the full name.
+ * @author Andrew Jaquith
+ * @since 2.3
+ */
+public interface UserProfile
+{
+
+ /**
+ * Returns the creation date.
+ * @return the creation date
+ */
+ public Date getCreated();
+
+ /**
+ * Returns the user's e-mail address.
+ * @return the e-mail address
+ */
+ public String getEmail();
+
+ /**
+ * Returns the user's full name.
+ * @return the full name
+ */
+ public String getFullname();
+
+ /**
+ * Returns the last-modified date.
+ * @return the date and time of last modification
+ */
+ public Date getLastModified();
+
+ /**
+ * Returns the user's login name.
+ * @return the login name
+ */
+ public String getLoginName();
+
+ /**
+ * Returns the user password for use with custom authentication. Note that
+ * the password field is not meaningful for container authentication; the
+ * user's private credentials are generally stored elsewhere. While it
+ * depends on the {@link UserDatabase}implementation, in most cases the
+ * value returned by this method will be a password hash, not the password
+ * itself.
+ * @return the password
+ */
+ public String getPassword();
+
+ /**
+ * Returns the user's wiki name, based on the full name with all
+ * whitespace removed.
+ * @return the wiki name.
+ */
+ public String getWikiName();
+
+ /**
+ * Returns <code>true</code> if the profile has never been
+ * saved before. Implementing classes might check the
+ * last modified date, for example, to determine this.
+ * @return whether the profile is new
+ */
+ public boolean isNew();
+
+ /**
+ * Sets the created date.
+ * @param date the creation date
+ */
+ public void setCreated( Date date );
+
+ /**
+ * Sets the user's e-mail address.
+ * @param email the e-mail address
+ */
+ public void setEmail( String email );
+
+ /**
+ * Sets the user's full name. For example, "Janne Jalkanen."
+ * @param arg the full name
+ */
+ public void setFullname( String arg );
+
+ /**
+ * Sets the last-modified date
+ * @param date the last-modified date
+ */
+ public void setLastModified( Date date );
+
+ /**
+ * Sets the name by which the user logs in. The login name is used as the
+ * username for custom authentication (see
+ * {@link com.ecyrd.jspwiki.auth.AuthenticationManager#login(WikiSession, String, String)},
+ * {@link com.ecyrd.jspwiki.auth.login.UserDatabaseLoginModule}). The login
+ * name is typically a short name ("jannej"). In contrast, the wiki name is
+ * typically of type FirstnameLastName ("JanneJalkanen").
+ * @param name the login name
+ */
+ public void setLoginName( String name );
+
+ /**
+ * Sets the user's password for use with custom authentication. It is
+ * <em>not</em> the responsibility of implementing classes to hash the
+ * password; that responsibility is borne by the UserDatabase implementation
+ * during save operations (see {@link UserDatabase#save(UserProfile)}).
+ * Note that the password field is not meaningful for container
+ * authentication; the user's private credentials are generally stored
+ * elsewhere.
+ * @param arg the password
+ */
+ public void setPassword( String arg );
+
+ /**
+ * No-op method. In previous versions of JSPWiki, the method
+ * set the user's wiki name directly. Now, the wiki name is automatically
+ * calculated based on the full name.
+ * @param name the wiki name
+ * @deprecated This method will be removed in a future release.
+ */
+ public void setWikiName( String name );
+
+ /**
+ * Returns a string representation of this user profile.
+ * @return the string
+ */
+ public String toString();
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/XMLUserDatabase.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/XMLUserDatabase.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/XMLUserDatabase.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/XMLUserDatabase.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,671 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2005 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.auth.user;
+
+import java.io.*;
+import java.security.Principal;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.ecyrd.jspwiki.NoRequiredPropertyException;
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
+import com.ecyrd.jspwiki.auth.WikiPrincipal;
+import com.ecyrd.jspwiki.auth.WikiSecurityException;
+
+/**
+ * <p>Manages {@link DefaultUserProfile} objects using XML files for persistence.
+ * Passwords are hashed using SHA1. User entries are simple <code><user></code>
+ * elements under the root. User profile properties are attributes of the
+ * element. For example:</p>
+ * <blockquote><code>
+ * <users><br/>
+ * <user loginName="janne" fullName="Janne Jalkanen"<br/>
+ * wikiName="JanneJalkanen" email="janne@ecyrd.com"<br/>
+ * password="{SHA}457b08e825da547c3b77fbc1ff906a1d00a7daee"/><br/>
+ * </users>
+ * </code></blockquote>
+ * <p>In this example, the un-hashed password is <code>myP@5sw0rd</code>. Passwords are hashed without salt.</p>
+ * @author Andrew Jaquith
+ * @since 2.3
+ */
+
+// FIXME: If the DB is shared across multiple systems, it's possible to lose accounts
+// if two people add new accounts right after each other from different wikis.
+public class XMLUserDatabase extends AbstractUserDatabase
+{
+
+ /**
+ * The jspwiki.properties property specifying the file system location of
+ * the user database.
+ */
+ public static final String PROP_USERDATABASE = "jspwiki.xmlUserDatabaseFile";
+
+ private static final String DEFAULT_USERDATABASE = "userdatabase.xml";
+
+ private static final String CREATED = "created";
+
+ private static final String EMAIL = "email";
+
+ private static final String FULL_NAME = "fullName";
+
+ private static final String LOGIN_NAME = "loginName";
+
+ private static final String LAST_MODIFIED = "lastModified";
+
+ private static final String PASSWORD = "password";
+
+ private static final String USER_TAG = "user";
+
+ private static final String WIKI_NAME = "wikiName";
+
+ private Document c_dom = null;
+
+ private DateFormat c_defaultFormat = DateFormat.getDateTimeInstance();
+
+ private DateFormat c_format = new SimpleDateFormat("yyyy.MM.dd 'at' HH:mm:ss:SSS z");
+
+ private File c_file = null;
+
+ /**
+ * Looks up and deletes the first {@link UserProfile} in the user database
+ * that matches a profile having a given login name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param loginName the login name of the user profile that shall be deleted
+ */
+ public synchronized void deleteByLoginName( String loginName ) throws NoSuchPrincipalException, WikiSecurityException
+ {
+ if ( c_dom == null )
+ {
+ throw new WikiSecurityException( "FATAL: database does not exist" );
+ }
+
+ NodeList users = c_dom.getDocumentElement().getElementsByTagName( USER_TAG );
+ for( int i = 0; i < users.getLength(); i++ )
+ {
+ Element user = (Element) users.item( i );
+ if ( user.getAttribute( LOGIN_NAME ).equals( loginName ) )
+ {
+ c_dom.getDocumentElement().removeChild(user);
+
+ // Commit to disk
+ saveDOM();
+ return;
+ }
+ }
+ throw new NoSuchPrincipalException( "Not in database: " + loginName );
+ }
+
+ /**
+ * Looks up and returns the first {@link UserProfile}in the user database
+ * that matches a profile having a given e-mail address. If the user
+ * database does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param index the e-mail address of the desired user profile
+ * @return the user profile
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByEmail(String)
+ */
+ public UserProfile findByEmail( String index ) throws NoSuchPrincipalException
+ {
+ UserProfile profile = findByAttribute( EMAIL, index );
+ if ( profile != null )
+ {
+ return profile;
+ }
+ throw new NoSuchPrincipalException( "Not in database: " + index );
+ }
+
+ /**
+ * Looks up and returns the first {@link UserProfile}in the user database
+ * that matches a profile having a given full name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param index the fill name of the desired user profile
+ * @return the user profile
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByFullName(java.lang.String)
+ */
+ public UserProfile findByFullName( String index ) throws NoSuchPrincipalException
+ {
+ UserProfile profile = findByAttribute( FULL_NAME, index );
+ if ( profile != null )
+ {
+ return profile;
+ }
+ throw new NoSuchPrincipalException( "Not in database: " + index );
+ }
+
+ /**
+ * Looks up and returns the first {@link UserProfile}in the user database
+ * that matches a profile having a given login name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param index the login name of the desired user profile
+ * @return the user profile
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByLoginName(java.lang.String)
+ */
+ public UserProfile findByLoginName( String index ) throws NoSuchPrincipalException
+ {
+ UserProfile profile = findByAttribute( LOGIN_NAME, index );
+ if ( profile != null )
+ {
+ return profile;
+ }
+ throw new NoSuchPrincipalException( "Not in database: " + index );
+ }
+
+ /**
+ * Looks up and returns the first {@link UserProfile}in the user database
+ * that matches a profile having a given wiki name. If the user database
+ * does not contain a user with a matching attribute, throws a
+ * {@link NoSuchPrincipalException}.
+ * @param index the wiki name of the desired user profile
+ * @return the user profile
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByWikiName(java.lang.String)
+ */
+ public UserProfile findByWikiName( String index ) throws NoSuchPrincipalException
+ {
+ UserProfile profile = findByAttribute( WIKI_NAME, index );
+ if ( profile != null )
+ {
+ return profile;
+ }
+ throw new NoSuchPrincipalException( "Not in database: " + index );
+ }
+
+ /**
+ * Returns all WikiNames that are stored in the UserDatabase
+ * as an array of WikiPrincipal objects. If the database does not
+ * contain any profiles, this method will return a zero-length
+ * array.
+ * @return the WikiNames
+ */
+ public Principal[] getWikiNames() throws WikiSecurityException
+ {
+ if ( c_dom == null )
+ {
+ throw new IllegalStateException( "FATAL: database does not exist" );
+ }
+ Set principals = new HashSet();
+ NodeList users = c_dom.getElementsByTagName( USER_TAG );
+ for( int i = 0; i < users.getLength(); i++ )
+ {
+ Element user = (Element) users.item( i );
+ String wikiName = user.getAttribute( WIKI_NAME );
+ if ( wikiName == null )
+ {
+ log.warn( "Detected null wiki name in XMLUserDataBase. Check your user database." );
+ }
+ else
+ {
+ Principal principal = new WikiPrincipal( wikiName, WikiPrincipal.WIKI_NAME );
+ principals.add( principal );
+ }
+ }
+ return (Principal[])principals.toArray( new Principal[principals.size()] );
+ }
+
+ /**
+ * Initializes the user database based on values from a Properties object.
+ * The properties object must contain a file path to the XML database file
+ * whose key is {@link #PROP_USERDATABASE}.
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#initialize(com.ecyrd.jspwiki.WikiEngine,
+ * java.util.Properties)
+ * @throws NoRequiredPropertyException if the user database cannot be located, parsed, or opened
+ */
+ public void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException
+ {
+ File defaultFile = null;
+ if( engine.getRootPath() == null )
+ {
+ log.warn( "Cannot identify JSPWiki root path" );
+ defaultFile = new File( "WEB-INF/" + DEFAULT_USERDATABASE ).getAbsoluteFile();
+ }
+ else
+ {
+ defaultFile = new File( engine.getRootPath() + "/WEB-INF/" + DEFAULT_USERDATABASE );
+ }
+
+ // Get database file location
+ String file = props.getProperty( PROP_USERDATABASE );
+ if( file == null )
+ {
+ log.error( "XML user database property " + PROP_USERDATABASE + " not found; trying " + defaultFile );
+ c_file = defaultFile;
+ }
+ else
+ {
+ c_file = new File( file );
+ }
+
+ log.info("XML user database at "+c_file.getAbsolutePath());
+
+ buildDOM();
+ sanitizeDOM();
+ }
+
+ private void buildDOM()
+ {
+ // Read DOM
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating( false );
+ factory.setExpandEntityReferences( false );
+ factory.setIgnoringComments( true );
+ factory.setNamespaceAware( false );
+ try
+ {
+ c_dom = factory.newDocumentBuilder().parse( c_file );
+ log.debug( "Database successfully initialized" );
+ c_lastModified = c_file.lastModified();
+ c_lastCheck = System.currentTimeMillis();
+ }
+ catch( ParserConfigurationException e )
+ {
+ log.error( "Configuration error: " + e.getMessage() );
+ }
+ catch( SAXException e )
+ {
+ log.error( "SAX error: " + e.getMessage() );
+ }
+ catch( FileNotFoundException e )
+ {
+ log.info("User database not found; creating from scratch...");
+ }
+ catch( IOException e )
+ {
+ log.error( "IO error: " + e.getMessage() );
+ }
+ if ( c_dom == null )
+ {
+ try
+ {
+ //
+ // Create the DOM from scratch
+ //
+ c_dom = factory.newDocumentBuilder().newDocument();
+ c_dom.appendChild( c_dom.createElement( "users") );
+ }
+ catch( ParserConfigurationException e )
+ {
+ log.fatal( "Could not create in-memory DOM" );
+ }
+ }
+ }
+
+ private void saveDOM() throws WikiSecurityException
+ {
+ if ( c_dom == null )
+ {
+ log.fatal( "User database doesn't exist in memory." );
+ }
+
+ File newFile = new File( c_file.getAbsolutePath() + ".new" );
+ try
+ {
+ BufferedWriter io = new BufferedWriter( new OutputStreamWriter (
+ new FileOutputStream( newFile ), "UTF-8" ) );
+
+ // Write the file header and document root
+ io.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ io.write("<users>\n");
+
+ // Write each profile as a <user> node
+ Element root = c_dom.getDocumentElement();
+ NodeList nodes = root.getElementsByTagName( USER_TAG );
+ for( int i = 0; i < nodes.getLength(); i++ )
+ {
+ Element user = (Element)nodes.item( i );
+ io.write( "<" + USER_TAG + " ");
+ io.write( LOGIN_NAME );
+ io.write( "=\"" + user.getAttribute( LOGIN_NAME ) + "\" " );
+ io.write( WIKI_NAME );
+ io.write( "=\"" + user.getAttribute( WIKI_NAME ) + "\" " );
+ io.write( FULL_NAME );
+ io.write( "=\"" + user.getAttribute( FULL_NAME ) + "\" " );
+ io.write( EMAIL );
+ io.write( "=\"" + user.getAttribute( EMAIL ) + "\" " );
+ io.write( PASSWORD );
+ io.write( "=\"" + user.getAttribute( PASSWORD ) + "\" " );
+ io.write( CREATED );
+ io.write( "=\"" + user.getAttribute( CREATED ) + "\" " );
+ io.write( LAST_MODIFIED );
+ io.write( "=\"" + user.getAttribute( LAST_MODIFIED ) + "\" " );
+ io.write(" />\n");
+ }
+ io.write("</users>");
+ io.close();
+ }
+ catch ( IOException e )
+ {
+ throw new WikiSecurityException( e.getLocalizedMessage() );
+ }
+
+ // Copy new file over old version
+ File backup = new File( c_file.getAbsolutePath() + ".old" );
+ if ( backup.exists() )
+ {
+ if ( !backup.delete() )
+ {
+ log.error( "Could not delete old user database backup: " + backup );
+ }
+ }
+ if ( !c_file.renameTo( backup ) )
+ {
+ log.error( "Could not create user database backup: " + backup );
+ }
+ if ( !newFile.renameTo( c_file ) )
+ {
+ log.error( "Could not save database: " + backup + " restoring backup." );
+ if ( !backup.renameTo( c_file ) )
+ {
+ log.error( "Restore failed. Check the file permissions." );
+ }
+ log.error( "Could not save database: " + c_file + ". Check the file permissions" );
+ }
+ }
+
+ private long c_lastCheck = 0;
+ private long c_lastModified = 0;
+
+ private void checkForRefresh()
+ {
+ long time = System.currentTimeMillis();
+
+ if( time - c_lastCheck > 60*1000L )
+ {
+ long lastModified = c_file.lastModified();
+
+ if( lastModified > c_lastModified )
+ {
+ buildDOM();
+ }
+ }
+ }
+
+ /**
+ * Determines whether the user database shares user/password data with the
+ * web container; always returns <code>false</code>.
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#isSharedWithContainer()
+ */
+ public boolean isSharedWithContainer()
+ {
+ return false;
+ }
+
+ /**
+ * @see com.ecyrd.jspwiki.auth.user.UserDatabase#rename(String, String)
+ */
+ public synchronized void rename(String loginName, String newName) throws NoSuchPrincipalException, DuplicateUserException, WikiSecurityException
+ {
+ if ( c_dom == null )
+ {
+ log.fatal( "Could not rename profile '" + loginName + "'; database does not exist" );
+ throw new IllegalStateException( "FATAL: database does not exist" );
+ }
+ checkForRefresh();
+
+ // Get the existing user; if not found, throws NoSuchPrincipalException
+ UserProfile profile = findByLoginName( loginName );
+
+ // Get user with the proposed name; if found, it's a collision
+ try
+ {
+ UserProfile otherProfile = findByLoginName( newName );
+ if ( otherProfile != null )
+ {
+ throw ( new DuplicateUserException( "Cannot rename: the login name '" + newName + "' is already taken." ) );
+ }
+ }
+ catch ( NoSuchPrincipalException e )
+ {
+ // Good! That means it's safe to save using the new name
+ }
+
+ // Find the user with the old login id attribute, and change it
+ NodeList users = c_dom.getElementsByTagName( USER_TAG );
+ for( int i = 0; i < users.getLength(); i++ )
+ {
+ Element user = (Element) users.item( i );
+ if ( user.getAttribute( LOGIN_NAME ).equals( loginName ) )
+ {
+ Date modDate = new Date( System.currentTimeMillis() );
+ setAttribute( user, LOGIN_NAME, newName );
+ setAttribute( user, LAST_MODIFIED, c_format.format( modDate ) );
+ profile.setLoginName( newName );
+ profile.setLastModified( modDate );
+ break;
+ }
+ }
+
+ // Commit to disk
+ saveDOM();
+ }
+
+ /**
+ * Saves a {@link UserProfile}to the user database, overwriting the
+ * existing profile if it exists. The user name under which the profile
+ * should be saved is returned by the supplied profile's
+ * {@link UserProfile#getLoginName()}method.
+ * @param profile the user profile to save
+ * @throws WikiSecurityException if the profile cannot be saved
+ */
+ public synchronized void save( UserProfile profile ) throws WikiSecurityException
+ {
+ if ( c_dom == null )
+ {
+ log.fatal( "Could not save profile " + profile + " database does not exist" );
+ throw new IllegalStateException( "FATAL: database does not exist" );
+ }
+
+ checkForRefresh();
+
+ String index = profile.getLoginName();
+ NodeList users = c_dom.getElementsByTagName( USER_TAG );
+ Element user = null;
+ boolean isNew = true;
+ for( int i = 0; i < users.getLength(); i++ )
+ {
+ Element currentUser = (Element) users.item( i );
+ if ( currentUser.getAttribute( LOGIN_NAME ).equals( index ) )
+ {
+ user = currentUser;
+ isNew = false;
+ break;
+ }
+ }
+ Date modDate = new Date( System.currentTimeMillis() );
+ if ( isNew )
+ {
+ profile.setCreated( modDate );
+ log.info( "Creating new user " + index );
+ user = c_dom.createElement( USER_TAG );
+ c_dom.getDocumentElement().appendChild( user );
+ setAttribute( user, CREATED, c_format.format( profile.getCreated() ) );
+ }
+ setAttribute( user, LAST_MODIFIED, c_format.format( modDate ) );
+ setAttribute( user, LOGIN_NAME, profile.getLoginName() );
+ setAttribute( user, FULL_NAME, profile.getFullname() );
+ setAttribute( user, WIKI_NAME, profile.getWikiName() );
+ setAttribute( user, EMAIL, profile.getEmail() );
+
+ // Hash and save the new password if it's different from old one
+ String newPassword = profile.getPassword();
+ if ( newPassword != null && !newPassword.equals( "" ) )
+ {
+ String oldPassword = user.getAttribute( PASSWORD );
+ if ( !oldPassword.equals( newPassword ) )
+ {
+ setAttribute( user, PASSWORD, SHA_PREFIX + getHash( newPassword ) );
+ }
+ }
+
+ // Set the profile timestamps
+ if ( isNew )
+ {
+ profile.setCreated( modDate );
+ }
+ profile.setLastModified( modDate );
+
+ // Commit to disk
+ saveDOM();
+ }
+
+ /**
+ * Private method that returns the first {@link UserProfile}matching a
+ * <user> element's supplied attribute.
+ * @param matchAttribute
+ * @param index
+ * @return the profile, or <code>null</code> if not found
+ */
+ private UserProfile findByAttribute( String matchAttribute, String index )
+ {
+ if ( c_dom == null )
+ {
+ throw new IllegalStateException( "FATAL: database does not exist" );
+ }
+
+ checkForRefresh();
+
+ NodeList users = c_dom.getElementsByTagName( USER_TAG );
+ for( int i = 0; i < users.getLength(); i++ )
+ {
+ Element user = (Element) users.item( i );
+ if ( user.getAttribute( matchAttribute ).equals( index ) )
+ {
+ UserProfile profile = new DefaultUserProfile();
+ profile.setLoginName( user.getAttribute( LOGIN_NAME ) );
+ profile.setFullname( user.getAttribute( FULL_NAME ) );
+ profile.setPassword( user.getAttribute( PASSWORD ) );
+ profile.setEmail( user.getAttribute( EMAIL ) );
+ String created = user.getAttribute( CREATED );
+ String modified = user.getAttribute( LAST_MODIFIED );
+
+ profile.setCreated( parseDate( profile, created ) );
+ profile.setLastModified( parseDate( profile, modified ) );
+
+ return profile;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Tries to parse a date using the default format - then, for backwards
+ * compatibility reasons, tries the platform default.
+ *
+ * @param profile
+ * @param date
+ * @return A parsed date, or null, if both parse attempts fail.
+ */
+ private Date parseDate( UserProfile profile, String date )
+ {
+ try
+ {
+ return c_format.parse( date );
+ }
+ catch( ParseException e )
+ {
+ try
+ {
+ return c_defaultFormat.parse( date );
+ }
+ catch ( ParseException e2)
+ {
+ log.warn("Could not parse 'created' or 'lastModified' "
+ + "attribute for "
+ + " profile '" + profile.getLoginName() + "'."
+ + " It may have been tampered with." );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * After loading the DOM, this method sanity-checks the dates in the DOM and makes
+ * sure they are formatted properly. This is sort-of hacky, but it should work.
+ */
+ private void sanitizeDOM()
+ {
+ if ( c_dom == null )
+ {
+ throw new IllegalStateException( "FATAL: database does not exist" );
+ }
+
+ NodeList users = c_dom.getElementsByTagName( USER_TAG );
+ for( int i = 0; i < users.getLength(); i++ )
+ {
+ Element user = (Element) users.item( i );
+ String loginName = user.getAttribute( LOGIN_NAME );
+ String created = user.getAttribute( CREATED );
+ String modified = user.getAttribute( LAST_MODIFIED );
+ try
+ {
+ created = c_format.format( c_format.parse( created ) );
+ modified = c_format.format( c_format.parse( modified ) );
+ user.setAttribute( CREATED, created );
+ user.setAttribute( LAST_MODIFIED, modified );
+ }
+ catch( ParseException e )
+ {
+ try
+ {
+ created = c_format.format( c_defaultFormat.parse( created ) );
+ modified = c_format.format( c_defaultFormat.parse( modified ) );
+ user.setAttribute( CREATED, created );
+ user.setAttribute( LAST_MODIFIED, modified );
+ }
+ catch ( ParseException e2)
+ {
+ log.warn("Could not parse 'created' or 'lastModified' "
+ + "attribute for "
+ + " profile '" + loginName + "'."
+ + " It may have been tampered with." );
+ }
+ }
+ }
+ }
+
+ /**
+ * Private method that sets an attibute value for a supplied DOM element.
+ * @param element the element whose attribute is to be set
+ * @param attribute the name of the attribute to set
+ * @param value the desired attribute value
+ */
+ private void setAttribute( Element element, String attribute, String value )
+ {
+ if ( value != null )
+ {
+ element.setAttribute( attribute, value );
+ }
+ }
+}
\ No newline at end of file
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/dav/AttachmentDavProvider.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,179 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.dav;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+
+import com.ecyrd.jspwiki.WikiEngine;
+import com.ecyrd.jspwiki.WikiPage;
+import com.ecyrd.jspwiki.action.AttachActionBean;
+import com.ecyrd.jspwiki.attachment.Attachment;
+import com.ecyrd.jspwiki.dav.items.AttachmentItem;
+import com.ecyrd.jspwiki.dav.items.DavItem;
+import com.ecyrd.jspwiki.dav.items.DirectoryItem;
+import com.ecyrd.jspwiki.providers.ProviderException;
+
+public class AttachmentDavProvider implements DavProvider
+{
+ protected WikiEngine m_engine;
+ protected static final Logger log = Logger.getLogger( AttachmentDavProvider.class );
+
+ public AttachmentDavProvider( WikiEngine engine )
+ {
+ m_engine = engine;
+ }
+
+ public WikiEngine getEngine()
+ {
+ return m_engine;
+ }
+
+ private Collection listAllPagesWithAttachments()
+ {
+ ArrayList pageNames = new ArrayList();
+
+ try
+ {
+ Collection atts = m_engine.getAttachmentManager().getAllAttachments();
+
+ for( Iterator i = atts.iterator(); i.hasNext(); )
+ {
+ Attachment att = (Attachment)i.next();
+
+ String pageName = att.getParentName();
+
+ if( !pageNames.contains(pageName) )
+ pageNames.add( pageName );
+ }
+ }
+ catch (ProviderException e)
+ {
+ log.error("Unable to get all attachments",e);
+ }
+
+ Collections.sort( pageNames );
+
+ ArrayList result = new ArrayList();
+
+ for( Iterator i = pageNames.iterator(); i.hasNext(); )
+ {
+ DirectoryItem di = new DirectoryItem( this, new DavPath( (String)i.next() ));
+
+ result.add( di );
+ }
+ return result;
+ }
+
+ protected Collection listAttachmentsOfPage( DavPath path )
+ {
+ String pageName = path.getName();
+
+ log.debug("Listing attachments for page "+pageName);
+
+ ArrayList result = new ArrayList();
+ try
+ {
+ WikiPage page = m_engine.getPage( pageName );
+ Collection attachments = m_engine.getAttachmentManager().listAttachments(page);
+
+ for( Iterator i = attachments.iterator(); i.hasNext(); )
+ {
+ Attachment att = (Attachment) i.next();
+
+ DavPath thisPath = new DavPath( "/" );
+
+ thisPath.append( att.getName() );
+
+ AttachmentItem ai = new AttachmentItem( this, thisPath, att );
+
+ result.add( ai );
+ }
+ }
+ catch( ProviderException e )
+ {
+ log.error("Unable to list attachments, returning what I got",e);
+ // FIXME: Not a good way to handle errors
+ }
+
+ return result;
+ }
+
+ public DavItem getItem(DavPath path)
+ {
+ if( path.isRoot() )
+ {
+ DirectoryItem di = new DirectoryItem( this, new DavPath("") );
+
+ di.addDavItems( listAllPagesWithAttachments() );
+ return di;
+ }
+ else if( path.isDirectory() )
+ {
+ DirectoryItem di = new DirectoryItem( this, path );
+
+ di.addDavItems( listAttachmentsOfPage(path) );
+
+ return di;
+ }
+ else
+ {
+ String attName = path.getPath();
+
+ try
+ {
+ Attachment att = m_engine.getAttachmentManager().getAttachmentInfo( attName );
+
+ if( att != null )
+ {
+ AttachmentItem ai = new AttachmentItem( this, path, att );
+
+ return ai;
+ }
+ }
+ catch( ProviderException e )
+ {
+ log.error("Unable to get the attachment data for "+attName,e);
+ }
+ }
+ return null;
+ }
+
+ public void setItem(DavPath path, DavItem item)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public String getURL(DavPath path)
+ {
+ String p = path.getPath();
+
+ if( p.startsWith("/") ) p = p.substring( 1 );
+
+ String url = DavUtil.combineURL( DavUtil.combineURL( m_engine.getBaseURL() , "attach/"), path.getPath() );
+ return url;
+ }
+
+}
Added: incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/dav/DavContext.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/dav/DavContext.java?rev=627255&view=auto
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/dav/DavContext.java (added)
+++ incubator/jspwiki/branches/JSPWIKI_STRIPES_BRANCH/src/com/ecyrd/jspwiki/dav/DavContext.java Tue Feb 12 21:53:55 2008
@@ -0,0 +1,61 @@
+/*
+ JSPWiki - a JSP-based WikiWiki clone.
+
+ Copyright (C) 2001-2002 Janne Jalkanen (Janne.Jalkanen@iki.fi)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.ecyrd.jspwiki.dav;
+
+import javax.servlet.http.HttpServletRequest;
+
+
+public class DavContext
+{
+ protected int m_depth = -1;
+ protected DavPath m_path;
+
+ public DavContext( HttpServletRequest req, DavPath dp )
+ {
+ m_path = dp;
+
+ String depth = req.getHeader("Depth");
+
+ if( depth == null )
+ {
+ m_depth = -1;
+ }
+ else
+ {
+ m_depth = Integer.parseInt( depth );
+ }
+ }
+
+ /**
+ * @return Returns the m_depth.
+ */
+ public int getDepth()
+ {
+ return m_depth;
+ }
+
+ /**
+ * @return Returns the m_path.
+ */
+ public DavPath getPath()
+ {
+ return m_path;
+ }
+}
\ No newline at end of file