This is an automated email from the ASF dual-hosted git repository.

cshannon pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq.git


The following commit(s) were added to refs/heads/main by this push:
     new b404e1e335 Optimize SecurityContext#isInOneOf (#1874)
b404e1e335 is described below

commit b404e1e335011af93ff3c40318bcfc2fc2800660
Author: Christopher L. Shannon <[email protected]>
AuthorDate: Fri Apr 3 18:20:25 2026 -0400

    Optimize SecurityContext#isInOneOf (#1874)
    
    This method was iterating over a set to find matching entries instead of
    just using contains(). This updates switches to using contains() which
    will be faster and also cleans up the code with a for each loop.
    This also removes the copying of the principal set set copy that is
    unnecessary and actually not thread safe without external sync when using
    Jaas.
    
    A small test was added but there are several other authorization tests
    that already exist that also exercise this method.
---
 .../apache/activemq/security/SecurityContext.java  | 16 ++--
 .../activemq/security/SecurityContextTest.java     | 95 ++++++++++++++++++++++
 2 files changed, 103 insertions(+), 8 deletions(-)

diff --git 
a/activemq-broker/src/main/java/org/apache/activemq/security/SecurityContext.java
 
b/activemq-broker/src/main/java/org/apache/activemq/security/SecurityContext.java
index fd677ce590..f24b6ffc3f 100644
--- 
a/activemq-broker/src/main/java/org/apache/activemq/security/SecurityContext.java
+++ 
b/activemq-broker/src/main/java/org/apache/activemq/security/SecurityContext.java
@@ -54,14 +54,9 @@ public abstract class SecurityContext {
     }
 
     public boolean isInOneOf(Set<?> allowedPrincipals) {
-        Iterator<?> allowedIter = allowedPrincipals.iterator();
-        HashSet<?> userPrincipals = new HashSet<Object>(getPrincipals());
-        while (allowedIter.hasNext()) {
-            Iterator<?> userIter = userPrincipals.iterator();
-            Object allowedPrincipal = allowedIter.next();
-            while (userIter.hasNext()) {
-                if (allowedPrincipal.equals(userIter.next()))
-                    return true;
+        for (Object allowedPrincipal : allowedPrincipals) {
+            if (contains(allowedPrincipal)) {
+                return true;
             }
         }
         return false;
@@ -69,6 +64,11 @@ public abstract class SecurityContext {
 
     public abstract Set<Principal> getPrincipals();
 
+    public boolean contains(Object principal) {
+        Set<Principal> principals = getPrincipals();
+        return principals != null && principals.contains(principal);
+    }
+
     public String getUserName() {
         return userName;
     }
diff --git 
a/activemq-broker/src/test/java/org/apache/activemq/security/SecurityContextTest.java
 
b/activemq-broker/src/test/java/org/apache/activemq/security/SecurityContextTest.java
new file mode 100644
index 0000000000..51ffb95d81
--- /dev/null
+++ 
b/activemq-broker/src/test/java/org/apache/activemq/security/SecurityContextTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.activemq.security;
+
+import static 
org.apache.activemq.security.SecurityContextTest.TestPrincipal.testPrincipal;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.security.Principal;
+import java.util.Objects;
+import java.util.Set;
+import org.junit.Test;
+
+public class SecurityContextTest {
+
+    @Test
+    public void testIsOneOf() {
+        SecurityContext context = newContext(Set.of(testPrincipal("one"),
+                testPrincipal("two"), testPrincipal("three")));
+
+        // test valid combos
+        assertTrue(context.isInOneOf(Set.of(testPrincipal("one"))));
+        assertTrue(context.isInOneOf(Set.of(testPrincipal("two"))));
+        assertTrue(context.isInOneOf(Set.of(testPrincipal("three"))));
+        assertTrue(context.isInOneOf(Set.of(testPrincipal("three"),
+                testPrincipal("four"), testPrincipal("five"))));
+
+        // test no matching
+        assertFalse(context.isInOneOf(Set.of(testPrincipal("four"),
+                testPrincipal("five"))));
+        assertFalse(context.isInOneOf(Set.of()));
+        // different impl types, should not find
+        assertFalse(context.isInOneOf(Set.of((Principal) () -> "one")));
+
+        // empty set
+        context = newContext(Set.of());
+        assertFalse(context.isInOneOf(Set.of(testPrincipal("one"))));
+        assertFalse(context.isInOneOf(Set.of()));
+    }
+
+    private SecurityContext newContext(Set<Principal> principals) {
+        return new SecurityContext("user") {
+            @Override
+            public Set<Principal> getPrincipals() {
+                return principals;
+            }
+        };
+    }
+
+    static class TestPrincipal implements Principal {
+        private final String name;
+
+        private TestPrincipal(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            TestPrincipal that = (TestPrincipal) o;
+            return Objects.equals(name, that.name);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(name);
+        }
+
+        static TestPrincipal testPrincipal(String name) {
+            return new TestPrincipal(name);
+        }
+    }
+
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact


Reply via email to