483059 Remove cache of authenticated users

This commit is contained in:
Jan Bartel 2015-11-26 15:30:50 +11:00
parent 18a3af1951
commit 81b2a6a4de
22 changed files with 629 additions and 709 deletions

View File

@ -289,6 +289,7 @@ public abstract class AbstractLoginModule implements LoginModule
public boolean logout() throws LoginException
{
this.currentUser.unsetJAASInfo(this.subject);
this.currentUser = null;
return true;
}

View File

@ -48,6 +48,8 @@ public class PropertyFileLoginModule extends AbstractLoginModule
private int _refreshInterval = 0;
private String _filename = DEFAULT_FILENAME;
/**
* Read contents of the configured property file.
*
@ -73,7 +75,6 @@ public class PropertyFileLoginModule extends AbstractLoginModule
{
PropertyUserStore propertyUserStore = new PropertyUserStore();
propertyUserStore.setConfig(_filename);
propertyUserStore.setRefreshInterval(_refreshInterval);
PropertyUserStore prev = _propertyUserStores.putIfAbsent(_filename, propertyUserStore);
if (prev == null)

View File

@ -22,14 +22,16 @@ import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.security.AbstractLoginService;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
@ -38,6 +40,7 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Credential;
import org.eclipse.jetty.util.security.Password;
import org.hamcrest.Matchers;
import org.junit.After;
@ -48,6 +51,43 @@ public class JaspiTest
{
Server _server;
LocalConnector _connector;
public class TestLoginService extends AbstractLoginService
{
protected Map<String, UserPrincipal> _users = new HashMap<>();
protected Map<String, String[]> _roles = new HashMap();
public TestLoginService(String name)
{
setName(name);
}
public void putUser (String username, Credential credential, String[] roles)
{
UserPrincipal userPrincipal = new UserPrincipal(username,credential);
_users.put(username, userPrincipal);
_roles.put(username, roles);
}
/**
* @see org.eclipse.jetty.security.AbstractLoginService#loadRoleInfo(org.eclipse.jetty.security.AbstractLoginService.UserPrincipal)
*/
@Override
protected String[] loadRoleInfo(UserPrincipal user)
{
return _roles.get(user.getName());
}
/**
* @see org.eclipse.jetty.security.AbstractLoginService#loadUserInfo(java.lang.String)
*/
@Override
protected UserPrincipal loadUserInfo(String username)
{
return _users.get(username);
}
}
@Before
public void before() throws Exception
@ -60,7 +100,7 @@ public class JaspiTest
ContextHandlerCollection contexts = new ContextHandlerCollection();
_server.setHandler(contexts);
HashLoginService loginService = new HashLoginService("TestRealm");
TestLoginService loginService = new TestLoginService("TestRealm");
loginService.putUser("user",new Password("password"),new String[]{"users"});
loginService.putUser("admin",new Password("secret"),new String[]{"users","admins"});
_server.addBean(loginService);

View File

@ -13,7 +13,6 @@
<New class="org.eclipse.jetty.security.HashLoginService">
<Set name="name">Test Realm</Set>
<Set name="config"><Property name="jetty.home" default="src/test/config"/>realm.properties</Set>
<Set name="refreshInterval">0</Set>
</New>
</Arg>
</Call>

View File

@ -32,14 +32,12 @@ import java.util.Locale;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.servlet.ServletRequest;
import javax.sql.DataSource;
import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
import org.eclipse.jetty.security.AbstractLoginService;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.MappedLoginService;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Credential;
@ -51,7 +49,7 @@ import org.eclipse.jetty.util.security.Credential;
* Obtain user/password/role information from a database
* via jndi DataSource.
*/
public class DataSourceLoginService extends MappedLoginService
public class DataSourceLoginService extends AbstractLoginService
{
private static final Logger LOG = Log.getLogger(DataSourceLoginService.class);
@ -68,8 +66,6 @@ public class DataSourceLoginService extends MappedLoginService
private String _userRoleTableName = "user_roles";
private String _userRoleTableUserKey = "user_id";
private String _userRoleTableRoleKey = "role_id";
private int _cacheMs = 30000;
private long _lastPurge = 0;
private String _userSql;
private String _roleSql;
private boolean _createTables = false;
@ -80,7 +76,7 @@ public class DataSourceLoginService extends MappedLoginService
*
*
*/
public class DBUser extends KnownUser
public class DBUserPrincipal extends UserPrincipal
{
private int _key;
@ -88,7 +84,7 @@ public class DataSourceLoginService extends MappedLoginService
* @param name
* @param credential
*/
public DBUser(String name, Credential credential, int key)
public DBUserPrincipal(String name, Credential credential, int key)
{
super(name, credential);
_key = key;
@ -292,81 +288,14 @@ public class DataSourceLoginService extends MappedLoginService
_userRoleTableRoleKey = roleTableRoleKey;
}
/* ------------------------------------------------------------ */
public void setCacheMs (int ms)
{
_cacheMs=ms;
}
/* ------------------------------------------------------------ */
public int getCacheMs ()
{
return _cacheMs;
}
/* ------------------------------------------------------------ */
@Override
protected void loadUsers()
{
}
/* ------------------------------------------------------------ */
/** Load user's info from database.
*
* @param userName the user name
*/
@Deprecated
protected UserIdentity loadUser (String userName)
{
try
{
try (Connection connection = getConnection();
PreparedStatement statement1 = connection.prepareStatement(_userSql))
{
statement1.setObject(1, userName);
try (ResultSet rs1 = statement1.executeQuery())
{
if (rs1.next())
{
int key = rs1.getInt(_userTableKey);
String credentials = rs1.getString(_userTablePasswordField);
List<String> roles = new ArrayList<String>();
try (PreparedStatement statement2 = connection.prepareStatement(_roleSql))
{
statement2.setInt(1, key);
try (ResultSet rs2 = statement2.executeQuery())
{
while (rs2.next())
{
roles.add(rs2.getString(_roleTableRoleField));
}
}
}
return putUser(userName, Credential.getCredential(credentials), roles.toArray(new String[roles.size()]));
}
}
}
}
catch (NamingException e)
{
LOG.warn("No datasource for "+_jndiName, e);
}
catch (SQLException e)
{
LOG.warn("Problem loading user info for "+userName, e);
}
return null;
}
/**
* @see org.eclipse.jetty.security.MappedLoginService#loadUserInfo(java.lang.String)
* @Override
*/
public KnownUser loadUserInfo (String username)
public UserPrincipal loadUserInfo (String username)
{
try
{
@ -381,7 +310,7 @@ public class DataSourceLoginService extends MappedLoginService
int key = rs1.getInt(_userTableKey);
String credentials = rs1.getString(_userTablePasswordField);
return new DBUser(username, Credential.getCredential(credentials), key);
return new DBUserPrincipal(username, Credential.getCredential(credentials), key);
}
}
}
@ -397,13 +326,15 @@ public class DataSourceLoginService extends MappedLoginService
return null;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.MappedLoginService#loadRoleInfo(org.eclipse.jetty.security.MappedLoginService.KnownUser)
* @see org.eclipse.jetty.security.MappedLoginService#loadRoleInfo(org.eclipse.jetty.security.UserPrincipal.KnownUser)
* @Override
*/
public String[] loadRoleInfo (KnownUser user)
public String[] loadRoleInfo (UserPrincipal user)
{
DBUser dbuser = (DBUser)user;
DBUserPrincipal dbuser = (DBUserPrincipal)user;
try
{
@ -440,19 +371,7 @@ public class DataSourceLoginService extends MappedLoginService
/* ------------------------------------------------------------ */
@Override
public UserIdentity login(String username, Object credentials, ServletRequest request)
{
long now = System.currentTimeMillis();
if (now - _lastPurge > _cacheMs || _cacheMs == 0)
{
_users.clear();
_lastPurge = now;
}
return super.login(username,credentials, request);
}
/* ------------------------------------------------------------ */
/**
@ -509,6 +428,11 @@ public class DataSourceLoginService extends MappedLoginService
/* ------------------------------------------------------------ */
/**
* @throws NamingException
* @throws SQLException
*/
private void prepareTables()
throws NamingException, SQLException
{
@ -610,6 +534,12 @@ public class DataSourceLoginService extends MappedLoginService
}
/* ------------------------------------------------------------ */
/**
* @return
* @throws NamingException
* @throws SQLException
*/
private Connection getConnection ()
throws NamingException, SQLException
{

View File

@ -240,7 +240,6 @@
<New class="org.eclipse.jetty.security.jaspi.modules.HashLoginService">
<Set name="name">Test Realm</Set>
<Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
<Set name="refreshInterval">0</Set>
</New>
</Item>
</Array>

View File

@ -0,0 +1,270 @@
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.security;
import java.io.Serializable;
import java.security.Principal;
import javax.security.auth.Subject;
import javax.servlet.ServletRequest;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Credential;
/**
* AbstractLoginService
*
*
*/
public abstract class AbstractLoginService extends AbstractLifeCycle implements LoginService
{
private static final Logger LOG = Log.getLogger(AbstractLoginService.class);
protected IdentityService _identityService=new DefaultIdentityService();
protected String _name;
protected boolean _fullValidate = false;
/* ------------------------------------------------------------ */
/**
* RolePrincipal
*
*
*/
public static class RolePrincipal implements Principal,Serializable
{
private static final long serialVersionUID = 2998397924051854402L;
private final String _roleName;
public RolePrincipal(String name)
{
_roleName=name;
}
public String getName()
{
return _roleName;
}
}
/* ------------------------------------------------------------ */
/**
* UserPrincipal
*
*
*/
public static class UserPrincipal implements Principal,Serializable
{
private static final long serialVersionUID = -6226920753748399662L;
private final String _name;
private final Credential _credential;
/* -------------------------------------------------------- */
public UserPrincipal(String name,Credential credential)
{
_name=name;
_credential=credential;
}
/* -------------------------------------------------------- */
public boolean authenticate(Object credentials)
{
return _credential!=null && _credential.check(credentials);
}
/* -------------------------------------------------------- */
public boolean authenticate (Credential c)
{
return(_credential != null && c != null && _credential.equals(c));
}
/* ------------------------------------------------------------ */
public String getName()
{
return _name;
}
/* -------------------------------------------------------- */
@Override
public String toString()
{
return _name;
}
}
/* ------------------------------------------------------------ */
protected abstract String[] loadRoleInfo (UserPrincipal user);
/* ------------------------------------------------------------ */
protected abstract UserPrincipal loadUserInfo (String username);
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.LoginService#getName()
*/
@Override
public String getName()
{
return _name;
}
/* ------------------------------------------------------------ */
/** Set the identityService.
* @param identityService the identityService to set
*/
public void setIdentityService(IdentityService identityService)
{
if (isRunning())
throw new IllegalStateException("Running");
_identityService = identityService;
}
/* ------------------------------------------------------------ */
/** Set the name.
* @param name the name to set
*/
public void setName(String name)
{
if (isRunning())
throw new IllegalStateException("Running");
_name = name;
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return this.getClass().getSimpleName()+"["+_name+"]";
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.LoginService#login(java.lang.String, java.lang.Object, javax.servlet.ServletRequest)
*/
@Override
public UserIdentity login(String username, Object credentials, ServletRequest request)
{
if (username == null)
return null;
UserPrincipal userPrincipal = loadUserInfo(username);
if (userPrincipal.authenticate(credentials))
{
//safe to load the roles
String[] roles = loadRoleInfo(userPrincipal);
Subject subject = new Subject();
subject.getPrincipals().add(userPrincipal);
subject.getPrivateCredentials().add(userPrincipal._credential);
if (roles!=null)
for (String role : roles)
subject.getPrincipals().add(new RolePrincipal(role));
subject.setReadOnly();
return _identityService.newUserIdentity(subject,userPrincipal,roles);
}
return null;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.LoginService#validate(org.eclipse.jetty.server.UserIdentity)
*/
@Override
public boolean validate(UserIdentity user)
{
if (!isFullValidate())
return true; //if we have a user identity it must be valid
//Do a full validation back against the user store
UserPrincipal fresh = loadUserInfo(user.getUserPrincipal().getName());
if (fresh == null)
return false; //user no longer exists
if (user.getUserPrincipal() instanceof UserPrincipal)
{
System.err.println("VALIDATING user "+fresh.getName());
return fresh.authenticate(((UserPrincipal)user.getUserPrincipal())._credential);
}
throw new IllegalStateException("UserPrincipal not KnownUser"); //can't validate
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.LoginService#getIdentityService()
*/
@Override
public IdentityService getIdentityService()
{
return _identityService;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.LoginService#logout(org.eclipse.jetty.server.UserIdentity)
*/
@Override
public void logout(UserIdentity user)
{
//Override in subclasses
}
/* ------------------------------------------------------------ */
/**
* @return
*/
public boolean isFullValidate()
{
return _fullValidate;
}
/* ------------------------------------------------------------ */
/**
* @param fullValidate
*/
public void setFullValidate(boolean fullValidate)
{
_fullValidate = fullValidate;
}
}

View File

@ -23,7 +23,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.security.MappedLoginService.KnownUser;
import org.eclipse.jetty.security.PropertyUserStore.UserListener;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.Scanner;
@ -49,45 +48,17 @@ import org.eclipse.jetty.util.security.Credential;
* <p>
* If DIGEST Authentication is used, the password must be in a recoverable format, either plain text or OBF:.
*/
public class HashLoginService extends MappedLoginService implements UserListener
public class HashLoginService extends AbstractLoginService
{
private static final Logger LOG = Log.getLogger(HashLoginService.class);
private PropertyUserStore _propertyUserStore;
private String _config;
private Resource _configResource;
private Scanner _scanner;
private boolean hotReload = false; // default is not to reload
protected PropertyUserStore _propertyUserStore;
protected String _config;
protected Resource _configResource;
protected boolean hotReload = false; // default is not to reload
public class HashKnownUser extends KnownUser
{
String[] _roles;
/**
* @param name
* @param credential
*/
public HashKnownUser(String name, Credential credential)
{
super(name, credential);
}
public void setRoles (String[] roles)
{
_roles = roles;
}
public String[] getRoles()
{
return _roles;
}
}
/* ------------------------------------------------------------ */
public HashLoginService()
@ -161,46 +132,11 @@ public class HashLoginService extends MappedLoginService implements UserListener
this.hotReload = enable;
}
/* ------------------------------------------------------------ */
/**
* sets the refresh interval (in seconds)
* @param sec the refresh interval
* @deprecated use {@link #setHotReload(boolean)} instead
*/
@Deprecated
public void setRefreshInterval(int sec)
{
}
/* ------------------------------------------------------------ */
/**
* @return refresh interval in seconds for how often the properties file should be checked for changes
* @deprecated use {@link #isHotReload()} instead
*/
@Deprecated
public int getRefreshInterval()
{
return (hotReload)?1:0;
}
/* ------------------------------------------------------------ */
@Override
protected UserIdentity loadUser(String username)
{
return null;
}
/* ------------------------------------------------------------ */
@Override
public void loadUsers() throws IOException
{
// TODO: Consider refactoring MappedLoginService to not have to override with unused methods
}
@Override
protected String[] loadRoleInfo(KnownUser user)
protected String[] loadRoleInfo(UserPrincipal user)
{
UserIdentity id = _propertyUserStore.getUserIdentity(user.getName());
if (id == null)
@ -218,13 +154,17 @@ public class HashLoginService extends MappedLoginService implements UserListener
return list.toArray(new String[roles.size()]);
}
/* ------------------------------------------------------------ */
@Override
protected KnownUser loadUserInfo(String userName)
protected UserPrincipal loadUserInfo(String userName)
{
UserIdentity id = _propertyUserStore.getUserIdentity(userName);
if (id != null)
{
return (KnownUser)id.getUserPrincipal();
return (UserPrincipal)id.getUserPrincipal();
}
return null;
@ -249,7 +189,6 @@ public class HashLoginService extends MappedLoginService implements UserListener
_propertyUserStore = new PropertyUserStore();
_propertyUserStore.setHotReload(hotReload);
_propertyUserStore.setConfigPath(_config);
_propertyUserStore.registerUserListener(this);
_propertyUserStore.start();
}
}
@ -262,28 +201,5 @@ public class HashLoginService extends MappedLoginService implements UserListener
protected void doStop() throws Exception
{
super.doStop();
if (_scanner != null)
_scanner.stop();
_scanner = null;
}
/* ------------------------------------------------------------ */
@Override
public void update(String userName, Credential credential, String[] roleArray)
{
if (LOG.isDebugEnabled())
LOG.debug("update: " + userName + " Roles: " + roleArray.length);
//TODO need to remove and replace the authenticated user?
}
/* ------------------------------------------------------------ */
@Override
public void remove(String userName)
{
if (LOG.isDebugEnabled())
LOG.debug("remove: " + userName);
removeUser(userName);
}
}

View File

@ -59,7 +59,7 @@ import org.eclipse.jetty.util.security.Credential;
*
*/
public class JDBCLoginService extends MappedLoginService
public class JDBCLoginService extends AbstractLoginService
{
private static final Logger LOG = Log.getLogger(JDBCLoginService.class);
@ -71,8 +71,6 @@ public class JDBCLoginService extends MappedLoginService
protected String _userTableKey;
protected String _userTablePasswordField;
protected String _roleTableRoleField;
protected int _cacheTime;
protected long _lastHashPurge;
protected Connection _con;
protected String _userSql;
protected String _roleSql;
@ -83,7 +81,7 @@ public class JDBCLoginService extends MappedLoginService
*
*
*/
public class JDBCKnownUser extends KnownUser
public class JDBCUserPrincipal extends UserPrincipal
{
int _userKey;
@ -91,7 +89,7 @@ public class JDBCLoginService extends MappedLoginService
* @param name
* @param credential
*/
public JDBCKnownUser(String name, Credential credential, int key)
public JDBCUserPrincipal(String name, Credential credential, int key)
{
super(name, credential);
_userKey = key;
@ -162,20 +160,18 @@ public class JDBCLoginService extends MappedLoginService
String _userRoleTable = properties.getProperty("userroletable");
String _userRoleTableUserKey = properties.getProperty("userroletableuserkey");
String _userRoleTableRoleKey = properties.getProperty("userroletablerolekey");
_cacheTime = new Integer(properties.getProperty("cachetime"));
if (_jdbcDriver == null || _jdbcDriver.equals("")
|| _url == null
|| _url.equals("")
|| _userName == null
|| _userName.equals("")
|| _password == null
|| _cacheTime < 0)
|| _password == null)
{
LOG.warn("UserRealm " + getName() + " has not been properly configured");
}
_cacheTime *= 1000;
_lastHashPurge = 0;
_userSql = "select " + _userTableKey + "," + _userTablePasswordField + " from " + _userTable + " where " + _userTableUserField + " = ?";
_roleSql = "select r." + _roleTableRoleField
+ " from "
@ -235,80 +231,15 @@ public class JDBCLoginService extends MappedLoginService
}
}
/* ------------------------------------------------------------ */
@Override
public UserIdentity login(String username, Object credentials, ServletRequest request)
{
long now = System.currentTimeMillis();
if (now - _lastHashPurge > _cacheTime || _cacheTime == 0)
{
_users.clear();
_lastHashPurge = now;
closeConnection();
}
return super.login(username,credentials, request);
}
/* ------------------------------------------------------------ */
@Override
protected void loadUsers()
{
}
/* ------------------------------------------------------------ */
@Deprecated
protected UserIdentity loadUser(String username)
{
try
{
if (null == _con)
connectDatabase();
if (null == _con)
throw new SQLException("Can't connect to database");
try (PreparedStatement stat1 = _con.prepareStatement(_userSql))
{
stat1.setObject(1, username);
try (ResultSet rs1 = stat1.executeQuery())
{
if (rs1.next())
{
int key = rs1.getInt(_userTableKey);
String credentials = rs1.getString(_userTablePasswordField);
List<String> roles = new ArrayList<String>();
try (PreparedStatement stat2 = _con.prepareStatement(_roleSql))
{
stat2.setInt(1, key);
try (ResultSet rs2 = stat2.executeQuery())
{
while (rs2.next())
roles.add(rs2.getString(_roleTableRoleField));
}
}
return putUser(username, Credential.getCredential(credentials), roles.toArray(new String[roles.size()]));
}
}
}
}
catch (SQLException e)
{
LOG.warn("UserRealm " + getName() + " could not load user information from database", e);
closeConnection();
}
return null;
}
/**
* @see org.eclipse.jetty.security.MappedLoginService#loadUserInfo(java.lang.String)
* @Override
*/
public KnownUser loadUserInfo (String username)
public UserPrincipal loadUserInfo (String username)
{
try
{
@ -328,7 +259,7 @@ public class JDBCLoginService extends MappedLoginService
int key = rs1.getInt(_userTableKey);
String credentials = rs1.getString(_userTablePasswordField);
return new JDBCKnownUser (username, Credential.getCredential(credentials), key);
return new JDBCUserPrincipal (username, Credential.getCredential(credentials), key);
}
}
}
@ -343,14 +274,14 @@ public class JDBCLoginService extends MappedLoginService
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.MappedLoginService#loadRoleInfo(org.eclipse.jetty.security.MappedLoginService.KnownUser)
* @see org.eclipse.jetty.security.MappedLoginService#loadRoleInfo(org.eclipse.jetty.security.UserPrincipal.KnownUser)
* @Override
*/
public String[] loadRoleInfo (KnownUser user)
public String[] loadRoleInfo (UserPrincipal user)
{
JDBCKnownUser jdbcUser = (JDBCKnownUser)user;
JDBCUserPrincipal jdbcUser = (JDBCUserPrincipal)user;
try
{
@ -386,6 +317,18 @@ public class JDBCLoginService extends MappedLoginService
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
*/
@Override
protected void doStop() throws Exception
{
closeConnection();
super.doStop();
}
/* ------------------------------------------------------------ */
/**
* Close an existing connection
*/

View File

@ -1,375 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.security;
import java.io.IOException;
import java.io.Serializable;
import java.security.Principal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.security.auth.Subject;
import javax.servlet.ServletRequest;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Credential;
/* ------------------------------------------------------------ */
/**
* A login service that keeps UserIdentities in a concurrent map
* either as the source or a cache of the users.
*
*/
public abstract class MappedLoginService extends AbstractLifeCycle implements LoginService
{
private static final Logger LOG = Log.getLogger(MappedLoginService.class);
protected IdentityService _identityService=new DefaultIdentityService();
protected String _name;
protected final ConcurrentMap<String, UserIdentity> _users=new ConcurrentHashMap<String, UserIdentity>();
/* ------------------------------------------------------------ */
protected MappedLoginService()
{
}
/* ------------------------------------------------------------ */
/** Get the name.
* @return the name
*/
public String getName()
{
return _name;
}
/* ------------------------------------------------------------ */
/** Get the identityService.
* @return the identityService
*/
public IdentityService getIdentityService()
{
return _identityService;
}
/* ------------------------------------------------------------ */
/** Get the users.
* @return the users
*/
public ConcurrentMap<String, UserIdentity> getUsers()
{
return _users;
}
/* ------------------------------------------------------------ */
/** Set the identityService.
* @param identityService the identityService to set
*/
public void setIdentityService(IdentityService identityService)
{
if (isRunning())
throw new IllegalStateException("Running");
_identityService = identityService;
}
/* ------------------------------------------------------------ */
/** Set the name.
* @param name the name to set
*/
public void setName(String name)
{
if (isRunning())
throw new IllegalStateException("Running");
_name = name;
}
/* ------------------------------------------------------------ */
/** Set the users.
* @param users the users to set
*/
public void setUsers(Map<String, UserIdentity> users)
{
if (isRunning())
throw new IllegalStateException("Running");
_users.clear();
_users.putAll(users);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
*/
@Override
protected void doStart() throws Exception
{
loadUsers();
super.doStart();
}
/* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
super.doStop();
}
/* ------------------------------------------------------------ */
public void logout(UserIdentity identity)
{
LOG.debug("logout {}",identity);
//TODO should remove the user?????
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return this.getClass().getSimpleName()+"["+_name+"]";
}
/* ------------------------------------------------------------ */
/** Put user into realm.
* Called by implementations to put the user data loaded from
* file/db etc into the user structure.
* @param userName User name
* @param info a UserIdentity instance, or a String password or Credential instance
* @return User instance
*/
protected synchronized UserIdentity putUser(String userName, Object info)
{
final UserIdentity identity;
if (info instanceof UserIdentity)
identity=(UserIdentity)info;
else
{
Credential credential = (info instanceof Credential)?(Credential)info:Credential.getCredential(info.toString());
Principal userPrincipal = new KnownUser(userName,credential);
Subject subject = new Subject();
subject.getPrincipals().add(userPrincipal);
subject.getPrivateCredentials().add(credential);
subject.setReadOnly();
identity=_identityService.newUserIdentity(subject,userPrincipal,IdentityService.NO_ROLES);
}
_users.put(userName,identity);
return identity;
}
/* ------------------------------------------------------------ */
/** Put user into realm.
* @param userName The user to add
* @param credential The users Credentials
* @param roles The users roles
* @return UserIdentity
*/
public synchronized UserIdentity putUser(String userName, Credential credential, String[] roles)
{
Principal userPrincipal = new KnownUser(userName,credential);
Subject subject = new Subject();
subject.getPrincipals().add(userPrincipal);
subject.getPrivateCredentials().add(credential);
if (roles!=null)
for (String role : roles)
subject.getPrincipals().add(new RolePrincipal(role));
subject.setReadOnly();
UserIdentity identity=_identityService.newUserIdentity(subject,userPrincipal,roles);
_users.put(userName,identity);
return identity;
}
public synchronized UserIdentity putUser (KnownUser userPrincipal, String[] roles)
{
Subject subject = new Subject();
subject.getPrincipals().add(userPrincipal);
subject.getPrivateCredentials().add(userPrincipal._credential);
if (roles!=null)
for (String role : roles)
subject.getPrincipals().add(new RolePrincipal(role));
subject.setReadOnly();
UserIdentity identity=_identityService.newUserIdentity(subject,userPrincipal,roles);
_users.put(userPrincipal._name,identity);
return identity;
}
/* ------------------------------------------------------------ */
public void removeUser(String username)
{
_users.remove(username);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.security.LoginService#login(java.lang.String, java.lang.Object, ServletRequest)
*/
public UserIdentity login(String username, Object credentials, ServletRequest request)
{
if (username == null)
return null;
UserIdentity user = _users.get(username);
if (user==null)
{
KnownUser userPrincipal = loadUserInfo(username);
if (userPrincipal.authenticate(credentials))
{
//safe to load the roles
String[] roles = loadRoleInfo(userPrincipal);
user = putUser(userPrincipal, roles);
return user;
}
}
else
{
UserPrincipal principal = (UserPrincipal)user.getUserPrincipal();
if (principal.authenticate(credentials))
return user;
}
return null;
}
/* ------------------------------------------------------------ */
public boolean validate(UserIdentity user)
{
if (_users.containsKey(user.getUserPrincipal().getName()))
return true;
if (loadUser(user.getUserPrincipal().getName())!=null)
return true;
return false;
}
/* ------------------------------------------------------------ */
protected abstract String[] loadRoleInfo (KnownUser user);
/* ------------------------------------------------------------ */
protected abstract KnownUser loadUserInfo (String username);
/* ------------------------------------------------------------ */
protected abstract UserIdentity loadUser(String username);
/* ------------------------------------------------------------ */
protected abstract void loadUsers() throws IOException;
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public interface UserPrincipal extends Principal,Serializable
{
boolean authenticate(Object credentials);
public boolean isAuthenticated();
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public static class RolePrincipal implements Principal,Serializable
{
private static final long serialVersionUID = 2998397924051854402L;
private final String _roleName;
public RolePrincipal(String name)
{
_roleName=name;
}
public String getName()
{
return _roleName;
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public static class Anonymous implements UserPrincipal,Serializable
{
private static final long serialVersionUID = 1097640442553284845L;
public boolean isAuthenticated()
{
return false;
}
public String getName()
{
return "Anonymous";
}
public boolean authenticate(Object credentials)
{
return false;
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public static class KnownUser implements UserPrincipal,Serializable
{
private static final long serialVersionUID = -6226920753748399662L;
private final String _name;
private final Credential _credential;
/* -------------------------------------------------------- */
public KnownUser(String name,Credential credential)
{
_name=name;
_credential=credential;
}
/* -------------------------------------------------------- */
public boolean authenticate(Object credentials)
{
return _credential!=null && _credential.check(credentials);
}
/* ------------------------------------------------------------ */
public String getName()
{
return _name;
}
/* -------------------------------------------------------- */
public boolean isAuthenticated()
{
return true;
}
/* -------------------------------------------------------- */
@Override
public String toString()
{
return _name;
}
}
}

View File

@ -33,8 +33,7 @@ import java.util.Set;
import javax.security.auth.Subject;
import org.eclipse.jetty.security.MappedLoginService.KnownUser;
import org.eclipse.jetty.security.MappedLoginService.RolePrincipal;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.PathWatcher;
import org.eclipse.jetty.util.PathWatcher.PathWatchEvent;
@ -64,17 +63,17 @@ public class PropertyUserStore extends AbstractLifeCycle implements PathWatcher.
{
private static final Logger LOG = Log.getLogger(PropertyUserStore.class);
private Path _configPath;
private Resource _configResource;
protected Path _configPath;
protected Resource _configResource;
private PathWatcher pathWatcher;
private boolean hotReload = false; // default is not to reload
protected PathWatcher pathWatcher;
protected boolean hotReload = false; // default is not to reload
private IdentityService _identityService = new DefaultIdentityService();
private boolean _firstLoad = true; // true if first load, false from that point on
private final List<String> _knownUsers = new ArrayList<String>();
private final Map<String, UserIdentity> _knownUserIdentities = new HashMap<String, UserIdentity>();
private List<UserListener> _listeners;
protected IdentityService _identityService = new DefaultIdentityService();
protected boolean _firstLoad = true; // true if first load, false from that point on
protected final List<String> _knownUsers = new ArrayList<String>();
protected final Map<String, UserIdentity> _knownUserIdentities = new HashMap<String, UserIdentity>();
protected List<UserListener> _listeners;
/**
* Get the config (as a string)
@ -186,27 +185,7 @@ public class PropertyUserStore extends AbstractLifeCycle implements PathWatcher.
this.hotReload = enable;
}
/* ------------------------------------------------------------ */
/**
* sets the refresh interval (in seconds)
* @param sec the refresh interval
* @deprecated use {@link #setHotReload(boolean)} instead
*/
@Deprecated
public void setRefreshInterval(int sec)
{
}
/* ------------------------------------------------------------ */
/**
* @return refresh interval in seconds for how often the properties file should be checked for changes
* @deprecated use {@link #isHotReload()} instead
*/
@Deprecated
public int getRefreshInterval()
{
return (hotReload)?1:0;
}
@Override
public String toString()
@ -221,7 +200,7 @@ public class PropertyUserStore extends AbstractLifeCycle implements PathWatcher.
}
/* ------------------------------------------------------------ */
private void loadUsers() throws IOException
protected void loadUsers() throws IOException
{
if (_configPath == null)
return;
@ -259,7 +238,7 @@ public class PropertyUserStore extends AbstractLifeCycle implements PathWatcher.
known.add(username);
Credential credential = Credential.getCredential(credentials);
Principal userPrincipal = new KnownUser(username,credential);
Principal userPrincipal = new AbstractLoginService.UserPrincipal(username,credential);
Subject subject = new Subject();
subject.getPrincipals().add(userPrincipal);
subject.getPrivateCredentials().add(credential);
@ -268,7 +247,7 @@ public class PropertyUserStore extends AbstractLifeCycle implements PathWatcher.
{
for (String role : roleArray)
{
subject.getPrincipals().add(new RolePrincipal(role));
subject.getPrincipals().add(new AbstractLoginService.RolePrincipal(role));
}
}

View File

@ -63,6 +63,7 @@ public class AliasedConstraintTest
private static LocalConnector connector;
private static ConstraintSecurityHandler security;
@BeforeClass
public static void startServer() throws Exception
{
@ -73,7 +74,8 @@ public class AliasedConstraintTest
ContextHandler context = new ContextHandler();
SessionHandler session = new SessionHandler();
HashLoginService loginService = new HashLoginService(TEST_REALM);
TestLoginService loginService = new TestLoginService(TEST_REALM);
loginService.putUser("user0",new Password("password"),new String[] {});
loginService.putUser("user",new Password("password"),new String[] { "user" });
loginService.putUser("user2",new Password("password"),new String[] { "user" });

View File

@ -85,7 +85,8 @@ public class ConstraintTest
ContextHandler _context = new ContextHandler();
SessionHandler _session = new SessionHandler();
HashLoginService _loginService = new HashLoginService(TEST_REALM);
TestLoginService _loginService = new TestLoginService(TEST_REALM);
_loginService.putUser("user0", new Password("password"), new String[]{});
_loginService.putUser("user",new Password("password"), new String[] {"user"});
_loginService.putUser("user2",new Password("password"), new String[] {"user"});

View File

@ -69,8 +69,9 @@ public class SpecExampleConstraintTest
ContextHandler _context = new ContextHandler();
_session = new SessionHandler();
HashLoginService _loginService = new HashLoginService(TEST_REALM);
_loginService.putUser("fred",new Password("password"));
TestLoginService _loginService = new TestLoginService(TEST_REALM);
_loginService.putUser("fred",new Password("password"), IdentityService.NO_ROLES);
_loginService.putUser("harry",new Password("password"), new String[] {"HOMEOWNER"});
_loginService.putUser("chris",new Password("password"), new String[] {"CONTRACTOR"});
_loginService.putUser("steven", new Password("password"), new String[] {"SALESCLERK"});

View File

@ -0,0 +1,69 @@
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.security;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.util.security.Credential;
/**
* TestLoginService
*
*
*/
public class TestLoginService extends AbstractLoginService
{
protected Map<String, UserPrincipal> _users = new HashMap<>();
protected Map<String, String[]> _roles = new HashMap<>();
public TestLoginService(String name)
{
setName(name);
}
public void putUser (String username, Credential credential, String[] roles)
{
UserPrincipal userPrincipal = new UserPrincipal(username,credential);
_users.put(username, userPrincipal);
_roles.put(username, roles);
}
/**
* @see org.eclipse.jetty.security.AbstractLoginService#loadRoleInfo(org.eclipse.jetty.security.AbstractLoginService.UserPrincipal)
*/
@Override
protected String[] loadRoleInfo(UserPrincipal user)
{
return _roles.get(user.getName());
}
/**
* @see org.eclipse.jetty.security.AbstractLoginService#loadUserInfo(java.lang.String)
*/
@Override
protected UserPrincipal loadUserInfo(String username)
{
return _users.get(username);
}
}

View File

@ -105,6 +105,19 @@ public abstract class Credential implements Serializable
return _cooked.equals(UnixCrypt.crypt(passwd, _cooked));
}
@Override
public boolean equals (Object credential)
{
if (!(credential instanceof Crypt))
return false;
Crypt c = (Crypt)credential;
return _cooked.equals(c._cooked);
}
public static String crypt(String user, String pw)
{
return "CRYPT:" + UnixCrypt.crypt(pw, user);
@ -167,12 +180,7 @@ public abstract class Credential implements Serializable
}
else if (credentials instanceof MD5)
{
MD5 md5 = (MD5) credentials;
if (_digest.length != md5._digest.length) return false;
boolean digestMismatch = false;
for (int i = 0; i < _digest.length; i++)
digestMismatch |= (_digest[i] != md5._digest[i]);
return !digestMismatch;
return equals((MD5)credentials);
}
else if (credentials instanceof Credential)
{
@ -193,6 +201,24 @@ public abstract class Credential implements Serializable
}
}
@Override
public boolean equals(Object obj)
{
if (obj instanceof MD5)
{
MD5 md5 = (MD5) obj;
if (_digest.length != md5._digest.length) return false;
boolean digestMismatch = false;
for (int i = 0; i < _digest.length; i++)
digestMismatch |= (_digest[i] != md5._digest[i]);
return !digestMismatch;
}
return false;
}
/* ------------------------------------------------------------ */
public static String digest(String password)
{

View File

@ -0,0 +1,79 @@
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.security;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.eclipse.jetty.util.security.Credential.Crypt;
import org.eclipse.jetty.util.security.Credential.MD5;
import org.junit.Test;
/**
* CredentialTest
*
*
*/
public class CredentialTest
{
@Test
public void testCrypt() throws Exception
{
Crypt c1 = (Crypt)Credential.getCredential(Crypt.crypt("fred", "abc123"));
Crypt c2 = (Crypt)Credential.getCredential(Crypt.crypt("fred", "abc123"));
Crypt c3 = (Crypt)Credential.getCredential(Crypt.crypt("fred", "xyz123"));
Credential c4 = Credential.getCredential(Crypt.crypt("fred", "xyz123"));
assertTrue(c1.equals(c2));
assertTrue(c2.equals(c1));
assertFalse(c1.equals(c3));
assertFalse(c3.equals(c1));
assertFalse(c3.equals(c2));
assertTrue(c4.equals(c3));
assertFalse(c4.equals(c1));
}
@Test
public void testMD5() throws Exception
{
MD5 m1 = (MD5)Credential.getCredential(MD5.digest("123foo"));
MD5 m2 = (MD5)Credential.getCredential(MD5.digest("123foo"));
MD5 m3 = (MD5)Credential.getCredential(MD5.digest("123boo"));
assertTrue(m1.equals(m2));
assertTrue(m2.equals(m1));
assertFalse(m3.equals(m1));
}
@Test
public void testPassword() throws Exception
{
Password p1 = new Password(Password.obfuscate("abc123"));
Credential p2 = Credential.getCredential(Password.obfuscate("abc123"));
assertTrue (p1.equals(p2));
}
}

View File

@ -25,6 +25,9 @@ import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServlet;
@ -39,6 +42,7 @@ import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.DigestAuthentication;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.security.AbstractLoginService;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.HashLoginService;
@ -55,6 +59,7 @@ import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Credential;
import org.eclipse.jetty.util.security.Password;
import org.junit.AfterClass;
import org.junit.Assert;
@ -79,6 +84,44 @@ public class DigestPostTest
public volatile static String _received = null;
private static Server _server;
public static class TestLoginService extends AbstractLoginService
{
protected Map<String, UserPrincipal> users = new HashMap<>();
protected Map<String, String[]> roles = new HashMap<>();
public TestLoginService(String name)
{
setName(name);
}
public void putUser (String username, Credential credential, String[] rolenames)
{
UserPrincipal userPrincipal = new UserPrincipal(username,credential);
users.put(username, userPrincipal);
roles.put(username, rolenames);
}
/**
* @see org.eclipse.jetty.security.AbstractLoginService#loadRoleInfo(org.eclipse.jetty.security.AbstractLoginService.UserPrincipal)
*/
@Override
protected String[] loadRoleInfo(UserPrincipal user)
{
return roles.get(user.getName());
}
/**
* @see org.eclipse.jetty.security.AbstractLoginService#loadUserInfo(java.lang.String)
*/
@Override
protected UserPrincipal loadUserInfo(String username)
{
return users.get(username);
}
}
@BeforeClass
public static void setUpServer()
{
@ -91,7 +134,7 @@ public class DigestPostTest
context.setContextPath("/test");
context.addServlet(PostServlet.class,"/");
HashLoginService realm = new HashLoginService("test");
TestLoginService realm = new TestLoginService("test");
realm.putUser("testuser",new Password("password"),new String[]{"test"});
_server.addBean(realm);

View File

@ -62,7 +62,6 @@ public class DataSourceLoginServiceTest
private static HttpClient _client;
private static String __realm = "DSRealm";
private static URI _baseUri;
private static final int __cacheInterval = 200;
private static DatabaseLoginServiceTestServer _testServer;
@ -124,7 +123,6 @@ public class DataSourceLoginServiceTest
loginService.setUserRoleTableUserKey("user_id");
loginService.setJndiName("dstest");
loginService.setName(__realm);
loginService.setCacheMs(__cacheInterval);
if (_testServer != null)
loginService.setServer(_testServer.getServer());
@ -154,7 +152,7 @@ public class DataSourceLoginServiceTest
String newpwd = String.valueOf(System.currentTimeMillis());
changePassword("jetty", newpwd);
TimeUnit.MILLISECONDS.sleep(2*__cacheInterval); //pause to ensure cache invalidates
startClient("jetty", newpwd);

View File

@ -54,9 +54,8 @@ detected.
<Set name="name">Test Realm</Set>
<Set name="config"><Property name="this.web-inf.url"/>realm.properties</Set>
<!-- To enable reload of realm when properties change, uncomment the following lines -->
<!-- changing refreshInterval (in seconds) as desired -->
<!--
<Set name="refreshInterval">5</Set>
<Set name="hotReload">false</Set>
<Call name="start"></Call>
-->
</New>

View File

@ -13,7 +13,7 @@
<New class="org.eclipse.jetty.security.HashLoginService">
<Set name="name">Test Realm</Set>
<Set name="config"><Property name="jetty.demo.realm" default="etc/realm.properties"/></Set>
<Set name="refreshInterval">0</Set>
<Set name="hotReload">false</Set>
</New>
</Arg>
</Call>

View File

@ -81,9 +81,8 @@ detected.
<Set name="name">Test Realm</Set>
<Set name="config"><SystemProperty name="jetty.base" default="."/>/etc/realm.properties</Set>
<!-- To enable reload of realm when properties change, uncomment the following lines -->
<!-- changing refreshInterval (in seconds) as desired -->
<!--
<Set name="refreshInterval">5</Set>
<Set name="hotReload">true</Set>
<Call name="start"></Call>
-->
</New>