Skip to content

Commit b404e1e

Browse files
authored
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.
1 parent df3f6ca commit b404e1e

2 files changed

Lines changed: 103 additions & 8 deletions

File tree

activemq-broker/src/main/java/org/apache/activemq/security/SecurityContext.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,21 @@ public SecurityContext(String userName) {
5454
}
5555

5656
public boolean isInOneOf(Set<?> allowedPrincipals) {
57-
Iterator<?> allowedIter = allowedPrincipals.iterator();
58-
HashSet<?> userPrincipals = new HashSet<Object>(getPrincipals());
59-
while (allowedIter.hasNext()) {
60-
Iterator<?> userIter = userPrincipals.iterator();
61-
Object allowedPrincipal = allowedIter.next();
62-
while (userIter.hasNext()) {
63-
if (allowedPrincipal.equals(userIter.next()))
64-
return true;
57+
for (Object allowedPrincipal : allowedPrincipals) {
58+
if (contains(allowedPrincipal)) {
59+
return true;
6560
}
6661
}
6762
return false;
6863
}
6964

7065
public abstract Set<Principal> getPrincipals();
7166

67+
public boolean contains(Object principal) {
68+
Set<Principal> principals = getPrincipals();
69+
return principals != null && principals.contains(principal);
70+
}
71+
7272
public String getUserName() {
7373
return userName;
7474
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.activemq.security;
18+
19+
import static org.apache.activemq.security.SecurityContextTest.TestPrincipal.testPrincipal;
20+
import static org.junit.Assert.assertFalse;
21+
import static org.junit.Assert.assertTrue;
22+
23+
import java.security.Principal;
24+
import java.util.Objects;
25+
import java.util.Set;
26+
import org.junit.Test;
27+
28+
public class SecurityContextTest {
29+
30+
@Test
31+
public void testIsOneOf() {
32+
SecurityContext context = newContext(Set.of(testPrincipal("one"),
33+
testPrincipal("two"), testPrincipal("three")));
34+
35+
// test valid combos
36+
assertTrue(context.isInOneOf(Set.of(testPrincipal("one"))));
37+
assertTrue(context.isInOneOf(Set.of(testPrincipal("two"))));
38+
assertTrue(context.isInOneOf(Set.of(testPrincipal("three"))));
39+
assertTrue(context.isInOneOf(Set.of(testPrincipal("three"),
40+
testPrincipal("four"), testPrincipal("five"))));
41+
42+
// test no matching
43+
assertFalse(context.isInOneOf(Set.of(testPrincipal("four"),
44+
testPrincipal("five"))));
45+
assertFalse(context.isInOneOf(Set.of()));
46+
// different impl types, should not find
47+
assertFalse(context.isInOneOf(Set.of((Principal) () -> "one")));
48+
49+
// empty set
50+
context = newContext(Set.of());
51+
assertFalse(context.isInOneOf(Set.of(testPrincipal("one"))));
52+
assertFalse(context.isInOneOf(Set.of()));
53+
}
54+
55+
private SecurityContext newContext(Set<Principal> principals) {
56+
return new SecurityContext("user") {
57+
@Override
58+
public Set<Principal> getPrincipals() {
59+
return principals;
60+
}
61+
};
62+
}
63+
64+
static class TestPrincipal implements Principal {
65+
private final String name;
66+
67+
private TestPrincipal(String name) {
68+
this.name = name;
69+
}
70+
71+
@Override
72+
public String getName() {
73+
return name;
74+
}
75+
76+
@Override
77+
public boolean equals(Object o) {
78+
if (o == null || getClass() != o.getClass()) {
79+
return false;
80+
}
81+
TestPrincipal that = (TestPrincipal) o;
82+
return Objects.equals(name, that.name);
83+
}
84+
85+
@Override
86+
public int hashCode() {
87+
return Objects.hashCode(name);
88+
}
89+
90+
static TestPrincipal testPrincipal(String name) {
91+
return new TestPrincipal(name);
92+
}
93+
}
94+
95+
}

0 commit comments

Comments
 (0)