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/08/03 14:17:35 UTC
svn commit: r682144 [3/4] - in
/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki:
auth/authorize/ auth/login/ auth/permissions/ auth/user/ content/
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/AbstractUserDatabase.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/AbstractUserDatabase.java?rev=682144&r1=682143&r2=682144&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/AbstractUserDatabase.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/AbstractUserDatabase.java Sun Aug 3 05:17:34 2008
@@ -1,30 +1,30 @@
-/*
+/*
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
+ 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 com.ecyrd.jspwiki.auth.user;
-import java.io.UnsupportedEncodingException;
+import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Properties;
+import java.util.*;
import org.apache.catalina.util.HexUtils;
import org.apache.log4j.Logger;
@@ -34,6 +34,7 @@
import com.ecyrd.jspwiki.auth.NoSuchPrincipalException;
import com.ecyrd.jspwiki.auth.WikiPrincipal;
import com.ecyrd.jspwiki.auth.WikiSecurityException;
+import com.ecyrd.jspwiki.util.CryptoUtil;
/**
* Abstract UserDatabase class that provides convenience methods for finding
@@ -46,8 +47,8 @@
protected static final Logger log = Logger.getLogger( AbstractUserDatabase.class );
protected static final String SHA_PREFIX = "{SHA}";
- protected static final String PROP_SHARED_WITH_CONTAINER = "jspwiki.userdatabase.isSharedWithContainer";
-
+ protected static final String SSHA_PREFIX = "{SSHA}";
+ protected static final long UID_NOT_SET = 0;
/**
* No-op method that in previous versions of JSPWiki was intended to
@@ -58,6 +59,7 @@
* @deprecated there is no need to call this method because the save, rename and
* delete methods contain their own commit logic
*/
+ @SuppressWarnings("deprecation")
public synchronized void commit() throws WikiSecurityException
{ }
@@ -117,21 +119,25 @@
}
/**
+ * {@inheritDoc}
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByEmail(java.lang.String)
*/
public abstract UserProfile findByEmail( String index ) throws NoSuchPrincipalException;
/**
+ * {@inheritDoc}
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByFullName(java.lang.String)
*/
public abstract UserProfile findByFullName( String index ) throws NoSuchPrincipalException;
/**
+ * {@inheritDoc}
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByLoginName(java.lang.String)
*/
public abstract UserProfile findByLoginName( String index ) throws NoSuchPrincipalException;
/**
+ * {@inheritDoc}
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByWikiName(java.lang.String)
*/
public abstract UserProfile findByWikiName( String index ) throws NoSuchPrincipalException;
@@ -149,13 +155,14 @@
* {@link UserProfile#getLoginName()}method.
* @return the array of Principals representing the user
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#getPrincipals(java.lang.String)
+ * @throws NoSuchPrincipalException {@inheritDoc}
*/
public Principal[] getPrincipals( String identifier ) throws NoSuchPrincipalException
{
try
{
UserProfile profile = findByLoginName( identifier );
- ArrayList principals = new ArrayList();
+ ArrayList<Principal> principals = new ArrayList<Principal>();
if ( profile.getLoginName() != null && profile.getLoginName().length() > 0 )
{
principals.add( new WikiPrincipal( profile.getLoginName(), WikiPrincipal.LOGIN_NAME ) );
@@ -168,7 +175,7 @@
{
principals.add( new WikiPrincipal( profile.getWikiName(), WikiPrincipal.WIKI_NAME ) );
}
- return (Principal[]) principals.toArray( new Principal[principals.size()] );
+ return principals.toArray( new Principal[principals.size()] );
}
catch( NoSuchPrincipalException e )
{
@@ -177,20 +184,24 @@
}
/**
+ * {@inheritDoc}
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#initialize(com.ecyrd.jspwiki.WikiEngine, java.util.Properties)
*/
public abstract void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException;
/**
- * Factory method that instantiates a new DefaultUserProfile.
- * @see com.ecyrd.jspwiki.auth.user.UserDatabase#newProfile()
+ * Factory method that instantiates a new DefaultUserProfile with a new, distinct
+ * unique identifier.
+ *
+ * @return A new, empty profile.
*/
public UserProfile newProfile()
{
- return new DefaultUserProfile();
+ return DefaultUserProfile.newProfile( this );
}
/**
+ * {@inheritDoc}
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#save(com.ecyrd.jspwiki.auth.user.UserProfile)
*/
public abstract void save( UserProfile profile ) throws WikiSecurityException;
@@ -206,26 +217,117 @@
* @param password the user's password (obtained from user input, e.g., a web form)
* @return <code>true</code> if the supplied user password matches the
* stored password
+ * @throws NoSuchAlgorithmException
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#validatePassword(java.lang.String,
* java.lang.String)
*/
public boolean validatePassword( String loginName, String password )
{
- String hashedPassword = getHash( password );
+ String hashedPassword;
try
{
UserProfile profile = findByLoginName( loginName );
String storedPassword = profile.getPassword();
+
+ // Is the password stored as a salted hash (the new 2.8 format?)
+ boolean newPasswordFormat = storedPassword.startsWith( SSHA_PREFIX );
+
+ // If new format, verify the hash
+ if ( newPasswordFormat )
+ {
+ hashedPassword = getHash( password );
+ return CryptoUtil.verifySaltedPassword( password.getBytes("UTF-8"), storedPassword );
+ }
+
+ // If old format, verify using the old SHA verification algorithm
if ( storedPassword.startsWith( SHA_PREFIX ) )
{
storedPassword = storedPassword.substring( SHA_PREFIX.length() );
}
- return hashedPassword.equals( storedPassword );
+ hashedPassword = getOldHash( password );
+ boolean verified = hashedPassword.equals( storedPassword );
+
+ // If in the old format and password verified, upgrade the hash to SSHA
+ if ( verified )
+ {
+ profile.setPassword( password );
+ save( profile );
+ }
+
+ return verified;
}
catch( NoSuchPrincipalException e )
{
- return false;
}
+ catch( NoSuchAlgorithmException e )
+ {
+ log.error( "Unsupported algorithm: " + e.getMessage() );
+ }
+ catch( UnsupportedEncodingException e )
+ {
+ log.fatal( "You do not have UTF-8!?!" );
+ }
+ catch( WikiSecurityException e )
+ {
+ log.error( "Could not upgrade SHA password to SSHA because profile could not be saved. Reason: " + e.getMessage() );
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * Generates a new random user identifier (uid) that is guaranteed to be unique.
+ *
+ * @param db The database for which the UID should be generated.
+ * @return A random, unique UID.
+ */
+ protected static long generateUid( UserDatabase db )
+ {
+ // Keep generating UUIDs until we find one that doesn't collide
+ long uid;
+ boolean collision;
+
+ do
+ {
+ uid = UUID.randomUUID().getLeastSignificantBits();
+ collision = true;
+ try
+ {
+ db.findByUid( uid );
+ }
+ catch ( NoSuchPrincipalException e )
+ {
+ collision = false;
+ }
+ }
+ while ( collision || uid == UID_NOT_SET );
+ return uid;
+ }
+
+ /**
+ * Private method that calculates the salted SHA-1 hash of a given
+ * <code>String</code>. Note that as of JSPWiki 2.8, this method calculates
+ * a <em>salted</em> hash rather than a plain hash.
+ * @param text the text to hash
+ * @return the result hash
+ */
+ protected String getHash( String text )
+ {
+ String hash = null;
+ try
+ {
+ hash = CryptoUtil.getSaltedPassword( text.getBytes("UTF-8") );
+ }
+ catch( NoSuchAlgorithmException e )
+ {
+ log.error( "Error creating salted SHA password hash:" + e.getMessage() );
+ hash = text;
+ }
+ catch( UnsupportedEncodingException e )
+ {
+ log.fatal("You do not have UTF-8!?!");
+ }
+ return hash;
}
/**
@@ -233,8 +335,9 @@
* <code>String</code>
* @param text the text to hash
* @return the result hash
+ * @deprecated this method is retained for backwards compatibility purposes; use {@link #getHash(String)} instead
*/
- protected String getHash( String text )
+ protected String getOldHash( String text )
{
String hash = null;
try
@@ -256,4 +359,25 @@
return hash;
}
+ /**
+ * Parses a long integer from a supplied string, or returns 0 if not parsable.
+ * @param value the string to parse
+ * @return the value parsed
+ */
+ protected long parseLong( String value )
+ {
+ if ( value == null || value.length() == 0 )
+ {
+ return 0;
+ }
+ try
+ {
+ return Long.parseLong( value );
+ }
+ catch ( NumberFormatException e )
+ {
+ return 0;
+ }
+ }
+
}
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/DefaultUserProfile.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/DefaultUserProfile.java?rev=682144&r1=682143&r2=682144&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/DefaultUserProfile.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/DefaultUserProfile.java Sun Aug 3 05:17:34 2008
@@ -1,54 +1,87 @@
-/*
- JSPWiki - a JSP-based WikiWiki clone.
+/*
+ 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
+ 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 com.ecyrd.jspwiki.auth.user;
+import java.io.Serializable;
import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
/**
* Default implementation for representing wiki user information, such as the
* login name, full name, wiki name, and e-mail address.
- * @author Janne Jalkanen
* @author Andrew Jaquith
* @since 2.3
*/
-public class DefaultUserProfile implements UserProfile
+public final class DefaultUserProfile implements UserProfile
{
+ private static final long serialVersionUID = -5600466893735300647L;
+
private static final String EMPTY_STRING = "";
private static final String WHITESPACE = "\\s";
+
+ private Map<String,Serializable> m_attributes = new HashMap<String,Serializable>();
private Date m_created = null;
private String m_email = null;
private String m_fullname = null;
+
+ private Date m_lockExpiry = null;
private String m_loginName = null;
private Date m_modified = null;
private String m_password = null;
+
+ private long m_uid = -1;
private String m_wikiname = null;
+ /**
+ * Private constructor to prevent direct instantiation.
+ */
+ private DefaultUserProfile() {}
+
+ /**
+ * Static factory method that creates a new DefaultUserProfile
+ * and sets a unique identifier (uid) for the supplied UserDatabase.
+ * @param db the UserDatabase for which the uid should be
+ * created
+ * @return the new profile
+ */
+ protected static UserProfile newProfile( UserDatabase db )
+ {
+ UserProfile profile = new DefaultUserProfile();
+ profile.setUid( AbstractUserDatabase.generateUid( db ) );
+ return profile;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public boolean equals( Object o )
{
if ( ( o != null ) && ( o instanceof UserProfile ) )
@@ -231,6 +264,7 @@
* @param name the wiki name
* @deprecated This method will be removed in a future release.
*/
+ @SuppressWarnings("deprecation")
public void setWikiName( String name )
{
}
@@ -263,4 +297,61 @@
}
return arg1.equals( arg2 );
}
+
+ //--------------------------- Attribute and lock interface implementations ---------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String,Serializable> getAttributes()
+ {
+ return m_attributes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Date getLockExpiry()
+ {
+ return isLocked() ? m_lockExpiry : null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getUid()
+ {
+ return m_uid;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isLocked()
+ {
+ boolean locked = m_lockExpiry != null && System.currentTimeMillis() < m_lockExpiry.getTime();
+
+ // Clear the lock if it's expired already
+ if ( !locked && m_lockExpiry != null )
+ {
+ m_lockExpiry = null;
+ }
+ return locked;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setLockExpiry( Date expiry )
+ {
+ m_lockExpiry = expiry;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setUid( long uid )
+ {
+ m_uid = uid;
+ }
}
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/DuplicateUserException.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/DuplicateUserException.java?rev=682144&r1=682143&r2=682144&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/DuplicateUserException.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/DuplicateUserException.java Sun Aug 3 05:17:34 2008
@@ -1,21 +1,22 @@
/*
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
+ 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 com.ecyrd.jspwiki.auth.user;
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/JDBCUserDatabase.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/JDBCUserDatabase.java?rev=682144&r1=682143&r2=682144&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/JDBCUserDatabase.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/JDBCUserDatabase.java Sun Aug 3 05:17:34 2008
@@ -1,30 +1,30 @@
-/*
+/*
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
+ 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 com.ecyrd.jspwiki.auth.user;
+import java.io.*;
import java.security.Principal;
import java.sql.*;
+import java.util.*;
import java.util.Date;
-import java.util.HashSet;
-import java.util.Properties;
-import java.util.Set;
import javax.naming.Context;
import javax.naming.InitialContext;
@@ -32,103 +32,119 @@
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;
+import com.ecyrd.jspwiki.util.Serializer;
/**
- * <p>Implementation of UserDatabase that persists {@link DefaultUserProfile}
+ * <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>
+ * 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>
+ * <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.attributes</code></td>
+ * <td><code>attributes</code></td>
+ * <td>The CLOB column containing the profile's custom attributes, stored as key/value strings, each separated by newline.</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.uid</code></td>
+ * <td><code>uid</code></td>
+ * <td>The column containing the profile's unique identifier, as a long integer</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.lockExpiry</code></td>
+ * <td><code>lock_expiry</code></td>
+ * <td>The column containing the date/time when the profile, if locked, should be unlocked.</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>
* </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>
+ * <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/>
@@ -137,98 +153,145 @@
* 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>
+ * <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
+ */
+public class JDBCUserDatabase extends AbstractUserDatabase
{
private static final String NOTHING = "";
- public static final String DEFAULT_DB_CREATED = "created";
+ public static final String DEFAULT_DB_ATTRIBUTES = "attributes";
- public static final String DEFAULT_DB_EMAIL = "email";
+ public static final String DEFAULT_DB_CREATED = "created";
- public static final String DEFAULT_DB_FULL_NAME = "full_name";
+ public static final String DEFAULT_DB_EMAIL = "email";
- public static final String DEFAULT_DB_HASH_PREFIX = "true";
+ public static final String DEFAULT_DB_FULL_NAME = "full_name";
- public static final String DEFAULT_DB_JNDI_NAME = "jdbc/UserDatabase";
+ public static final String DEFAULT_DB_JNDI_NAME = "jdbc/UserDatabase";
- public static final String DEFAULT_DB_MODIFIED = "modified";
+ public static final String DEFAULT_DB_LOCK_EXPIRY = "lock_expiry";
- public static final String DEFAULT_DB_ROLE = "role";
+ 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_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_PASSWORD = "password";
+
+ public static final String DEFAULT_DB_UID = "uid";
+
+ public static final String DEFAULT_DB_WIKI_NAME = "wiki_name";
+
+ public static final String PROP_DB_ATTRIBUTES = "jspwiki.userdatabase.attributes";
+
+ public static final String PROP_DB_CREATED = "jspwiki.userdatabase.created";
- public static final String DEFAULT_DB_WIKI_NAME = "wiki_name";
+ public static final String PROP_DB_EMAIL = "jspwiki.userdatabase.email";
- public static final String PROP_DB_CREATED = "jspwiki.userdatabase.created";
+ public static final String PROP_DB_FULL_NAME = "jspwiki.userdatabase.fullName";
- public static final String PROP_DB_EMAIL = "jspwiki.userdatabase.email";
+ public static final String PROP_DB_DATASOURCE = "jspwiki.userdatabase.datasource";
- public static final String PROP_DB_FULL_NAME = "jspwiki.userdatabase.fullName";
+ public static final String PROP_DB_LOCK_EXPIRY = "jspwiki.userdatabase.lockExpiry";
- public static final String PROP_DB_DATASOURCE = "jspwiki.userdatabase.datasource";
+ public static final String PROP_DB_LOGIN_NAME = "jspwiki.userdatabase.loginName";
- public static final String PROP_DB_HASH_PREFIX = "jspwiki.userdatabase.hashPrefix";
+ public static final String PROP_DB_MODIFIED = "jspwiki.userdatabase.modified";
- public static final String PROP_DB_LOGIN_NAME = "jspwiki.userdatabase.loginName";
+ public static final String PROP_DB_PASSWORD = "jspwiki.userdatabase.password";
- public static final String PROP_DB_MODIFIED = "jspwiki.userdatabase.modified";
+ public static final String PROP_DB_UID = "jspwiki.userdatabase.uid";
- 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 = "jspwiki.userdatabase.role";
+ public static final String PROP_DB_ROLE_TABLE = "jspwiki.userdatabase.roleTable";
- 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_TABLE = "jspwiki.userdatabase.table";
+ public static final String PROP_DB_WIKI_NAME = "jspwiki.userdatabase.wikiName";
- public static final String PROP_DB_WIKI_NAME = "jspwiki.userdatabase.wikiName";
+ private DataSource m_ds = null;
- 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_findByUid = 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_attributes = null;
+
private String m_email = null;
+
private String m_fullName = null;
- private boolean m_hashPrefix = true;
+
+ private String m_lockExpiry = null;
+
private String m_loginName = null;
+
private String m_password = null;
+
private String m_role = null;
+
private String m_roleTable = null;
+
+ private String m_uid = 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;
/**
@@ -237,8 +300,9 @@
* 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>.
+ * 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
@@ -246,12 +310,12 @@
// 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 )
+ if( m_supportsCommits )
{
conn.setAutoCommit( false );
}
@@ -259,29 +323,35 @@
PreparedStatement ps;
// Delete user record
ps = conn.prepareStatement( m_deleteUserByLoginName );
- ps.setString(1, loginName );
+ ps.setString( 1, loginName );
ps.execute();
ps.close();
// Delete role record
ps = conn.prepareStatement( m_deleteRoleByLoginName );
- ps.setString(1, loginName );
+ ps.setString( 1, loginName );
ps.execute();
ps.close();
// Commit and close connection
- if ( m_supportsCommits )
+ if( m_supportsCommits )
{
conn.commit();
}
}
- catch ( SQLException e )
+ catch( SQLException e )
{
throw new WikiSecurityException( e.getMessage() );
}
finally
{
- try { conn.close(); } catch (Exception e) {}
+ try
+ {
+ if( conn != null ) conn.close();
+ }
+ catch( Exception e )
+ {
+ }
}
}
@@ -312,21 +382,29 @@
/**
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#findByWikiName(String)
*/
+ public UserProfile findByUid( long uid ) throws NoSuchPrincipalException
+ {
+ return findByPreparedStatement( m_findByUid, Long.valueOf( uid ) );
+ }
+
+ /**
+ * @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.
+ * 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();
+ Set<Principal> principals = new HashSet<Principal>();
Connection conn = null;
try
{
@@ -336,7 +414,7 @@
while ( rs.next() )
{
String wikiName = rs.getString( m_wikiName );
- if ( wikiName == null )
+ if( wikiName == null )
{
log.warn( "Detected null wiki name in XMLUserDataBase. Check your user database." );
}
@@ -348,21 +426,27 @@
}
ps.close();
}
- catch ( SQLException e )
+ catch( SQLException e )
{
throw new WikiSecurityException( e.getMessage() );
}
finally
{
- try { conn.close(); } catch (Exception e) {}
+ try
+ {
+ if( conn != null ) conn.close();
+ }
+ catch( Exception e )
+ {
+ }
}
- return (Principal[])principals.toArray( new Principal[principals.size()] );
+ return principals.toArray( new Principal[principals.size()] );
}
/**
* @see com.ecyrd.jspwiki.auth.user.UserDatabase#initialize(com.ecyrd.jspwiki.WikiEngine,
- * java.util.Properties)
+ * java.util.Properties)
*/
public void initialize( WikiEngine engine, Properties props ) throws NoRequiredPropertyException
{
@@ -370,51 +454,60 @@
try
{
Context initCtx = new InitialContext();
- Context ctx = (Context) initCtx.lookup("java:comp/env");
+ 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_email = props.getProperty( PROP_DB_EMAIL, DEFAULT_DB_EMAIL );
+ m_fullName = props.getProperty( PROP_DB_FULL_NAME, DEFAULT_DB_FULL_NAME );
+ m_lockExpiry = props.getProperty( PROP_DB_LOCK_EXPIRY, DEFAULT_DB_LOCK_EXPIRY );
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_password = props.getProperty( PROP_DB_PASSWORD, DEFAULT_DB_PASSWORD );
+ m_uid = props.getProperty( PROP_DB_UID, DEFAULT_DB_UID );
+ 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_attributes = props.getProperty( PROP_DB_ATTRIBUTES, DEFAULT_DB_ATTRIBUTES );
+
+ 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 + "=?";
+ m_findByUid = "SELECT * FROM " + m_userTable + " WHERE " + m_uid + "=?";
+ m_findByWikiName = "SELECT * FROM " + m_userTable + " WHERE " + m_wikiName + "=?";
- // Prepare the user isert/update SQL
- m_insertProfile = "INSERT INTO " + m_userTable + " ("
+ // The user insert SQL prepared statement
+ m_insertProfile = "INSERT INTO " + m_userTable + " ("
+ + m_uid + ","
+ m_email + ","
+ m_fullName + ","
+ m_password + ","
+ m_wikiName + ","
+ m_modified + ","
+ m_loginName + ","
+ + m_attributes + ","
+ m_created
- + ") VALUES (?,?,?,?,?,?,?)";
- m_updateProfile = "UPDATE " + m_userTable + " SET "
+ + ") VALUES (?,?,?,?,?,?,?,?,?)";
+
+ // The user update SQL prepared statement
+ m_updateProfile = "UPDATE " + m_userTable + " SET "
+ + m_uid + "=?,"
+ m_email + "=?,"
+ m_fullName + "=?,"
+ m_password + "=?,"
+ m_wikiName + "=?,"
- + m_modified + "=? WHERE " + m_loginName + "=?";
+ + m_modified + "=?,"
+ + m_loginName + "=?,"
+ + m_attributes + "=?,"
+ + m_lockExpiry + "=? "
+ + "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 + "=?";
+ 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 + "=?";
@@ -423,14 +516,9 @@
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" ) );
+ m_renameProfile = "UPDATE " + m_userTable + " SET " + m_loginName + "=?," + m_modified + "=? WHERE " + m_loginName
+ + "=?";
+ m_renameRoles = "UPDATE " + m_roleTable + " SET " + m_loginName + "=? WHERE " + m_loginName + "=?";
}
catch( NamingException e )
{
@@ -447,14 +535,20 @@
ps.executeQuery();
ps.close();
}
- catch ( SQLException e )
+ 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) {}
+ try
+ {
+ if( conn != null ) conn.close();
+ }
+ catch( Exception e )
+ {
+ }
}
log.info( "JDBCUserDatabase initialized from JNDI DataSource: " + jndiName );
@@ -463,40 +557,37 @@
{
conn = m_ds.getConnection();
DatabaseMetaData dmd = conn.getMetaData();
- if ( dmd.supportsTransactions() )
+ if( dmd.supportsTransactions() )
{
m_supportsCommits = true;
conn.setAutoCommit( false );
- log.info("JDBCUserDatabase supports transactions. Good; we will use them." );
+ log.info( "JDBCUserDatabase supports transactions. Good; we will use them." );
}
}
- catch ( SQLException e )
+ catch( SQLException e )
{
- log.warn("JDBCUserDatabase warning: user database doesn't seem to support transactions. Reason: " + e.getMessage() );
+ 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) {}
+ try
+ {
+ if( conn != null ) 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
+ public void rename( String loginName, String newName )
+ throws NoSuchPrincipalException,
+ DuplicateUserException,
+ WikiSecurityException
{
// Get the existing user; if not found, throws NoSuchPrincipalException
UserProfile profile = findByLoginName( loginName );
@@ -505,12 +596,12 @@
try
{
UserProfile otherProfile = findByLoginName( newName );
- if ( otherProfile != null )
+ if( otherProfile != null )
{
throw new DuplicateUserException( "Cannot rename: the login name '" + newName + "' is already taken." );
}
}
- catch ( NoSuchPrincipalException e )
+ catch( NoSuchPrincipalException e )
{
// Good! That means it's safe to save using the new name
}
@@ -520,7 +611,7 @@
{
// Open the database connection
conn = m_ds.getConnection();
- if ( m_supportsCommits )
+ if( m_supportsCommits )
{
conn.setAutoCommit( false );
}
@@ -548,18 +639,24 @@
profile.setLastModified( modDate );
// Commit and close connection
- if ( m_supportsCommits )
+ if( m_supportsCommits )
{
conn.commit();
}
}
- catch ( SQLException e )
+ catch( SQLException e )
{
throw new WikiSecurityException( e.getMessage() );
}
finally
{
- try { conn.close(); } catch (Exception e) {}
+ try
+ {
+ if( conn != null ) conn.close();
+ }
+ catch( Exception e )
+ {
+ }
}
}
@@ -576,28 +673,29 @@
{
existingProfile = findByLoginName( loginName );
}
- catch ( NoSuchPrincipalException e )
+ 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.
+ // 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 ) )
+ String existingPassword = (existingProfile == null) ? null : existingProfile.getPassword();
+ if( NOTHING.equals( password ) )
{
password = null;
}
- if ( password == null)
+ if( password == null )
{
password = existingPassword;
}
// If password changed, hash it before we save
- if ( !password.equals( existingPassword ) )
+ if( !password.equals( existingPassword ) )
{
- password = m_hashPrefix ? SHA_PREFIX + getHash( password ) : getHash( password );
+ password = getHash( password );
}
Connection conn = null;
@@ -605,47 +703,54 @@
{
// Open the database connection
conn = m_ds.getConnection();
- if ( m_supportsCommits )
+ if( m_supportsCommits )
{
conn.setAutoCommit( false );
}
Timestamp ts = new Timestamp( System.currentTimeMillis() );
Date modDate = new Date( ts.getTime() );
- if ( existingProfile == null )
+ java.sql.Date lockExpiry = profile.getLockExpiry() == null ? null : new java.sql.Date( profile.getLockExpiry().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.setLong( 1, profile.getUid() );
+ ps.setString( 2, profile.getEmail() );
+ ps.setString( 3, profile.getFullname() );
+ ps.setString( 4, password );
+ ps.setString( 5, profile.getWikiName() );
+ ps.setTimestamp( 6, ts );
+ ps.setString( 7, profile.getLoginName() );
+ try
+ {
+ ps.setString( 8, Serializer.serializeToBase64( profile.getAttributes() ) );
+ }
+ catch ( IOException e )
+ {
+ throw new WikiSecurityException( "Could not save user profile attribute. Reason: " + e.getMessage() );
+ }
+ ps.setTimestamp( 9, ts );
ps.execute();
ps.close();
- // Insert role record if no roles yet
- if ( m_sharedWithContainer )
+ // Insert new role record
+ ps = conn.prepareStatement( m_findRoles );
+ ps.setString( 1, profile.getLoginName() );
+ ResultSet rs = ps.executeQuery();
+ int roles = 0;
+ while ( rs.next() )
{
- ps = conn.prepareStatement( m_findRoles );
+ roles++;
+ }
+ ps.close();
+ if( roles == 0 )
+ {
+ ps = conn.prepareStatement( m_insertRole );
ps.setString( 1, profile.getLoginName() );
- ResultSet rs = ps.executeQuery();
- int roles = 0;
- while ( rs.next() )
- {
- roles++;
- }
+ ps.setString( 2, m_initialRole );
+ ps.execute();
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
@@ -655,12 +760,23 @@
{
// 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.setLong( 1, profile.getUid() );
+ ps.setString( 2, profile.getEmail() );
+ ps.setString( 3, profile.getFullname() );
+ ps.setString( 4, password );
+ ps.setString( 5, profile.getWikiName() );
+ ps.setTimestamp( 6, ts );
+ ps.setString( 7, profile.getLoginName() );
+ try
+ {
+ ps.setString( 8, Serializer.serializeToBase64( profile.getAttributes() ) );
+ }
+ catch ( IOException e )
+ {
+ throw new WikiSecurityException( "Could not save user profile attribute. Reason: " + e.getMessage() );
+ }
+ ps.setDate( 9, lockExpiry );
+ ps.setString( 10, profile.getLoginName() );
ps.execute();
ps.close();
}
@@ -668,28 +784,37 @@
profile.setLastModified( modDate );
// Commit and close connection
- if ( m_supportsCommits )
+ if( m_supportsCommits )
{
conn.commit();
}
}
- catch ( SQLException e )
+ catch( SQLException e )
{
throw new WikiSecurityException( e.getMessage() );
}
finally
{
- try { conn.close(); } catch (Exception e) {}
+ try
+ {
+ if( conn != null ) conn.close();
+ }
+ catch( Exception e )
+ {
+ }
}
}
/**
- *
- * @param rs
- * @return
+ * Private method that returns the first {@link UserProfile} matching a
+ * named column's value. This method will also set the UID if it has not yet been set.
+ * @param sql the SQL statement that should be prepared; it must have one parameter
+ * to set (either a String or a Long)
+ * @param index the value to match
+ * @return the resolved UserProfile
* @throws SQLException
*/
- private UserProfile findByPreparedStatement( String sql, String index ) throws NoSuchPrincipalException
+ private UserProfile findByPreparedStatement( String sql, Object index ) throws NoSuchPrincipalException
{
UserProfile profile = null;
boolean found = false;
@@ -699,48 +824,93 @@
{
// Open the database connection
conn = m_ds.getConnection();
- if ( m_supportsCommits )
+ if( m_supportsCommits )
{
conn.setAutoCommit( false );
}
PreparedStatement ps = conn.prepareStatement( sql );
- ps.setString( 1, index );
+
+ // Set the parameter to search by
+ if ( index instanceof String )
+ {
+ ps.setString( 1, (String)index );
+ }
+ else if ( index instanceof Long )
+ {
+ ps.setLong( 1, ( (Long)index).longValue() );
+ }
+ else
+ {
+ throw new IllegalArgumentException( "Index type not recognized!" );
+ }
+
+ // Go and get the record!
ResultSet rs = ps.executeQuery();
while ( rs.next() )
{
- if ( profile != null )
+ if( profile != null )
{
unique = false;
break;
}
- profile = new DefaultUserProfile();
+ profile = newProfile();
+
+ // Fetch the basic user attributes
+ profile.setUid( rs.getLong( m_uid ) );
+ if ( profile.getUid() == UID_NOT_SET )
+ {
+ profile.setUid( generateUid( this ) );
+ }
profile.setCreated( rs.getTimestamp( m_created ) );
profile.setEmail( rs.getString( m_email ) );
- profile.setFullname( rs.getString( m_fullName) );
+ profile.setFullname( rs.getString( m_fullName ) );
profile.setLastModified( rs.getTimestamp( m_modified ) );
- profile.setLoginName( rs.getString( m_loginName ) ) ;
+ Date lockExpiry = rs.getDate( m_lockExpiry );
+ profile.setLockExpiry( rs.wasNull() ? null : lockExpiry );
+ profile.setLoginName( rs.getString( m_loginName ) );
profile.setPassword( rs.getString( m_password ) );
+
+ // Fetch the user attributes
+ String rawAttributes = rs.getString( m_attributes );
+ if ( rawAttributes != null )
+ {
+ try
+ {
+ Map<String,? extends Serializable> attributes = Serializer.deserializeFromBase64( rawAttributes );
+ profile.getAttributes().putAll( attributes );
+ }
+ catch ( IOException e )
+ {
+ log.error( "Could not parse user profile attributes!", e );
+ }
+ }
found = true;
}
ps.close();
}
- catch ( SQLException e )
+ catch( SQLException e )
{
throw new NoSuchPrincipalException( e.getMessage() );
}
finally
{
- try { conn.close(); } catch (Exception e) {}
+ try
+ {
+ if( conn != null ) conn.close();
+ }
+ catch( Exception e )
+ {
+ }
}
- if ( !found )
+ if( !found )
{
- throw new NoSuchPrincipalException("Could not find profile in database!");
+ throw new NoSuchPrincipalException( "Could not find profile in database!" );
}
- if ( !unique )
+ if( !unique )
{
- throw new NoSuchPrincipalException("More than one profile in database!");
+ throw new NoSuchPrincipalException( "More than one profile in database!" );
}
return profile;
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserDatabase.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserDatabase.java?rev=682144&r1=682143&r2=682144&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserDatabase.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserDatabase.java Sun Aug 3 05:17:34 2008
@@ -1,21 +1,22 @@
-/*
- JSPWiki - a JSP-based WikiWiki clone.
+/*
+ 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
+ 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 com.ecyrd.jspwiki.auth.user;
@@ -126,6 +127,17 @@
/**
* Looks up and returns the first {@link UserProfile} in the user database
+ * that matches a profile having a given unique ID (uid). If the user database
+ * does not contain a user with a unique ID, it throws a
+ * {@link NoSuchPrincipalException}.
+ * @param uid the unique identifier of the desired user profile
+ * @return the user profile
+ * @since 2.8
+ */
+ public UserProfile findByUid( long uid ) 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}.
@@ -150,13 +162,6 @@
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>.
Modified: incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserProfile.java
URL: http://svn.apache.org/viewvc/incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserProfile.java?rev=682144&r1=682143&r2=682144&view=diff
==============================================================================
--- incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserProfile.java (original)
+++ incubator/jspwiki/branches/JSPWIKI_2_9_STRIPES_BRANCH/src/com/ecyrd/jspwiki/auth/user/UserProfile.java Sun Aug 3 05:17:34 2008
@@ -1,37 +1,51 @@
/*
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
+ 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 com.ecyrd.jspwiki.auth.user;
+import java.io.Serializable;
import java.util.Date;
+import java.util.Map;
/**
* 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.
+ * As of 2.8, user profiles can store custom key/value String/Serializable attributes, and store
+ * a unique ID. Locks are checked by {@link com.ecyrd.jspwiki.auth.AuthenticationManager};
+ * if a profile is locked, the user cannot log with that profile.
* @author Andrew Jaquith
* @since 2.3
*/
-public interface UserProfile
+public interface UserProfile extends Serializable
{
/**
+ * Returns the attributes associated with this profile as a Map of key/value pairs.
+ * The Map should generally be a "live" Map; changes to the keys or values will be reflected
+ * in the UserProfile.
+ * @return the attributes
+ */
+ public Map<String,Serializable> getAttributes();
+
+ /**
* Returns the creation date.
* @return the creation date
*/
@@ -56,6 +70,18 @@
public Date getLastModified();
/**
+ * Returns the date/time of expiration of the profile's lock, if it has been
+ * previously locked via {@link #setLockExpiry(Date)} and the lock is
+ * still active. If the profile is unlocked, this method returns <code>null</code>.
+ * Note that calling this method after the expiration date, <em>even if had previously
+ * been set explicitly by {@link #setLockExpiry(Date)}</em>, will always return
+ * <code>null</null>.
+ *
+ * @return the lock expiration date
+ */
+ public Date getLockExpiry();
+
+ /**
* Returns the user's login name.
* @return the login name
*/
@@ -73,6 +99,13 @@
public String getPassword();
/**
+ * Returns the unique identifier for the user profile. If not previously
+ * set, the value will be -1.
+ * @return the unique ID.
+ */
+ public long getUid();
+
+ /**
* Returns the user's wiki name, based on the full name with all
* whitespace removed.
* @return the wiki name.
@@ -80,6 +113,15 @@
public String getWikiName();
/**
+ * Returns
+ * <code>true</code> if the profile is currently locked (disabled); <code>false</code> otherwise.
+ * By default, profiles are created unlocked. Strictly speaking, calling this method is equivalent to calling {@link #getLockExpiry()}
+ * and, if it returns a non-<code>null</code> value, checking if the date returned is later than the current time.
+ * @return the result
+ */
+ public boolean isLocked();
+
+ /**
* 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.
@@ -112,6 +154,14 @@
public void setLastModified( Date date );
/**
+ * Locks the profile until a specified lock expiration date.
+ *
+ * @param expiry the date the lock expires; setting this value to <code>null</code>
+ * will cause the lock to be cleared.
+ */
+ public void setLockExpiry( Date expiry );
+
+ /**
* 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)},
@@ -135,6 +185,13 @@
public void setPassword( String arg );
/**
+ * Sets the unique identifier for the user profile. Note that UserDatabase implementations
+ * are required <em>not</em> to change the unique identifier after the initial save.
+ * @param uid the unique identifier to set
+ */
+ public void setUid( long uid );
+
+ /**
* 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.