This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch tomee-10.x in repository https://gitbox.apache.org/repos/asf/tomee.git
commit f7855760838823a3722a008d44baf2624dd59a3f Author: Richard Zowalla <[email protected]> AuthorDate: Tue Oct 13 08:47:30 2026 +0200 Do not map the caller principal to a role by name in getLogicalRoles (#3070) Realm-authenticated callers get roles from the realm only; group principals still map by name. (cherry picked from commit 6dbc78217a476843e587c4695f0730421dd9b133) --- .../tomee/catalina/TomcatSecurityService.java | 13 ++- .../TomcatSecurityServiceLogicalRolesTest.java | 104 +++++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatSecurityService.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatSecurityService.java index 92262daa2e..3575688363 100644 --- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatSecurityService.java +++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomcatSecurityService.java @@ -144,6 +144,17 @@ public class TomcatSecurityService extends AbstractSecurityService { @Override public Set<String> getLogicalRoles(final Principal[] principals, final Set<String> logicalRoles) { + // the caller's own principals are resolved through the realm only, never by name + final Set<Principal> callerPrincipals = new HashSet<>(); + for (final Principal principal : principals) { + if (principal instanceof TomcatUser user) { + callerPrincipals.add(user.getTomcatPrincipal()); + if (user.getTomcatPrincipal() instanceof GenericPrincipal genericPrincipal) { + callerPrincipals.add(genericPrincipal.getUserPrincipal()); + } + } + } + final Set<String> roles = new LinkedHashSet<>(logicalRoles.size()); for (final String logicalRole : logicalRoles) { for (final Principal principal : principals) { @@ -152,7 +163,7 @@ public class TomcatSecurityService extends AbstractSecurityService { roles.add(logicalRole); break; } - } else if (principal != null) { + } else if (principal != null && !callerPrincipals.contains(principal)) { final String name = principal.getName(); if (logicalRole.equals(name)) { roles.add(logicalRole); diff --git a/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/TomcatSecurityServiceLogicalRolesTest.java b/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/TomcatSecurityServiceLogicalRolesTest.java new file mode 100644 index 0000000000..0e86f59baf --- /dev/null +++ b/tomee/tomee-catalina/src/test/java/org/apache/tomee/catalina/TomcatSecurityServiceLogicalRolesTest.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomee.catalina; + +import org.apache.catalina.Wrapper; +import org.apache.catalina.realm.GenericPrincipal; +import org.apache.catalina.realm.RealmBase; +import org.junit.Test; +import org.mockito.Mockito; + +import java.security.Principal; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +public class TomcatSecurityServiceLogicalRolesTest { + + // getLogicalRoles uses no instance state, skip the constructor which needs a running server + private final TomcatSecurityService service = + Mockito.mock(TomcatSecurityService.class, Mockito.CALLS_REAL_METHODS); + + @Test + public void userNamedLikeRoleIsNotGrantedThatRole() { + final GenericPrincipal generic = new GenericPrincipal("admin", List.of("user"), new NamedPrincipal("admin")); + final Principal[] principals = { + generic, + new TomcatSecurityService.TomcatUser(new UserRoleRealm(), generic), + generic.getUserPrincipal() + }; + + assertEquals(Set.of("user"), service.getLogicalRoles(principals, Set.of("admin", "user"))); + } + + @Test + public void groupPrincipalIsMappedToRoleByName() { + final GenericPrincipal generic = new GenericPrincipal("bob", List.of("user"), new NamedPrincipal("bob")); + final Principal[] principals = { + generic, + new TomcatSecurityService.TomcatUser(new UserRoleRealm(), generic), + generic.getUserPrincipal(), + new GroupPrincipal("admin") + }; + + assertEquals(Set.of("admin", "user"), service.getLogicalRoles(principals, Set.of("admin", "user"))); + } + + @Test + public void groupPrincipalWithoutRealmIsMappedToRoleByName() { + final Principal[] principals = {new GroupPrincipal("admin")}; + + assertEquals(Set.of("admin"), service.getLogicalRoles(principals, Set.of("admin", "user"))); + } + + private static class NamedPrincipal implements Principal { + private final String name; + + NamedPrincipal(final String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + } + + private static class GroupPrincipal extends NamedPrincipal { + GroupPrincipal(final String name) { + super(name); + } + } + + public static class UserRoleRealm extends RealmBase { + @Override + public boolean hasRole(final Wrapper wrapper, final Principal principal, final String role) { + return "user".equals(role); + } + + @Override + protected String getPassword(final String username) { + return null; + } + + @Override + protected Principal getPrincipal(final String username) { + return null; + } + } +}
