diff --git a/java/org/apache/catalina/realm/UserDatabaseRealm.java b/java/org/apache/catalina/realm/UserDatabaseRealm.java index bffb978a90e0..9d69540931c6 100644 --- a/java/org/apache/catalina/realm/UserDatabaseRealm.java +++ b/java/org/apache/catalina/realm/UserDatabaseRealm.java @@ -17,7 +17,7 @@ package org.apache.catalina.realm; import java.security.Principal; -import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -29,7 +29,6 @@ import org.apache.catalina.Role; import org.apache.catalina.User; import org.apache.catalina.UserDatabase; -import org.apache.catalina.Wrapper; import org.apache.naming.ContextBindings; import org.apache.tomcat.util.ExceptionUtils; @@ -113,69 +112,6 @@ public void setLocalJndiResource(boolean localJndiResource) { } - // --------------------------------------------------------- Public Methods - - /** - * Return true if the specified Principal has the specified - * security role, within the context of this Realm; otherwise return - * false. This implementation returns true if the - * User has the role, or if any Group that the - * User is a member of has the role. - * - * @param principal Principal for whom the role is to be checked - * @param role Security role to be checked - */ - @Override - public boolean hasRole(Wrapper wrapper, Principal principal, String role) { - - UserDatabase database = getUserDatabase(); - if (database == null) { - return false; - } - - // Check for a role alias defined in a element - if (wrapper != null) { - String realRole = wrapper.findSecurityReference(role); - if (realRole != null) { - role = realRole; - } - } - if (principal instanceof GenericPrincipal) { - GenericPrincipal gp = (GenericPrincipal) principal; - if (gp.getUserPrincipal() instanceof UserDatabasePrincipal) { - principal = database.findUser(gp.getName()); - } - } - if (!(principal instanceof User)) { - // Play nice with SSO and mixed Realms - // No need to pass the wrapper here because role mapping has been - // performed already a few lines above - return super.hasRole(null, principal, role); - } - if ("*".equals(role)) { - return true; - } else if (role == null) { - return false; - } - User user = (User) principal; - Role dbrole = database.findRole(role); - if (dbrole == null) { - return false; - } - if (user.isInRole(dbrole)) { - return true; - } - Iterator groups = user.getGroups(); - while (groups.hasNext()) { - Group group = groups.next(); - if (group.isInRole(dbrole)) { - return true; - } - } - return false; - } - - // ------------------------------------------------------ Protected Methods @Override @@ -212,32 +148,7 @@ protected String getPassword(String username) { */ @Override protected Principal getPrincipal(String username) { - UserDatabase database = getUserDatabase(); - if (database == null) { - return null; - } - - User user = database.findUser(username); - if (user == null) { - return null; - } - - Set roles = new HashSet<>(); - Iterator uroles = user.getRoles(); - while (uroles.hasNext()) { - Role role = uroles.next(); - roles.add(role.getName()); - } - Iterator groups = user.getGroups(); - while (groups.hasNext()) { - Group group = groups.next(); - uroles = group.getRoles(); - while (uroles.hasNext()) { - Role role = uroles.next(); - roles.add(role.getName()); - } - } - return new GenericPrincipal(username, new ArrayList<>(roles), new UserDatabasePrincipal(username)); + return new UserDatabasePrincipal(username); } @@ -261,7 +172,9 @@ private UserDatabase getUserDatabase() { database = (UserDatabase) context.lookup(resourceName); } catch (Throwable e) { ExceptionUtils.handleThrowable(e); - containerLog.error(sm.getString("userDatabaseRealm.lookup", resourceName), e); + if (containerLog != null) { + containerLog.error(sm.getString("userDatabaseRealm.lookup", resourceName), e); + } database = null; } } @@ -308,14 +221,65 @@ protected void stopInternal() throws LifecycleException { } - private static class UserDatabasePrincipal implements Principal { - private final String name; - private UserDatabasePrincipal(String name) { - this.name = name; + public final class UserDatabasePrincipal extends GenericPrincipal { + private static final long serialVersionUID = 1L; + private final User user; + + public UserDatabasePrincipal(String username) { + super(username); + UserDatabase database = getUserDatabase(); + if (database == null) { + user = null; + } else { + user = database.findUser(username); + } + } + + @Override + public String[] getRoles() { + if (user == null) { + return super.getRoles(); + } + Set roles = new HashSet<>(); + Iterator uroles = user.getRoles(); + while (uroles.hasNext()) { + Role role = uroles.next(); + roles.add(role.getName()); + } + Iterator groups = user.getGroups(); + while (groups.hasNext()) { + Group group = groups.next(); + uroles = group.getRoles(); + while (uroles.hasNext()) { + Role role = uroles.next(); + roles.add(role.getName()); + } + } + return roles.toArray(new String[0]); } + @Override - public String getName() { - return name; + public boolean hasRole(String role) { + if (user == null) { + return super.hasRole(role); + } + if ("*".equals(role)) { + return true; + } else if (role == null) { + return false; + } + Role dbrole = database.findRole(role); + if (dbrole == null) { + return false; + } + return user.isInRole(dbrole); } + + private Object writeReplace() { + // Replace with a static principal disconnected from the database + return new GenericPrincipal(getName(), Arrays.asList(getRoles())); + } + } + } diff --git a/test/org/apache/catalina/realm/TestGenericPrincipal.java b/test/org/apache/catalina/realm/TestGenericPrincipal.java index 5a994156e6fa..b2ac92feddb8 100644 --- a/test/org/apache/catalina/realm/TestGenericPrincipal.java +++ b/test/org/apache/catalina/realm/TestGenericPrincipal.java @@ -56,6 +56,13 @@ public void testSerialize03() throws ClassNotFoundException, IOException { doTest(gpIn); } + @Test + public void testSerialize04() throws ClassNotFoundException, IOException { + UserDatabaseRealm realm = new UserDatabaseRealm(); + GenericPrincipal gpIn = realm.new UserDatabasePrincipal(USER); + doTest(gpIn); + } + private void doTest(GenericPrincipal gpIn) throws ClassNotFoundException, IOException { GenericPrincipal gpOut = serializeAndDeserialize(gpIn); diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index a26cc8c438de..993e0b76d3c0 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -160,6 +160,10 @@ AprLifecycleListener does not show dev version suffix for libtcnative and libapr. (michaelo) + + Refactor principal handling in UserDatabaseRealm using + an inner class that extends GenericPrincipal. (remm) + Ignore duplicates when collecting the effective roles list from Roles and Groups in UserDatabaseRealm.getPrincipal(String).