Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-11.0.x
This commit is contained in:
commit
83e5b3fc04
|
@ -14,7 +14,6 @@ jdbc
|
|||
jsp
|
||||
annotations
|
||||
ext
|
||||
demo-realm
|
||||
|
||||
[files]
|
||||
basehome:modules/demo.d/demo-jaas.xml|webapps/demo-jaas.xml
|
||||
|
@ -22,6 +21,6 @@ basehome:modules/demo.d/demo-login.conf|etc/demo-login.conf
|
|||
basehome:modules/demo.d/demo-login.properties|etc/demo-login.properties
|
||||
maven://org.eclipse.jetty.demos/demo-jaas-webapp/${jetty.version}/war|webapps/demo-jaas.war
|
||||
|
||||
[ini-template]
|
||||
[ini]
|
||||
# Enable security via jaas, and configure it
|
||||
jetty.jaas.login.conf=etc/demo-login.conf
|
||||
jetty.jaas.login.conf?=etc/demo-login.conf
|
||||
|
|
|
@ -231,12 +231,14 @@ public class JAASLoginService extends ContainerLifeCycle implements LoginService
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.trace("IGNORED", e);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Login error", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
INSTANCE.remove();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import javax.security.auth.login.LoginContext;
|
|||
* JAASUserPrincipal
|
||||
* <p>
|
||||
* Implements the JAAS version of the
|
||||
* org.eclipse.jetty.http.UserPrincipal interface.
|
||||
* org.eclipse.jetty.security.UserPrincipal interface.
|
||||
*/
|
||||
public class JAASUserPrincipal implements Principal
|
||||
{
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Map;
|
|||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
|
||||
import org.eclipse.jetty.security.UserPrincipal;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -57,11 +58,11 @@ public abstract class AbstractDatabaseLoginModule extends AbstractLoginModule
|
|||
*/
|
||||
public abstract Connection getConnection() throws Exception;
|
||||
|
||||
public class JDBCUserInfo extends UserInfo
|
||||
public class JDBCUser extends JAASUser
|
||||
{
|
||||
public JDBCUserInfo(String userName, Credential credential)
|
||||
public JDBCUser(UserPrincipal user)
|
||||
{
|
||||
super(userName, credential);
|
||||
super(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,7 +80,7 @@ public abstract class AbstractDatabaseLoginModule extends AbstractLoginModule
|
|||
* @throws Exception if unable to get the user info
|
||||
*/
|
||||
@Override
|
||||
public UserInfo getUserInfo(String userName)
|
||||
public JAASUser getUser(String userName)
|
||||
throws Exception
|
||||
{
|
||||
try (Connection connection = getConnection())
|
||||
|
@ -100,11 +101,9 @@ public abstract class AbstractDatabaseLoginModule extends AbstractLoginModule
|
|||
}
|
||||
|
||||
if (dbCredential == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JDBCUserInfo(userName, Credential.getCredential(dbCredential));
|
||||
return new JDBCUser(new UserPrincipal(userName, Credential.getCredential(dbCredential)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
package org.eclipse.jetty.jaas.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
|
@ -34,9 +34,10 @@ import javax.security.auth.login.FailedLoginException;
|
|||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
import org.eclipse.jetty.jaas.JAASPrincipal;
|
||||
import org.eclipse.jetty.jaas.JAASRole;
|
||||
import org.eclipse.jetty.jaas.callback.ObjectCallback;
|
||||
import org.eclipse.jetty.security.UserPrincipal;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
|
||||
/**
|
||||
* AbstractLoginModule
|
||||
|
@ -50,35 +51,22 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
|
||||
private boolean authState = false;
|
||||
private boolean commitState = false;
|
||||
private JAASUserInfo currentUser;
|
||||
private JAASUser currentUser;
|
||||
private Subject subject;
|
||||
|
||||
/**
|
||||
* JAASUserInfo
|
||||
*
|
||||
* This class unites the UserInfo data with jaas concepts
|
||||
* such as Subject and Principals
|
||||
*/
|
||||
public class JAASUserInfo
|
||||
public abstract static class JAASUser
|
||||
{
|
||||
private UserInfo user;
|
||||
private Principal principal;
|
||||
private List<JAASRole> roles;
|
||||
|
||||
public JAASUserInfo(UserInfo u)
|
||||
private final UserPrincipal _user;
|
||||
private List<JAASRole> _roles;
|
||||
|
||||
public JAASUser(UserPrincipal u)
|
||||
{
|
||||
this.user = u;
|
||||
this.principal = new JAASPrincipal(u.getUserName());
|
||||
_user = u;
|
||||
}
|
||||
|
||||
public String getUserName()
|
||||
{
|
||||
return this.user.getUserName();
|
||||
}
|
||||
|
||||
public Principal getPrincipal()
|
||||
{
|
||||
return this.principal;
|
||||
return _user.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,12 +74,12 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
*/
|
||||
public void setJAASInfo(Subject subject)
|
||||
{
|
||||
subject.getPrincipals().add(this.principal);
|
||||
if (this.user.getCredential() != null)
|
||||
{
|
||||
subject.getPrivateCredentials().add(this.user.getCredential());
|
||||
}
|
||||
subject.getPrincipals().addAll(roles);
|
||||
if (_user == null)
|
||||
return;
|
||||
|
||||
_user.configureSubject(subject);
|
||||
if (_roles != null)
|
||||
subject.getPrincipals().addAll(_roles);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,35 +87,29 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
*/
|
||||
public void unsetJAASInfo(Subject subject)
|
||||
{
|
||||
subject.getPrincipals().remove(this.principal);
|
||||
if (this.user.getCredential() != null)
|
||||
{
|
||||
subject.getPrivateCredentials().remove(this.user.getCredential());
|
||||
}
|
||||
subject.getPrincipals().removeAll(this.roles);
|
||||
if (_user == null)
|
||||
return;
|
||||
_user.deconfigureSubject(subject);
|
||||
if (_roles != null)
|
||||
subject.getPrincipals().removeAll(_roles);
|
||||
}
|
||||
|
||||
public boolean checkCredential(Object suppliedCredential)
|
||||
{
|
||||
return this.user.checkCredential(suppliedCredential);
|
||||
return _user.authenticate(suppliedCredential);
|
||||
}
|
||||
|
||||
public void fetchRoles() throws Exception
|
||||
{
|
||||
this.user.fetchRoles();
|
||||
this.roles = new ArrayList<JAASRole>();
|
||||
if (this.user.getRoleNames() != null)
|
||||
{
|
||||
Iterator<String> itor = this.user.getRoleNames().iterator();
|
||||
while (itor.hasNext())
|
||||
{
|
||||
this.roles.add(new JAASRole((String)itor.next()));
|
||||
}
|
||||
}
|
||||
List<String> rolenames = doFetchRoles();
|
||||
if (rolenames != null)
|
||||
_roles = rolenames.stream().map(JAASRole::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public abstract List<String> doFetchRoles() throws Exception;
|
||||
}
|
||||
|
||||
public abstract UserInfo getUserInfo(String username) throws Exception;
|
||||
public abstract JAASUser getUser(String username) throws Exception;
|
||||
|
||||
public Subject getSubject()
|
||||
{
|
||||
|
@ -139,12 +121,12 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
this.subject = s;
|
||||
}
|
||||
|
||||
public JAASUserInfo getCurrentUser()
|
||||
public JAASUser getCurrentUser()
|
||||
{
|
||||
return this.currentUser;
|
||||
}
|
||||
|
||||
public void setCurrentUser(JAASUserInfo u)
|
||||
public void setCurrentUser(JAASUser u)
|
||||
{
|
||||
this.currentUser = u;
|
||||
}
|
||||
|
@ -252,15 +234,15 @@ public abstract class AbstractLoginModule implements LoginModule
|
|||
throw new FailedLoginException();
|
||||
}
|
||||
|
||||
UserInfo userInfo = getUserInfo(webUserName);
|
||||
JAASUser user = getUser(webUserName);
|
||||
|
||||
if (userInfo == null)
|
||||
if (user == null)
|
||||
{
|
||||
setAuthenticated(false);
|
||||
throw new FailedLoginException();
|
||||
}
|
||||
|
||||
currentUser = new JAASUserInfo(userInfo);
|
||||
currentUser = user;
|
||||
setAuthenticated(currentUser.checkCredential(webCredential));
|
||||
|
||||
if (isAuthenticated())
|
||||
|
|
|
@ -45,6 +45,7 @@ import javax.security.auth.login.FailedLoginException;
|
|||
import javax.security.auth.login.LoginException;
|
||||
|
||||
import org.eclipse.jetty.jaas.callback.ObjectCallback;
|
||||
import org.eclipse.jetty.security.UserPrincipal;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -179,18 +180,13 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
|
||||
private DirContext _rootContext;
|
||||
|
||||
public class LDAPUserInfo extends UserInfo
|
||||
public class LDAPUser extends JAASUser
|
||||
{
|
||||
Attributes attributes;
|
||||
|
||||
/**
|
||||
* @param userName the user name
|
||||
* @param credential the credential
|
||||
* @param attributes the user {@link Attributes}
|
||||
*/
|
||||
public LDAPUserInfo(String userName, Credential credential, Attributes attributes)
|
||||
public LDAPUser(UserPrincipal user, Attributes attributes)
|
||||
{
|
||||
super(userName, credential);
|
||||
super(user);
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
|
@ -201,6 +197,25 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
}
|
||||
}
|
||||
|
||||
public class LDAPBindingUser extends JAASUser
|
||||
{
|
||||
DirContext _context;
|
||||
String _userDn;
|
||||
|
||||
public LDAPBindingUser(UserPrincipal user, DirContext context, String userDn)
|
||||
{
|
||||
super(user);
|
||||
_context = context;
|
||||
_userDn = userDn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> doFetchRoles() throws Exception
|
||||
{
|
||||
return getUserRolesByDn(_context, _userDn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the available information about the user
|
||||
* <p>
|
||||
|
@ -214,19 +229,17 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
* @throws Exception if unable to get the user info
|
||||
*/
|
||||
@Override
|
||||
public UserInfo getUserInfo(String username) throws Exception
|
||||
public JAASUser getUser(String username) throws Exception
|
||||
{
|
||||
Attributes attributes = getUserAttributes(username);
|
||||
String pwdCredential = getUserCredentials(attributes);
|
||||
|
||||
if (pwdCredential == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
pwdCredential = convertCredentialLdapToJetty(pwdCredential);
|
||||
Credential credential = Credential.getCredential(pwdCredential);
|
||||
return new LDAPUserInfo(username, credential, attributes);
|
||||
return new LDAPUser(new UserPrincipal(username, credential), attributes);
|
||||
}
|
||||
|
||||
protected String doRFC2254Encoding(String inputString)
|
||||
|
@ -421,7 +434,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
else
|
||||
{
|
||||
// This sets read and the credential
|
||||
UserInfo userInfo = getUserInfo(webUserName);
|
||||
JAASUser userInfo = getUser(webUserName);
|
||||
|
||||
if (userInfo == null)
|
||||
{
|
||||
|
@ -429,7 +442,7 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
return false;
|
||||
}
|
||||
|
||||
setCurrentUser(new JAASUserInfo(userInfo));
|
||||
setCurrentUser(userInfo);
|
||||
|
||||
if (webCredential instanceof String)
|
||||
authed = credentialLogin(Credential.getCredential((String)webCredential));
|
||||
|
@ -520,12 +533,8 @@ public class LdapLoginModule extends AbstractLoginModule
|
|||
try
|
||||
{
|
||||
DirContext dirContext = new InitialDirContext(environment);
|
||||
List<String> roles = getUserRolesByDn(dirContext, userDn);
|
||||
|
||||
UserInfo userInfo = new UserInfo(username, null, roles);
|
||||
setCurrentUser(new JAASUserInfo(userInfo));
|
||||
setCurrentUser(new LDAPBindingUser(new UserPrincipal(username, null), dirContext, userDn));
|
||||
setAuthenticated(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (javax.naming.AuthenticationException e)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.jaas.spi;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -27,8 +28,9 @@ import javax.security.auth.callback.CallbackHandler;
|
|||
|
||||
import org.eclipse.jetty.jaas.JAASLoginService;
|
||||
import org.eclipse.jetty.jaas.PropertyUserStoreManager;
|
||||
import org.eclipse.jetty.security.AbstractLoginService;
|
||||
import org.eclipse.jetty.security.PropertyUserStore;
|
||||
import org.eclipse.jetty.security.RolePrincipal;
|
||||
import org.eclipse.jetty.security.UserPrincipal;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -122,22 +124,23 @@ public class PropertyFileLoginModule extends AbstractLoginModule
|
|||
* @throws Exception if unable to get the user information
|
||||
*/
|
||||
@Override
|
||||
public UserInfo getUserInfo(String userName) throws Exception
|
||||
public JAASUser getUser(String userName) throws Exception
|
||||
{
|
||||
LOG.debug("Checking PropertyUserStore {} for {}", _store.getConfig(), userName);
|
||||
UserIdentity userIdentity = _store.getUserIdentity(userName);
|
||||
if (userIdentity == null)
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Checking PropertyUserStore {} for {}", _store.getConfig(), userName);
|
||||
UserPrincipal up = _store.getUserPrincipal(userName);
|
||||
if (up == null)
|
||||
return null;
|
||||
|
||||
//TODO in future versions change the impl of PropertyUserStore so its not
|
||||
//storing Subjects etc, just UserInfo
|
||||
Set<AbstractLoginService.RolePrincipal> principals = userIdentity.getSubject().getPrincipals(AbstractLoginService.RolePrincipal.class);
|
||||
|
||||
List<String> roles = principals.stream()
|
||||
.map(AbstractLoginService.RolePrincipal::getName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Credential credential = (Credential)userIdentity.getSubject().getPrivateCredentials().iterator().next();
|
||||
return new UserInfo(userName, credential, roles);
|
||||
List<RolePrincipal> rps = _store.getRolePrincipals(userName);
|
||||
List<String> roles = rps == null ? Collections.emptyList() : rps.stream().map(RolePrincipal::getName).collect(Collectors.toList());
|
||||
return new JAASUser(up)
|
||||
{
|
||||
@Override
|
||||
public List<String> doFetchRoles()
|
||||
{
|
||||
return roles;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.jaas.spi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
|
||||
/**
|
||||
* UserInfo
|
||||
*
|
||||
* This is the information read from the external source
|
||||
* about a user.
|
||||
*
|
||||
* Can be cached.
|
||||
*/
|
||||
public class UserInfo
|
||||
{
|
||||
private final AutoLock _lock = new AutoLock();
|
||||
private String _userName;
|
||||
private Credential _credential;
|
||||
protected List<String> _roleNames = new ArrayList<>();
|
||||
protected boolean _rolesLoaded = false;
|
||||
|
||||
/**
|
||||
* @param userName the user name
|
||||
* @param credential the credential
|
||||
* @param roleNames a {@link List} of role name
|
||||
*/
|
||||
public UserInfo(String userName, Credential credential, List<String> roleNames)
|
||||
{
|
||||
_userName = userName;
|
||||
_credential = credential;
|
||||
if (roleNames != null)
|
||||
{
|
||||
_roleNames.addAll(roleNames);
|
||||
_rolesLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userName the user name
|
||||
* @param credential the credential
|
||||
*/
|
||||
public UserInfo(String userName, Credential credential)
|
||||
{
|
||||
this(userName, credential, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be overridden by subclasses to obtain
|
||||
* role info
|
||||
*
|
||||
* @return List of role associated to the user
|
||||
* @throws Exception if the roles cannot be retrieved
|
||||
*/
|
||||
public List<String> doFetchRoles()
|
||||
throws Exception
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public void fetchRoles() throws Exception
|
||||
{
|
||||
try (AutoLock l = _lock.lock())
|
||||
{
|
||||
if (!_rolesLoaded)
|
||||
{
|
||||
_roleNames.addAll(doFetchRoles());
|
||||
_rolesLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getUserName()
|
||||
{
|
||||
return this._userName;
|
||||
}
|
||||
|
||||
public List<String> getRoleNames()
|
||||
{
|
||||
return Collections.unmodifiableList(_roleNames);
|
||||
}
|
||||
|
||||
public boolean checkCredential(Object suppliedCredential)
|
||||
{
|
||||
return _credential.check(suppliedCredential);
|
||||
}
|
||||
|
||||
protected Credential getCredential()
|
||||
{
|
||||
return _credential;
|
||||
}
|
||||
}
|
|
@ -18,12 +18,14 @@
|
|||
|
||||
package org.eclipse.jetty.jaas;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
import org.eclipse.jetty.jaas.callback.ServletRequestCallback;
|
||||
import org.eclipse.jetty.jaas.spi.AbstractLoginModule;
|
||||
import org.eclipse.jetty.jaas.spi.UserInfo;
|
||||
import org.eclipse.jetty.security.UserPrincipal;
|
||||
import org.eclipse.jetty.util.ArrayUtil;
|
||||
import org.eclipse.jetty.util.security.Password;
|
||||
|
||||
|
@ -34,9 +36,16 @@ public class TestLoginModule extends AbstractLoginModule
|
|||
public ServletRequestCallback _callback = new ServletRequestCallback();
|
||||
|
||||
@Override
|
||||
public UserInfo getUserInfo(String username) throws Exception
|
||||
{
|
||||
return new UserInfo(username, new Password("aaa"));
|
||||
public JAASUser getUser(String username) throws Exception
|
||||
{
|
||||
return new JAASUser(new UserPrincipal(username, new Password("aaa")))
|
||||
{
|
||||
@Override
|
||||
public List<String> doFetchRoles() throws Exception
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,9 +19,12 @@
|
|||
package org.eclipse.jetty.security.jaspi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
@ -29,6 +32,8 @@ import jakarta.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.RolePrincipal;
|
||||
import org.eclipse.jetty.security.UserPrincipal;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -55,7 +60,7 @@ public class JaspiTest
|
|||
public class TestLoginService extends AbstractLoginService
|
||||
{
|
||||
protected Map<String, UserPrincipal> _users = new HashMap<>();
|
||||
protected Map<String, String[]> _roles = new HashMap();
|
||||
protected Map<String, List<RolePrincipal>> _roles = new HashMap<>();
|
||||
|
||||
public TestLoginService(String name)
|
||||
{
|
||||
|
@ -66,11 +71,15 @@ public class JaspiTest
|
|||
{
|
||||
UserPrincipal userPrincipal = new UserPrincipal(username, credential);
|
||||
_users.put(username, userPrincipal);
|
||||
_roles.put(username, roles);
|
||||
if (roles != null)
|
||||
{
|
||||
List<RolePrincipal> rps = Arrays.stream(roles).map(RolePrincipal::new).collect(Collectors.toList());
|
||||
_roles.put(username, rps);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] loadRoleInfo(UserPrincipal user)
|
||||
protected List<RolePrincipal> loadRoleInfo(UserPrincipal user)
|
||||
{
|
||||
return _roles.get(user.getName());
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.sql.Statement;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NameNotFoundException;
|
||||
import javax.naming.NamingException;
|
||||
|
@ -35,16 +36,17 @@ 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.RolePrincipal;
|
||||
import org.eclipse.jetty.security.UserPrincipal;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* DataSourceUserRealm
|
||||
* DataSourceLoginService
|
||||
* <p>
|
||||
* Obtain user/password/role information from a database
|
||||
* via jndi DataSource.
|
||||
* Obtain user/password/role information from a database via jndi DataSource.
|
||||
*/
|
||||
public class DataSourceLoginService extends AbstractLoginService
|
||||
{
|
||||
|
@ -264,7 +266,7 @@ public class DataSourceLoginService extends AbstractLoginService
|
|||
}
|
||||
|
||||
@Override
|
||||
public String[] loadRoleInfo(UserPrincipal user)
|
||||
public List<RolePrincipal> loadRoleInfo(UserPrincipal user)
|
||||
{
|
||||
DBUserPrincipal dbuser = (DBUserPrincipal)user;
|
||||
|
||||
|
@ -280,11 +282,9 @@ public class DataSourceLoginService extends AbstractLoginService
|
|||
try (ResultSet rs2 = statement2.executeQuery())
|
||||
{
|
||||
while (rs2.next())
|
||||
{
|
||||
roles.add(rs2.getString(_roleTableRoleField));
|
||||
}
|
||||
|
||||
return roles.toArray(new String[roles.size()]);
|
||||
return roles.stream().map(RolePrincipal::new).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,19 +18,21 @@
|
|||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* AbstractLoginService
|
||||
*
|
||||
* Base class for LoginServices that allows subclasses to provide the user authentication and authorization information,
|
||||
* but provides common behaviour such as handling authentication.
|
||||
*/
|
||||
public abstract class AbstractLoginService extends ContainerLifeCycle implements LoginService
|
||||
{
|
||||
|
@ -40,65 +42,7 @@ public abstract class AbstractLoginService extends ContainerLifeCycle implements
|
|||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String[] loadRoleInfo(UserPrincipal user);
|
||||
protected abstract List<RolePrincipal> loadRoleInfo(UserPrincipal user);
|
||||
|
||||
protected abstract UserPrincipal loadUserInfo(String username);
|
||||
|
||||
|
@ -155,18 +99,22 @@ public abstract class AbstractLoginService extends ContainerLifeCycle implements
|
|||
if (userPrincipal != null && userPrincipal.authenticate(credentials))
|
||||
{
|
||||
//safe to load the roles
|
||||
String[] roles = loadRoleInfo(userPrincipal);
|
||||
List<RolePrincipal> roles = loadRoleInfo(userPrincipal);
|
||||
|
||||
List<String> roleNames = new ArrayList<>();
|
||||
Subject subject = new Subject();
|
||||
subject.getPrincipals().add(userPrincipal);
|
||||
subject.getPrivateCredentials().add(userPrincipal._credential);
|
||||
userPrincipal.configureSubject(subject);
|
||||
if (roles != null)
|
||||
for (String role : roles)
|
||||
{
|
||||
roles.forEach(p ->
|
||||
{
|
||||
subject.getPrincipals().add(new RolePrincipal(role));
|
||||
}
|
||||
p.configureForSubject(subject);
|
||||
roleNames.add(p.getName());
|
||||
});
|
||||
}
|
||||
|
||||
subject.setReadOnly();
|
||||
return _identityService.newUserIdentity(subject, userPrincipal, roles);
|
||||
return _identityService.newUserIdentity(subject, userPrincipal, roleNames.toArray(new String[0]));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -185,10 +133,10 @@ public abstract class AbstractLoginService extends ContainerLifeCycle implements
|
|||
|
||||
if (user.getUserPrincipal() instanceof UserPrincipal)
|
||||
{
|
||||
return fresh.authenticate(((UserPrincipal)user.getUserPrincipal())._credential);
|
||||
return fresh.authenticate(((UserPrincipal)user.getUserPrincipal()));
|
||||
}
|
||||
|
||||
throw new IllegalStateException("UserPrincipal not KnownUser"); //can't validate
|
||||
throw new IllegalStateException("UserPrincipal not known"); //can't validate
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -201,7 +149,6 @@ public abstract class AbstractLoginService extends ContainerLifeCycle implements
|
|||
public void logout(UserIdentity user)
|
||||
{
|
||||
//Override in subclasses
|
||||
|
||||
}
|
||||
|
||||
public boolean isFullValidate()
|
||||
|
|
|
@ -19,20 +19,13 @@
|
|||
package org.eclipse.jetty.security;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Properties User Realm.
|
||||
* <p>
|
||||
* 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:
|
||||
*
|
||||
* An implementation of a LoginService that stores users and roles in-memory in HashMaps.
|
||||
* The source of the users and roles information is a properties file formatted like so:
|
||||
* <pre>
|
||||
* username: password [,rolename ...]
|
||||
* </pre>
|
||||
|
@ -72,7 +65,7 @@ public class HashLoginService extends AbstractLoginService
|
|||
}
|
||||
|
||||
/**
|
||||
* Load realm users from properties file.
|
||||
* Load users from properties file.
|
||||
* <p>
|
||||
* The property file maps usernames to password specs followed by an optional comma separated list of role names.
|
||||
* </p>
|
||||
|
@ -121,41 +114,21 @@ public class HashLoginService extends AbstractLoginService
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String[] loadRoleInfo(UserPrincipal user)
|
||||
protected List<RolePrincipal> loadRoleInfo(UserPrincipal user)
|
||||
{
|
||||
UserIdentity id = _userStore.getUserIdentity(user.getName());
|
||||
if (id == null)
|
||||
return null;
|
||||
|
||||
Set<RolePrincipal> roles = id.getSubject().getPrincipals(RolePrincipal.class);
|
||||
if (roles == null)
|
||||
return null;
|
||||
|
||||
List<String> list = roles.stream()
|
||||
.map(rolePrincipal -> rolePrincipal.getName())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return list.toArray(new String[roles.size()]);
|
||||
return _userStore.getRolePrincipals(user.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserPrincipal loadUserInfo(String userName)
|
||||
{
|
||||
UserIdentity id = _userStore.getUserIdentity(userName);
|
||||
if (id != null)
|
||||
{
|
||||
return (UserPrincipal)id.getUserPrincipal();
|
||||
}
|
||||
|
||||
return null;
|
||||
return _userStore.getUserPrincipal(userName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
super.doStart();
|
||||
|
||||
// can be null so we switch to previous behaviour using PropertyUserStore
|
||||
if (_userStore == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -179,7 +152,6 @@ public class HashLoginService extends AbstractLoginService
|
|||
}
|
||||
|
||||
/**
|
||||
* To facilitate testing.
|
||||
*
|
||||
* @return true if a UserStore has been created from a config, false if a UserStore was provided.
|
||||
*/
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
|
@ -28,6 +27,7 @@ import java.sql.SQLException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import org.eclipse.jetty.util.Loader;
|
||||
|
@ -37,17 +37,8 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* HashMapped User Realm with JDBC as data source.
|
||||
* The {@link #login(String, Object, ServletRequest)} method checks the inherited Map for the user. If the user is not
|
||||
* found, it will fetch details from the database and populate the inherited
|
||||
* Map. It then calls the superclass {@link #login(String, Object, ServletRequest)} method to perform the actual
|
||||
* authentication. Periodically (controlled by configuration parameter),
|
||||
* internal hashes are cleared. Caching can be disabled by setting cache refresh
|
||||
* interval to zero. Uses one database connection that is initialized at
|
||||
* startup. Reconnect on failures.
|
||||
* <p>
|
||||
* An example properties file for configuration is in
|
||||
* <code>${jetty.home}/etc/jdbcRealm.properties</code>
|
||||
* JDBC as a source of user authentication and authorization information.
|
||||
* Uses one database connection that is lazily initialized. Reconnect on failures.
|
||||
*/
|
||||
public class JDBCLoginService extends AbstractLoginService
|
||||
{
|
||||
|
@ -61,16 +52,18 @@ public class JDBCLoginService extends AbstractLoginService
|
|||
protected String _userTableKey;
|
||||
protected String _userTablePasswordField;
|
||||
protected String _roleTableRoleField;
|
||||
protected Connection _con;
|
||||
protected String _userSql;
|
||||
protected String _roleSql;
|
||||
protected Connection _con;
|
||||
|
||||
/**
|
||||
* JDBCKnownUser
|
||||
* JDBCUserPrincipal
|
||||
*
|
||||
* A UserPrincipal with extra jdbc key info.
|
||||
*/
|
||||
public class JDBCUserPrincipal extends UserPrincipal
|
||||
{
|
||||
int _userKey;
|
||||
final int _userKey;
|
||||
|
||||
public JDBCUserPrincipal(String name, Credential credential, int key)
|
||||
{
|
||||
|
@ -85,25 +78,21 @@ public class JDBCLoginService extends AbstractLoginService
|
|||
}
|
||||
|
||||
public JDBCLoginService()
|
||||
throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
public JDBCLoginService(String name)
|
||||
throws IOException
|
||||
{
|
||||
setName(name);
|
||||
}
|
||||
|
||||
public JDBCLoginService(String name, String config)
|
||||
throws IOException
|
||||
{
|
||||
setName(name);
|
||||
setConfig(config);
|
||||
}
|
||||
|
||||
public JDBCLoginService(String name, IdentityService identityService, String config)
|
||||
throws IOException
|
||||
{
|
||||
setName(name);
|
||||
setIdentityService(identityService);
|
||||
|
@ -171,19 +160,12 @@ public class JDBCLoginService extends AbstractLoginService
|
|||
}
|
||||
|
||||
/**
|
||||
* (re)Connect to database with parameters setup by loadConfig()
|
||||
* Connect to database with parameters setup by loadConfig()
|
||||
*/
|
||||
public void connectDatabase()
|
||||
public Connection connectDatabase()
|
||||
throws SQLException
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName(_jdbcDriver);
|
||||
_con = DriverManager.getConnection(_url, _userName, _password);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("UserRealm {} could not connect to database; will try later", getName(), e);
|
||||
}
|
||||
return DriverManager.getConnection(_url, _userName, _password);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -192,10 +174,7 @@ public class JDBCLoginService extends AbstractLoginService
|
|||
try
|
||||
{
|
||||
if (null == _con)
|
||||
connectDatabase();
|
||||
|
||||
if (null == _con)
|
||||
throw new SQLException("Can't connect to database");
|
||||
_con = connectDatabase();
|
||||
|
||||
try (PreparedStatement stat1 = _con.prepareStatement(_userSql))
|
||||
{
|
||||
|
@ -214,7 +193,7 @@ public class JDBCLoginService extends AbstractLoginService
|
|||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
LOG.warn("UserRealm {} could not load user information from database", getName(), e);
|
||||
LOG.warn("LoginService {} could not load user {}", getName(), username, e);
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
|
@ -222,17 +201,17 @@ public class JDBCLoginService extends AbstractLoginService
|
|||
}
|
||||
|
||||
@Override
|
||||
public String[] loadRoleInfo(UserPrincipal user)
|
||||
public List<RolePrincipal> loadRoleInfo(UserPrincipal user)
|
||||
{
|
||||
if (user == null)
|
||||
return null;
|
||||
|
||||
JDBCUserPrincipal jdbcUser = (JDBCUserPrincipal)user;
|
||||
|
||||
try
|
||||
{
|
||||
if (null == _con)
|
||||
connectDatabase();
|
||||
|
||||
if (null == _con)
|
||||
throw new SQLException("Can't connect to database");
|
||||
_con = connectDatabase();
|
||||
|
||||
List<String> roles = new ArrayList<String>();
|
||||
|
||||
|
@ -242,16 +221,15 @@ public class JDBCLoginService extends AbstractLoginService
|
|||
try (ResultSet rs2 = stat2.executeQuery())
|
||||
{
|
||||
while (rs2.next())
|
||||
{
|
||||
roles.add(rs2.getString(_roleTableRoleField));
|
||||
}
|
||||
return roles.toArray(new String[roles.size()]);
|
||||
|
||||
return roles.stream().map(RolePrincipal::new).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
LOG.warn("UserRealm {} could not load user information from database", getName(), e);
|
||||
LOG.warn("LoginService {} could not load roles for user {}", getName(), user.getName(), e);
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
|
@ -273,7 +251,7 @@ public class JDBCLoginService extends AbstractLoginService
|
|||
if (_con != null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Closing db connection for JDBCUserRealm");
|
||||
LOG.debug("Closing db connection for JDBCLoginService");
|
||||
try
|
||||
{
|
||||
_con.close();
|
||||
|
|
|
@ -206,7 +206,7 @@ public class PropertyUserStore extends UserStore implements PathWatcher.Listener
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%x[users.count=%d,identityService=%s]", getClass().getSimpleName(), hashCode(), getKnownUserIdentities().size(), getIdentityService());
|
||||
return String.format("%s[cfg=%s]", super.toString(), _configPath);
|
||||
}
|
||||
|
||||
protected void loadUsers() throws IOException
|
||||
|
@ -251,7 +251,7 @@ public class PropertyUserStore extends UserStore implements PathWatcher.Listener
|
|||
}
|
||||
}
|
||||
|
||||
List<String> currentlyKnownUsers = new ArrayList<>(getKnownUserIdentities().keySet());
|
||||
List<String> currentlyKnownUsers = new ArrayList<>(_users.keySet());
|
||||
// if its not the initial load then we want to process removed users
|
||||
if (!_firstLoad)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.Principal;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
/**
|
||||
* RolePrincipal
|
||||
*
|
||||
* Represents a role. This class can be added to a Subject to represent a role that the
|
||||
* Subject has.
|
||||
*
|
||||
*/
|
||||
public class RolePrincipal implements Principal, Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 2998397924051854402L;
|
||||
private final String _roleName;
|
||||
|
||||
public RolePrincipal(String name)
|
||||
{
|
||||
_roleName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return _roleName;
|
||||
}
|
||||
|
||||
public void configureForSubject(Subject subject)
|
||||
{
|
||||
subject.getPrincipals().add(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.Principal;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
|
||||
/**
|
||||
* UserPrincipal
|
||||
*
|
||||
* Represents a user with a credential.
|
||||
* Instances of this class can be added to a Subject to
|
||||
* present the user, while the credentials can be added
|
||||
* directly to the Subject.
|
||||
*/
|
||||
public class UserPrincipal implements Principal, Serializable
|
||||
{
|
||||
private static final long serialVersionUID = -6226920753748399662L;
|
||||
private final String _name;
|
||||
protected 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 boolean authenticate(UserPrincipal u)
|
||||
{
|
||||
return (u != null && authenticate(u._credential));
|
||||
}
|
||||
|
||||
public void configureSubject(Subject subject)
|
||||
{
|
||||
if (subject == null)
|
||||
return;
|
||||
|
||||
subject.getPrincipals().add(this);
|
||||
if (_credential != null)
|
||||
subject.getPrivateCredentials().add(_credential);
|
||||
}
|
||||
|
||||
public void deconfigureSubject(Subject subject)
|
||||
{
|
||||
if (subject == null)
|
||||
return;
|
||||
subject.getPrincipals().remove(this);
|
||||
if (_credential != null)
|
||||
subject.getPrivateCredentials().remove(_credential);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
}
|
|
@ -18,59 +18,75 @@
|
|||
|
||||
package org.eclipse.jetty.security;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.security.auth.Subject;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
|
||||
/**
|
||||
* Base class to store User
|
||||
* Store of user authentication and authorization information.
|
||||
*
|
||||
*/
|
||||
public class UserStore extends AbstractLifeCycle
|
||||
{
|
||||
private IdentityService _identityService = new DefaultIdentityService();
|
||||
private final Map<String, UserIdentity> _knownUserIdentities = new ConcurrentHashMap<>();
|
||||
protected final Map<String, User> _users = new ConcurrentHashMap<>();
|
||||
|
||||
protected class User
|
||||
{
|
||||
protected UserPrincipal _userPrincipal;
|
||||
protected List<RolePrincipal> _rolePrincipals = Collections.emptyList();
|
||||
|
||||
protected User(String username, Credential credential, String[] roles)
|
||||
{
|
||||
_userPrincipal = new UserPrincipal(username, credential);
|
||||
|
||||
_rolePrincipals = Collections.emptyList();
|
||||
|
||||
if (roles != null)
|
||||
_rolePrincipals = Arrays.stream(roles).map(RolePrincipal::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected UserPrincipal getUserPrincipal()
|
||||
{
|
||||
return _userPrincipal;
|
||||
}
|
||||
|
||||
protected List<RolePrincipal> getRolePrincipals()
|
||||
{
|
||||
return _rolePrincipals;
|
||||
}
|
||||
}
|
||||
|
||||
public void addUser(String username, Credential credential, String[] roles)
|
||||
{
|
||||
Principal userPrincipal = new AbstractLoginService.UserPrincipal(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 AbstractLoginService.RolePrincipal(role));
|
||||
}
|
||||
}
|
||||
|
||||
subject.setReadOnly();
|
||||
_knownUserIdentities.put(username, _identityService.newUserIdentity(subject, userPrincipal, roles));
|
||||
_users.put(username, new User(username, credential, roles));
|
||||
}
|
||||
|
||||
public void removeUser(String username)
|
||||
{
|
||||
_knownUserIdentities.remove(username);
|
||||
_users.remove(username);
|
||||
}
|
||||
|
||||
public UserPrincipal getUserPrincipal(String username)
|
||||
{
|
||||
User user = _users.get(username);
|
||||
return (user == null ? null : user.getUserPrincipal());
|
||||
}
|
||||
|
||||
public List<RolePrincipal> getRolePrincipals(String username)
|
||||
{
|
||||
User user = _users.get(username);
|
||||
return (user == null ? null : user.getRolePrincipals());
|
||||
}
|
||||
|
||||
public UserIdentity getUserIdentity(String userName)
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _knownUserIdentities.get(userName);
|
||||
}
|
||||
|
||||
public IdentityService getIdentityService()
|
||||
{
|
||||
return _identityService;
|
||||
}
|
||||
|
||||
public Map<String, UserIdentity> getKnownUserIdentities()
|
||||
{
|
||||
return _knownUserIdentities;
|
||||
return String.format("%s@%x[users.count=%d]", getClass().getSimpleName(), hashCode(), _users.size());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,9 +191,9 @@ public class PropertyUserStoreTest
|
|||
|
||||
store.start();
|
||||
|
||||
assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("tom"), notNullValue());
|
||||
assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("dick"), notNullValue());
|
||||
assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", store.getUserIdentity("harry"), notNullValue());
|
||||
assertThat("Failed to retrieve user directly from PropertyUserStore", store.getUserPrincipal("tom"), notNullValue());
|
||||
assertThat("Failed to retrieve user directly from PropertyUserStore", store.getUserPrincipal("dick"), notNullValue());
|
||||
assertThat("Failed to retrieve user directly from PropertyUserStore", store.getUserPrincipal("harry"), notNullValue());
|
||||
userCount.assertThatCount(is(3));
|
||||
userCount.awaitCount(3);
|
||||
}
|
||||
|
@ -224,12 +224,12 @@ public class PropertyUserStoreTest
|
|||
|
||||
store.start();
|
||||
|
||||
assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", //
|
||||
store.getUserIdentity("tom"), notNullValue());
|
||||
assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", //
|
||||
store.getUserIdentity("dick"), notNullValue());
|
||||
assertThat("Failed to retrieve UserIdentity directly from PropertyUserStore", //
|
||||
store.getUserIdentity("harry"), notNullValue());
|
||||
assertThat("Failed to retrieve user directly from PropertyUserStore", //
|
||||
store.getUserPrincipal("tom"), notNullValue());
|
||||
assertThat("Failed to retrieve user directly from PropertyUserStore", //
|
||||
store.getUserPrincipal("dick"), notNullValue());
|
||||
assertThat("Failed to retrieve user directly from PropertyUserStore", //
|
||||
store.getUserPrincipal("harry"), notNullValue());
|
||||
userCount.assertThatCount(is(3));
|
||||
userCount.awaitCount(3);
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ public class PropertyUserStoreTest
|
|||
addAdditionalUser(usersFile, "skip: skip, roleA\n");
|
||||
userCount.awaitCount(4);
|
||||
assertThat(loadCount.get(), is(2));
|
||||
assertThat(store.getUserIdentity("skip"), notNullValue());
|
||||
assertThat(store.getUserPrincipal("skip"), notNullValue());
|
||||
userCount.assertThatCount(is(4));
|
||||
userCount.assertThatUsers(hasItem("skip"));
|
||||
|
||||
|
|
|
@ -44,26 +44,14 @@ public class TestLoginService extends AbstractLoginService
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String[] loadRoleInfo(UserPrincipal user)
|
||||
protected List<RolePrincipal> loadRoleInfo(UserPrincipal user)
|
||||
{
|
||||
UserIdentity userIdentity = userStore.getUserIdentity(user.getName());
|
||||
Set<RolePrincipal> roles = userIdentity.getSubject().getPrincipals(RolePrincipal.class);
|
||||
if (roles == null)
|
||||
return null;
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
for (RolePrincipal r : roles)
|
||||
{
|
||||
list.add(r.getName());
|
||||
}
|
||||
|
||||
return list.toArray(new String[roles.size()]);
|
||||
return userStore.getRolePrincipals(user.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserPrincipal loadUserInfo(String username)
|
||||
{
|
||||
UserIdentity userIdentity = userStore.getUserIdentity(username);
|
||||
return userIdentity == null ? null : (UserPrincipal)userIdentity.getUserPrincipal();
|
||||
return userStore.getUserPrincipal(username);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,7 @@
|
|||
package org.eclipse.jetty.security;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.util.security.Credential;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -44,30 +41,21 @@ public class UserStoreTest
|
|||
@Test
|
||||
public void addUser()
|
||||
{
|
||||
this.userStore.addUser("foo", Credential.getCredential("beer"), new String[]{"pub"});
|
||||
assertEquals(1, this.userStore.getKnownUserIdentities().size());
|
||||
UserIdentity userIdentity = this.userStore.getUserIdentity("foo");
|
||||
assertNotNull(userIdentity);
|
||||
assertEquals("foo", userIdentity.getUserPrincipal().getName());
|
||||
Set<AbstractLoginService.RolePrincipal>
|
||||
roles = userIdentity.getSubject().getPrincipals(AbstractLoginService.RolePrincipal.class);
|
||||
List<String> list = roles.stream()
|
||||
.map(rolePrincipal -> rolePrincipal.getName())
|
||||
.collect(Collectors.toList());
|
||||
assertEquals(1, list.size());
|
||||
assertEquals("pub", list.get(0));
|
||||
userStore.addUser("foo", Credential.getCredential("beer"), new String[]{"pub"});
|
||||
assertNotNull(userStore.getUserPrincipal("foo"));
|
||||
|
||||
List<RolePrincipal> rps = userStore.getRolePrincipals("foo");
|
||||
assertNotNull(rps);
|
||||
assertNotNull(rps.get(0));
|
||||
assertEquals("pub", rps.get(0).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeUser()
|
||||
{
|
||||
this.userStore.addUser("foo", Credential.getCredential("beer"), new String[]{"pub"});
|
||||
assertEquals(1, this.userStore.getKnownUserIdentities().size());
|
||||
UserIdentity userIdentity = this.userStore.getUserIdentity("foo");
|
||||
assertNotNull(userIdentity);
|
||||
assertEquals("foo", userIdentity.getUserPrincipal().getName());
|
||||
assertNotNull(userStore.getUserPrincipal("foo"));
|
||||
userStore.removeUser("foo");
|
||||
userIdentity = this.userStore.getUserIdentity("foo");
|
||||
assertNull(userIdentity);
|
||||
assertNull(userStore.getUserPrincipal("foo"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,13 @@ import java.net.Socket;
|
|||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
@ -43,6 +46,8 @@ 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.RolePrincipal;
|
||||
import org.eclipse.jetty.security.UserPrincipal;
|
||||
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
|
@ -85,7 +90,7 @@ public class DigestPostTest
|
|||
public static class TestLoginService extends AbstractLoginService
|
||||
{
|
||||
protected Map<String, UserPrincipal> users = new HashMap<>();
|
||||
protected Map<String, String[]> roles = new HashMap<>();
|
||||
protected Map<String, List<RolePrincipal>> roles = new HashMap<>();
|
||||
|
||||
public TestLoginService(String name)
|
||||
{
|
||||
|
@ -96,11 +101,12 @@ public class DigestPostTest
|
|||
{
|
||||
UserPrincipal userPrincipal = new UserPrincipal(username, credential);
|
||||
users.put(username, userPrincipal);
|
||||
roles.put(username, rolenames);
|
||||
if (rolenames != null)
|
||||
roles.put(username, Arrays.stream(rolenames).map(RolePrincipal::new).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] loadRoleInfo(UserPrincipal user)
|
||||
protected List<RolePrincipal> loadRoleInfo(UserPrincipal user)
|
||||
{
|
||||
return roles.get(user.getName());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue