// ========================================================================
// $Id: HashUserRealm.java,v 1.29 2005/08/13 00:01:24 gregwilkins Exp $
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed 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 org.openqa.jetty.http;
import java.io.Externalizable;
import java.io.IOException;
import java.io.PrintStream;
import java.security.Principal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import org.apache.commons.logging.Log;
import org.openqa.jetty.log.LogFactory;
import org.openqa.jetty.util.Credential;
import org.openqa.jetty.util.Password;
import org.openqa.jetty.util.Resource;
/* ------------------------------------------------------------ */
/** HashMapped User Realm.
*
* An implementation of UserRealm that stores users and roles in-memory in
* HashMaps.
* <P>
* Typically these maps are populated by calling the load() method or passing
* a properties resource to the constructor. The format of the properties
* file is: <PRE>
* username: password [,rolename ...]
* </PRE>
* Passwords may be clear text, obfuscated or checksummed. The class
* com.mortbay.Util.Password should be used to generate obfuscated
* passwords or password checksums.
*
* If DIGEST Authentication is used, the password must be in a recoverable
* format, either plain text or OBF:.
*
* The HashUserRealm also implements SSORealm but provides no implementation
* of SSORealm. Instead setSSORealm may be used to provide a delegate
* SSORealm implementation.
*
* @see Password
* @version $Id: HashUserRealm.java,v 1.29 2005/08/13 00:01:24 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public class HashUserRealm
extends HashMap
implements UserRealm, SSORealm, Externalizable
{
private static Log log = LogFactory.getLog(HashUserRealm.class);
/** HttpContext Attribute to set to activate SSO.
*/
public static final String __SSO = "org.openqa.jetty.http.SSO";
/* ------------------------------------------------------------ */
private String _realmName;
private String _config;
protected HashMap _roles=new HashMap(7);
private SSORealm _ssoRealm;
/* ------------------------------------------------------------ */
/** Constructor.
*/
public HashUserRealm()
{}
/* ------------------------------------------------------------ */
/** Constructor.
* @param name Realm Name
*/
public HashUserRealm(String name)
{
_realmName=name;
}
/* ------------------------------------------------------------ */
/** Constructor.
* @param name Realm name
* @param config Filename or url of user properties file.
*/
public HashUserRealm(String name, String config)
throws IOException
{
_realmName=name;
load(config);
}
/* ------------------------------------------------------------ */
public void writeExternal(java.io.ObjectOutput out)
throws java.io.IOException
{
out.writeObject(_realmName);
out.writeObject(_config);
}
/* ------------------------------------------------------------ */
public void readExternal(java.io.ObjectInput in)
throws java.io.IOException, ClassNotFoundException
{
_realmName= (String)in.readObject();
_config=(String)in.readObject();
if (_config!=null)
load(_config);
}
/* ------------------------------------------------------------ */
/** Load realm users from properties file.
* The property file maps usernames to password specs followed by
* an optional comma separated list of role names.
*
* @param config Filename or url of user properties file.
* @exception IOException
*/
public void load(String config)
throws IOException
{
_config=config;
if(log.isDebugEnabled())log.debug("Load "+this+" from "+config);
Properties properties = new Properties();
Resource resource=Resource.newResource(config);
properties.load(resource.getInputStream());
Iterator iter = properties.entrySet().iterator();
while(iter.hasNext())
{
Map.Entry entry = (Map.Entry)iter.next();
String username=entry.getKey().toString().trim();
String credentials=entry.getValue().toString().trim();
String roles=null;
int c=credentials.indexOf(',');
if (c>0)
{
roles=credentials.substring(c+1).trim();
credentials=credentials.substring(0,c).trim();
}
if (username!=null && username.length()>0 &&
credentials!=null && credentials.length()>0)
{
put(username,credentials);
if(roles!=null && roles.length()>0)
{
StringTokenizer tok = new StringTokenizer(roles,", ");
while (tok.hasMoreTokens())
addUserToRole(username,tok.nextToken());
}
}
}
}
/* ------------------------------------------------------------ */
/**
* @param name The realm name
*/
public void setName(String name)
{
_realmName=name;
}
/* ------------------------------------------------------------ */
/**
* @return The realm name.
*/
public String getName()
{
return _realmName;
}
/* ------------------------------------------------------------ */
public Principal getPrincipal(String username)
{
return (Principal)super.get(username);
}
/* ------------------------------------------------------------ */
public Principal authenticate(String username,
Object credentials,
HttpRequest request)
{
KnownUser user;
synchronized (this)
{
user = (KnownUser)super.get(username);
}
if (user==null)
return null;
if (user.authenticate(credentials))
return user;
return null;
}
/* ------------------------------------------------------------ */
public void disassociate(Principal user)
{
}
/* ------------------------------------------------------------ */
public Principal pushRole(Principal user, String role)
{
if (user==null)
user=new User();
return new WrappedUser(user,role);
}
/* ------------------------------------------------------------ */
public Principal popRole(Principal user)
{
WrappedUser wu = (WrappedUser)user;
return wu.getUserPrincipal();
}
/* ------------------------------------------------------------ */
/** Put user into realm.
* @param name User name
* @param credentials String password, Password or UserPrinciple
* instance.
* @return Old UserPrinciple value or null
*/
public synchronized Object put(Object name, Object credentials)
{
if (credentials instanceof Principal)
return super.put(name.toString(),
credentials);
if (credentials instanceof Password)
return super.put(name,
new KnownUser(name.toString(),
(Password)credentials));
if (credentials != null)
return super
.put(name,
new KnownUser(name.toString(),
Credential.getCredential(credentials.toString())));
return null;
}
/* ------------------------------------------------------------ */
/** Add a user to a role.
* @param userName
* @param roleName
*/
public synchronized void addUserToRole(String userName, String roleName)
{
HashSet userSet = (HashSet)_roles.get(roleName);
if (userSet==null)
{
userSet=new HashSet(11);
_roles.put(roleName,userSet);
}
userSet.add(userName);
}
/* -------------------------------------------------------- */
public boolean reauthenticate(Principal user)
{
return ((User)user).isAuthenticated();
}
/* ------------------------------------------------------------ */
/** Check if a user is in a role.
* @param user The user, which must be from this realm
* @param roleName
* @return True if the user can act in the role.
*/
public synchronized boolean isUserInRole(Principal user, String roleName)
{
if (user instanceof WrappedUser)
return ((WrappedUser)user).isUserInRole(roleName);
if (user==null || ((User)user).getUserRealm()!=this)
return false;
HashSet userSet = (HashSet)_roles.get(roleName);
return userSet!=null && userSet.contains(user.getName());
}
/* ------------------------------------------------------------ */
public void logout(Principal user)
{}
/* ------------------------------------------------------------ */
public String toString()
{
return "Realm["+_realmName+"]";
}
/* ------------------------------------------------------------ */
public void dump(PrintStream out)
{
out.println(this+":");
out.println(super.toString());
out.println(_roles);
}
/* ------------------------------------------------------------ */
/**
* @return The SSORealm to delegate single sign on requests to.
*/
public SSORealm getSSORealm()
{
return _ssoRealm;
}
/* ------------------------------------------------------------ */
/** Set the SSORealm.
* A SSORealm implementation may be set to enable support for SSO.
* @param ssoRealm The SSORealm to delegate single sign on requests to.
*/
public void setSSORealm(SSORealm ssoRealm)
{
_ssoRealm = ssoRealm;
}
/* ------------------------------------------------------------ */
public Credential getSingleSignOn(HttpRequest request,
HttpResponse response)
{
if (_ssoRealm!=null)
return _ssoRealm.getSingleSignOn(request,response);
return null;
}
/* ------------------------------------------------------------ */
public void setSingleSignOn(HttpRequest request,
HttpResponse response,
Principal principal,
Credential credential)
{
if (_ssoRealm!=null)
_ssoRealm.setSingleSignOn(request,response,principal,credential);
}
/* ------------------------------------------------------------ */
public void clearSingleSignOn(String username)
{
if (_ssoRealm!=null)
_ssoRealm.clearSingleSignOn(username);
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class User implements Principal
{
List roles=null;
/* ------------------------------------------------------------ */
private UserRealm getUserRealm()
{
return HashUserRealm.this;
}
public String getName()
{
return "Anonymous";
}
public boolean isAuthenticated()
{
return false;
}
public String toString()
{
return getName();
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class KnownUser extends User
{
private String _userName;
private Credential _cred;
/* -------------------------------------------------------- */
KnownUser(String name,Credential credential)
{
_userName=name;
_cred=credential;
}
/* -------------------------------------------------------- */
boolean authenticate(Object credentials)
{
return _cred!=null && _cred.check(credentials);
}
/* ------------------------------------------------------------ */
public String getName()
{
return _userName;
}
/* -------------------------------------------------------- */
public boolean isAuthenticated()
{
return true;
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class WrappedUser extends User
{
private Principal user;
private String role;
WrappedUser(Principal user, String role)
{
this.user=user;
this.role=role;
}
Principal getUserPrincipal()
{
return user;
}
public String getName()
{
return "role:"+role;
}
public boolean isAuthenticated()
{
return true;
}
public boolean isUserInRole(String role)
{
return this.role.equals(role);
}
}
}