diff --git a/demos/demo-jaas-webapp/src/main/config/modules/demo-jaas.mod b/demos/demo-jaas-webapp/src/main/config/modules/demo-jaas.mod index a27bf23cd1a..8c97d60007c 100644 --- a/demos/demo-jaas-webapp/src/main/config/modules/demo-jaas.mod +++ b/demos/demo-jaas-webapp/src/main/config/modules/demo-jaas.mod @@ -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 \ No newline at end of file +jetty.jaas.login.conf?=etc/demo-login.conf diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java index f29ab99de88..a6d17256e9d 100644 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java +++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASLoginService.java @@ -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; } diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASUserPrincipal.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASUserPrincipal.java index 9eb707f2d39..33a1f4a8033 100644 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASUserPrincipal.java +++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/JAASUserPrincipal.java @@ -26,7 +26,7 @@ import javax.security.auth.login.LoginContext; * JAASUserPrincipal *

* Implements the JAAS version of the - * org.eclipse.jetty.http.UserPrincipal interface. + * org.eclipse.jetty.security.UserPrincipal interface. */ public class JAASUserPrincipal implements Principal { diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java index acd04e7a90b..b23249cba1b 100644 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java +++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractDatabaseLoginModule.java @@ -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))); } } diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java index 2bbb253b7f3..eb3f18f3d37 100644 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java +++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java @@ -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 roles; - - public JAASUserInfo(UserInfo u) + private final UserPrincipal _user; + private List _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(); - if (this.user.getRoleNames() != null) - { - Iterator itor = this.user.getRoleNames().iterator(); - while (itor.hasNext()) - { - this.roles.add(new JAASRole((String)itor.next())); - } - } + List rolenames = doFetchRoles(); + if (rolenames != null) + _roles = rolenames.stream().map(JAASRole::new).collect(Collectors.toList()); } + + public abstract List 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()) diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java index 8b4798f38a0..ed60381c8a0 100644 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java +++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/LdapLoginModule.java @@ -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 doFetchRoles() throws Exception + { + return getUserRolesByDn(_context, _userDn); + } + } + /** * get the available information about the user *

@@ -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 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) diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java index 63ca7289b2a..0025cce5e35 100644 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java +++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java @@ -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 principals = userIdentity.getSubject().getPrincipals(AbstractLoginService.RolePrincipal.class); - - List 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 rps = _store.getRolePrincipals(userName); + List roles = rps == null ? Collections.emptyList() : rps.stream().map(RolePrincipal::getName).collect(Collectors.toList()); + return new JAASUser(up) + { + @Override + public List doFetchRoles() + { + return roles; + } + }; } } diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/UserInfo.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/UserInfo.java deleted file mode 100644 index 6b2a9a64e43..00000000000 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/UserInfo.java +++ /dev/null @@ -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 _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 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 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 getRoleNames() - { - return Collections.unmodifiableList(_roleNames); - } - - public boolean checkCredential(Object suppliedCredential) - { - return _credential.check(suppliedCredential); - } - - protected Credential getCredential() - { - return _credential; - } -} diff --git a/jetty-jaas/src/test/java/org/eclipse/jetty/jaas/TestLoginModule.java b/jetty-jaas/src/test/java/org/eclipse/jetty/jaas/TestLoginModule.java index 20868dbe870..187c1d3049f 100644 --- a/jetty-jaas/src/test/java/org/eclipse/jetty/jaas/TestLoginModule.java +++ b/jetty-jaas/src/test/java/org/eclipse/jetty/jaas/TestLoginModule.java @@ -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 doFetchRoles() throws Exception + { + return Collections.emptyList(); + } + }; } @Override diff --git a/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java index 6dbc96c7840..e2a7b6fcfa5 100644 --- a/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java +++ b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/JaspiTest.java @@ -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 _users = new HashMap<>(); - protected Map _roles = new HashMap(); + protected Map> _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 rps = Arrays.stream(roles).map(RolePrincipal::new).collect(Collectors.toList()); + _roles.put(username, rps); + } } @Override - protected String[] loadRoleInfo(UserPrincipal user) + protected List loadRoleInfo(UserPrincipal user) { return _roles.get(user.getName()); } diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java index 5bf7a507484..9670f2322ad 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/security/DataSourceLoginService.java @@ -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 *

- * 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 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()); } } } diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/AbstractLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/AbstractLoginService.java index 1f1152d35a6..6649cffbd2a 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/AbstractLoginService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/AbstractLoginService.java @@ -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 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 roles = loadRoleInfo(userPrincipal); + List 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() diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java index c39d7d8d532..846ab8d43a6 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/HashLoginService.java @@ -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. - *

- * An implementation of UserRealm that stores users and roles in-memory in HashMaps. - *

- * 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: *

  *  username: password [,rolename ...]
  * 
@@ -72,7 +65,7 @@ public class HashLoginService extends AbstractLoginService } /** - * Load realm users from properties file. + * Load users from properties file. *

* The property file maps usernames to password specs followed by an optional comma separated list of role names. *

@@ -121,41 +114,21 @@ public class HashLoginService extends AbstractLoginService } @Override - protected String[] loadRoleInfo(UserPrincipal user) + protected List loadRoleInfo(UserPrincipal user) { - UserIdentity id = _userStore.getUserIdentity(user.getName()); - if (id == null) - return null; - - Set roles = id.getSubject().getPrincipals(RolePrincipal.class); - if (roles == null) - return null; - - List 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. */ diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java b/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java index 828ce13dd12..828089eb959 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/JDBCLoginService.java @@ -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. - *

- * An example properties file for configuration is in - * ${jetty.home}/etc/jdbcRealm.properties + * 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 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 roles = new ArrayList(); @@ -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(); diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java index 0fb47ec1698..f692abfef18 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java @@ -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 currentlyKnownUsers = new ArrayList<>(getKnownUserIdentities().keySet()); + List currentlyKnownUsers = new ArrayList<>(_users.keySet()); // if its not the initial load then we want to process removed users if (!_firstLoad) { diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/RolePrincipal.java b/jetty-security/src/main/java/org/eclipse/jetty/security/RolePrincipal.java new file mode 100644 index 00000000000..46bcba5b0a4 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/RolePrincipal.java @@ -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); + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/UserPrincipal.java b/jetty-security/src/main/java/org/eclipse/jetty/security/UserPrincipal.java new file mode 100644 index 00000000000..a4946c1aed2 --- /dev/null +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/UserPrincipal.java @@ -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; + } +} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/UserStore.java b/jetty-security/src/main/java/org/eclipse/jetty/security/UserStore.java index 9efde97029e..6cb78971896 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/UserStore.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/UserStore.java @@ -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 _knownUserIdentities = new ConcurrentHashMap<>(); + protected final Map _users = new ConcurrentHashMap<>(); + + protected class User + { + protected UserPrincipal _userPrincipal; + protected List _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 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 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 getKnownUserIdentities() - { - return _knownUserIdentities; + return String.format("%s@%x[users.count=%d]", getClass().getSimpleName(), hashCode(), _users.size()); } } diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java index 9feba87321b..9651b81bcf0 100644 --- a/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/PropertyUserStoreTest.java @@ -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")); diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java b/jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java index d54b1dd8dc1..00395840df6 100644 --- a/jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java @@ -44,26 +44,14 @@ public class TestLoginService extends AbstractLoginService } @Override - protected String[] loadRoleInfo(UserPrincipal user) + protected List loadRoleInfo(UserPrincipal user) { - UserIdentity userIdentity = userStore.getUserIdentity(user.getName()); - Set roles = userIdentity.getSubject().getPrincipals(RolePrincipal.class); - if (roles == null) - return null; - - List 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); } } diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/UserStoreTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/UserStoreTest.java index fdd14c2d825..5fef57475df 100644 --- a/jetty-security/src/test/java/org/eclipse/jetty/security/UserStoreTest.java +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/UserStoreTest.java @@ -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 - roles = userIdentity.getSubject().getPrincipals(AbstractLoginService.RolePrincipal.class); - List 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 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")); } } diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java index f69b273b8c2..9192394a540 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DigestPostTest.java @@ -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 users = new HashMap<>(); - protected Map roles = new HashMap<>(); + protected Map> 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 loadRoleInfo(UserPrincipal user) { return roles.get(user.getName()); }