/*==========================================================================*\
| $Id: DatabaseAuthenticator.java,v 1.4 2011/12/25 02:24:54 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2011 Virginia Tech
|
| This file is part of Web-CAT.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU Affero General Public License as published
| by the Free Software Foundation; either version 3 of the License, or
| (at your option) any later version.
|
| Web-CAT 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 General Public License for more details.
|
| You should have received a copy of the GNU Affero General Public License
| along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package org.webcat.core;
import com.webobjects.eoaccess.*;
import com.webobjects.eocontrol.*;
import com.webobjects.foundation.*;
import org.webcat.core.Application;
import org.webcat.core.AuthenticationDomain;
import org.webcat.core.DatabaseAuthenticator;
import org.webcat.core.User;
import org.webcat.core.UserAuthenticator;
import org.webcat.core.WCProperties;
import org.webcat.woextensions.WCEC;
import org.apache.log4j.Logger;
// -------------------------------------------------------------------------
/**
* A concrete implementation of <code>UserAuthenticator</code> that
* tests user ids/passwords against information stored in the database.
* This implementation tests the application property
* <code>authenticator.addIfNotFound</code>. If this property is
* true, user names that are not already in the database will be added
* as new users with the given password (i.e., automatic
* account creation for unknown user names). If the property is
* false or unset, then only users already in the database will be
* admitted.
*
* @author Stephen Edwards
* @author Last changed by $Author: stedwar2 $
* @version $Revision: 1.4 $, $Date: 2011/12/25 02:24:54 $
*/
public class DatabaseAuthenticator
implements UserAuthenticator
{
//~ Constructors ..........................................................
// ----------------------------------------------------------
/**
* Create a new <code>DatabaseAuthenticator</code> object.
*
*/
public DatabaseAuthenticator()
{
// Private data are initialized in their declarations
}
//~ Methods ...............................................................
// ----------------------------------------------------------
/**
* Initialize and configure the authenticator, reading subclass-specific
* settings from properties. The authenticator should read any
* instance-specific settings from properties named
* "baseName.<property>". This operation should only be called once,
* before any authenticate requests.
*
* @param baseName The base property name for this authenticator object
* @param properties The property collection from which the object
* should read its configuration settings
* @return true If configuration was successful and authenticator is
* ready for service
*/
public boolean configure( String baseName,
WCProperties properties
)
{
addIfNotFound = properties.booleanForKey( baseName + ".addIfNotFound" );
skipPasswordChecks =
properties.booleanForKey( baseName + ".skipPasswordChecks" );
log.debug( baseName + ".addIfNotFound = " + addIfNotFound );
if ( skipPasswordChecks )
{
log.warn(
baseName + ".skipPasswordChecks = " + skipPasswordChecks );
}
else
{
log.debug(
baseName + ".skipPasswordChecks = " + skipPasswordChecks );
}
return true;
}
// ----------------------------------------------------------
/**
* Validate the user `username' with the password `password'.
* Should not be called until the authenticator has been configured.
*
* @param userName The user id to validate
* @param password The password to check
* @param domain The authentication domain associated with this check
* @param ec The editing context to use
* @return The current user object, or null if invalid login
*/
public User authenticate( String userName,
String password,
AuthenticationDomain domain,
com.webobjects.eocontrol.EOEditingContext ec
)
{
User user = null;
try
{
User u = (User)EOUtilities.objectMatchingValues(
ec, User.ENTITY_NAME,
new NSDictionary<String, Object>(
new Object[]{ userName , domain },
new String[]{ User.USER_NAME_KEY,
User.AUTHENTICATION_DOMAIN_KEY }
) );
if (skipPasswordChecks
|| (password == null && u.password() == null)
|| (password != null && password.equals( u.password())))
{
log.debug( "user " + userName + " validated" );
user = u;
if ( user.authenticationDomain() != domain )
{
if ( user.authenticationDomain() == null )
{
user.setAuthenticationDomainRelationship( domain );
log.info( "user " + userName
+ " added to domain ("
+ domain.displayableName()
+ ")" );
}
else
{
log.warn(
"user " + userName
+ " successfully validated in '"
+ domain.displayableName()
+ "' but bound to '"
+ user.authenticationDomain().displayableName()
+ "'"
);
user = null;
}
}
}
else
{
log.info( "user " + userName + ": login validation failed" );
}
}
catch ( EOObjectNotAvailableException e )
{
if ( addIfNotFound )
{
user = User.createUser(
userName,
password,
domain,
User.STUDENT_PRIVILEGES,
ec
);
log.info( "DatabaseAuthenticator: new user '"
+ userName
+ "' created"
);
}
}
catch ( EOUtilities.MoreThanOneException e )
{
log.error( "DatabaseAuthenticator: user '"
+ userName
+ "':",
e
);
}
return user;
}
// ----------------------------------------------------------
/**
* Check whether users validated with this authenticator can
* change their password. For authentication mechanisms using
* external databases or servers where no changes are allowed, the
* authenticator should return false.
*
* @return True if users associated with this authenticator can
* change their password
*/
public boolean canChangePassword()
{
return true;
}
// ----------------------------------------------------------
/**
* Change the user's password. For authentication mechanisms using
* external databases or servers where no changes are allowed, an
* authenticator may simply return false for all requests.
*
* @param user The user
* @param newPassword The password to change to
* @return True if the password change was successful
*/
public boolean changePassword( User user,
String newPassword )
{
boolean result = false;
EOEditingContext ec = WCEC.newEditingContext();
try
{
ec.lock();
User localUser = user.localInstance(ec);
localUser.setPassword( newPassword );
ec.saveChanges();
result = true;
}
finally
{
ec.unlock();
ec.dispose();
}
return result;
}
// ----------------------------------------------------------
/**
* Generate a new random password.
* @return The new password
*/
public static String generatePassword()
{
StringBuffer password = new StringBuffer();
// generate a random number
for(int i = 0; i < DEFAULT_GENERATED_LENGTH; i++)
{
// now generate a random alpha-numeric for each position
// in the password
int index = randGen.nextInt( availChars.length() );
password.append( availChars.charAt( index ) );
}
return password.toString();
}
// ----------------------------------------------------------
/**
* Change the user's password to a new random password, and e-mail's
* the user their new password. For authentication mechanisms using
* external databases or servers where no changes are allowed, an
* authenticator may simply return false for all requests.
*
* @param user The user
* @return True if the password change was successful
*/
public boolean newRandomPassword( User user )
{
String newPass = generatePassword();
if ( changePassword( user, newPass ) )
{
WCProperties properties =
new WCProperties( Application.configurationProperties() );
user.addPropertiesTo( properties );
if ( properties.getProperty( "login.url" ) == null )
{
String dest = Application.application().servletConnectURL();
properties.setProperty( "login.url", dest );
}
Application.sendSimpleEmail(
user.email(),
properties.stringForKeyWithDefault(
"DatabaseAuthenticator.new.user.email.title",
"New Web-CAT Password" ),
properties.stringForKeyWithDefault(
"DatabaseAuthenticator.new.user.email.message",
"Your Web-CAT user name is : ${user.userName}\n"
+ "Your new Web-CAT password is: ${user.password}\n\n"
+ "You login to Web-CAT at:\n\n"
+ "${login.url}\n\n"
+ "You can change your password by logging into Web-CAT "
+ "and visiting\nthe Home->My Profile page."
)
);
return true;
}
else
{
return false;
}
}
//~ Instance/static variables .............................................
private boolean addIfNotFound = false;
private boolean skipPasswordChecks = false;
private static final java.util.Random randGen = new java.util.Random();
private static final int DEFAULT_GENERATED_LENGTH = 8;
private static final String availChars =
"ABCDEFGHJKLMNPQRSTUVWXYZabcdefghikmnopqrstuvwxyz23456789!@#$%^&*";
static Logger log = Logger.getLogger( DatabaseAuthenticator.class );
}