Skip to content

Commit 1d31ea2

Browse files
authored
Entitle com.unboundid.ldap.listener as test package (#130706) (#130820)
1 parent 3d36017 commit 1d31ea2

File tree

7 files changed

+117
-8
lines changed

7 files changed

+117
-8
lines changed

test/framework/src/main/java/org/elasticsearch/entitlement/bootstrap/TestEntitlementBootstrap.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ public static void setTriviallyAllowingTestCode(boolean newValue) {
8787
policyManager.setTriviallyAllowingTestCode(newValue);
8888
}
8989

90+
public static void setEntitledTestPackages(String[] entitledTestPackages) {
91+
policyManager.setEntitledTestPackages(entitledTestPackages);
92+
}
93+
9094
public static void reset() {
9195
if (policyManager != null) {
9296
policyManager.reset();

test/framework/src/main/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManager.java

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.entitlement.runtime.policy;
1111

12+
import org.elasticsearch.common.util.ArrayUtils;
1213
import org.elasticsearch.entitlement.runtime.policy.entitlements.Entitlement;
1314
import org.elasticsearch.test.ESTestCase;
1415

@@ -17,6 +18,7 @@
1718
import java.nio.file.Path;
1819
import java.security.CodeSource;
1920
import java.security.ProtectionDomain;
21+
import java.util.Arrays;
2022
import java.util.Collection;
2123
import java.util.List;
2224
import java.util.Map;
@@ -29,6 +31,7 @@ public class TestPolicyManager extends PolicyManager {
2931

3032
boolean isActive;
3133
boolean isTriviallyAllowingTestCode;
34+
String[] entitledTestPackages = TEST_FRAMEWORK_PACKAGE_PREFIXES;
3235

3336
/**
3437
* We don't have modules in tests, so we can't use the inherited map of entitlements per module.
@@ -60,6 +63,16 @@ public void setTriviallyAllowingTestCode(boolean newValue) {
6063
this.isTriviallyAllowingTestCode = newValue;
6164
}
6265

66+
public void setEntitledTestPackages(String... entitledTestPackages) {
67+
assertNoRedundantPrefixes(TEST_FRAMEWORK_PACKAGE_PREFIXES, entitledTestPackages, false);
68+
if (entitledTestPackages.length > 1) {
69+
assertNoRedundantPrefixes(entitledTestPackages, entitledTestPackages, true);
70+
}
71+
String[] packages = ArrayUtils.concat(this.entitledTestPackages, entitledTestPackages);
72+
Arrays.sort(packages);
73+
this.entitledTestPackages = packages;
74+
}
75+
6376
/**
6477
* Called between tests so each test is not affected by prior tests
6578
*/
@@ -110,19 +123,47 @@ private boolean isEntitlementClass(Class<?> requestingClass) {
110123
&& (requestingClass.getName().contains("Test") == false);
111124
}
112125

113-
@Deprecated // TODO: reevaluate whether we want this.
114-
// If we can simply check for dependencies the gradle worker has that aren't
115-
// declared in the gradle config (namely org.gradle) that would be simpler.
116126
private boolean isTestFrameworkClass(Class<?> requestingClass) {
117-
String packageName = requestingClass.getPackageName();
118-
for (String prefix : TEST_FRAMEWORK_PACKAGE_PREFIXES) {
119-
if (packageName.startsWith(prefix)) {
127+
return isTestFrameworkClass(entitledTestPackages, requestingClass.getPackageName());
128+
}
129+
130+
// no redundant entries allowed, see assertNoRedundantPrefixes
131+
static boolean isTestFrameworkClass(String[] sortedPrefixes, String packageName) {
132+
int idx = Arrays.binarySearch(sortedPrefixes, packageName);
133+
if (idx >= 0) {
134+
return true;
135+
}
136+
idx = -idx - 2; // candidate package index (insertion point - 1)
137+
if (idx >= 0 && idx < sortedPrefixes.length) {
138+
String candidate = sortedPrefixes[idx];
139+
if (packageName.startsWith(candidate)
140+
&& (packageName.length() == candidate.length() || packageName.charAt(candidate.length()) == '.')) {
120141
return true;
121142
}
122143
}
123144
return false;
124145
}
125146

147+
private static boolean isNotPrefixMatch(String name, String prefix, boolean discardExactMatch) {
148+
assert prefix.endsWith(".") == false : "Invalid package prefix ending with '.' [" + prefix + "]";
149+
if (name == prefix || name.startsWith(prefix)) {
150+
if (name.length() == prefix.length()) {
151+
return discardExactMatch;
152+
}
153+
return false == (name.length() > prefix.length() && name.charAt(prefix.length()) == '.');
154+
}
155+
return true;
156+
}
157+
158+
static void assertNoRedundantPrefixes(String[] setA, String[] setB, boolean discardExactMatch) {
159+
for (String a : setA) {
160+
for (String b : setB) {
161+
assert isNotPrefixMatch(a, b, discardExactMatch) && isNotPrefixMatch(b, a, discardExactMatch)
162+
: "Redundant prefix entries: [" + a + ", " + b + "]";
163+
}
164+
}
165+
}
166+
126167
private boolean isTestCode(Class<?> requestingClass) {
127168
// TODO: Cache this? It's expensive
128169
for (Class<?> candidate = requireNonNull(requestingClass); candidate != null; candidate = candidate.getDeclaringClass()) {
@@ -163,6 +204,10 @@ private boolean isTestCode(Class<?> requestingClass) {
163204
"org.bouncycastle.jsse.provider" // Used in test code if FIPS is enabled, support more fine-grained config in ES-12128
164205
};
165206

207+
static {
208+
Arrays.sort(TEST_FRAMEWORK_PACKAGE_PREFIXES);
209+
}
210+
166211
@Override
167212
protected ModuleEntitlements getEntitlements(Class<?> requestingClass) {
168213
return classEntitlementsMap.computeIfAbsent(requestingClass, this::computeEntitlements);

test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,16 +513,30 @@ protected void afterIfSuccessful() throws Exception {}
513513
public @interface WithEntitlementsOnTestCode {
514514
}
515515

516+
@Retention(RetentionPolicy.RUNTIME)
517+
@Target(ElementType.TYPE)
518+
@Inherited
519+
public @interface EntitledTestPackages {
520+
String[] value();
521+
}
522+
516523
@BeforeClass
517524
public static void setupEntitlementsForClass() {
518525
boolean withoutEntitlements = getTestClass().isAnnotationPresent(WithoutEntitlements.class);
519526
boolean withEntitlementsOnTestCode = getTestClass().isAnnotationPresent(WithEntitlementsOnTestCode.class);
527+
EntitledTestPackages entitledPackages = getTestClass().getAnnotation(EntitledTestPackages.class);
528+
520529
if (TestEntitlementBootstrap.isEnabledForTest()) {
521530
TestEntitlementBootstrap.setActive(false == withoutEntitlements);
522531
TestEntitlementBootstrap.setTriviallyAllowingTestCode(false == withEntitlementsOnTestCode);
532+
if (entitledPackages != null) {
533+
assert withEntitlementsOnTestCode == false : "Cannot use @WithEntitlementsOnTestCode together with @EntitledTestPackages";
534+
assert entitledPackages.value().length > 0 : "No test packages specified in @EntitledTestPackages";
535+
TestEntitlementBootstrap.setEntitledTestPackages(entitledPackages.value());
536+
}
523537
} else if (withEntitlementsOnTestCode) {
524538
throw new AssertionError(
525-
"Cannot use WithEntitlementsOnTestCode on tests that are not configured to use entitlements for testing"
539+
"Cannot use @WithEntitlementsOnTestCode on tests that are not configured to use entitlements for testing"
526540
);
527541
}
528542
}

test/framework/src/test/java/org/elasticsearch/entitlement/runtime/policy/TestPolicyManagerTests.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@
1313
import org.elasticsearch.test.ESTestCase;
1414
import org.junit.Before;
1515

16+
import java.util.Arrays;
1617
import java.util.List;
1718
import java.util.Map;
1819
import java.util.concurrent.atomic.AtomicInteger;
1920

2021
import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ComponentKind.PLUGIN;
22+
import static org.hamcrest.Matchers.both;
23+
import static org.hamcrest.Matchers.containsString;
24+
import static org.hamcrest.Matchers.equalTo;
2125

2226
public class TestPolicyManagerTests extends ESTestCase {
2327
TestPolicyManager policyManager;
@@ -60,4 +64,43 @@ public void testIsTriviallyAllowed() {
6064
policyManager.setTriviallyAllowingTestCode(false);
6165
assertFalse(policyManager.isTriviallyAllowed(getClass()));
6266
}
67+
68+
public void testDefaultEntitledTestPackages() {
69+
String[] testPackages = policyManager.entitledTestPackages.clone();
70+
TestPolicyManager.assertNoRedundantPrefixes(testPackages, testPackages, true);
71+
72+
Arrays.sort(testPackages);
73+
assertThat("Entitled test framework packages are not sorted", policyManager.entitledTestPackages, equalTo(testPackages));
74+
}
75+
76+
public void testRejectSetRedundantEntitledTestPackages() {
77+
var throwable = expectThrows(AssertionError.class, () -> policyManager.setEntitledTestPackages("org.apache.lucene.tests"));
78+
var baseMatcher = both(containsString("Redundant prefix entries"));
79+
assertThat(throwable.getMessage(), baseMatcher.and(containsString("org.apache.lucene.tests, org.apache.lucene.tests")));
80+
81+
throwable = expectThrows(AssertionError.class, () -> policyManager.setEntitledTestPackages("org.apache.lucene"));
82+
assertThat(throwable.getMessage(), baseMatcher.and(containsString("org.apache.lucene.tests, org.apache.lucene")));
83+
84+
throwable = expectThrows(AssertionError.class, () -> policyManager.setEntitledTestPackages("org.apache.lucene.tests.whatever"));
85+
assertThat(throwable.getMessage(), baseMatcher.and(containsString("org.apache.lucene.tests, org.apache.lucene.tests.whatever")));
86+
87+
throwable = expectThrows(AssertionError.class, () -> policyManager.setEntitledTestPackages("my.package", "my.package.sub"));
88+
assertThat(throwable.getMessage(), baseMatcher.and(containsString("my.package, my.package.sub")));
89+
90+
throwable = expectThrows(AssertionError.class, () -> policyManager.setEntitledTestPackages("trailing.dot."));
91+
assertThat(throwable.getMessage(), containsString("Invalid package prefix ending with '.' [trailing.dot.]"));
92+
}
93+
94+
public void testIsTestFrameworkClass() {
95+
String[] sortedPrefixes = { "a.b", "a.bc", "a.c" };
96+
97+
assertTrue(TestPolicyManager.isTestFrameworkClass(sortedPrefixes, "a.b"));
98+
assertTrue(TestPolicyManager.isTestFrameworkClass(sortedPrefixes, "a.b.c"));
99+
assertTrue(TestPolicyManager.isTestFrameworkClass(sortedPrefixes, "a.bc"));
100+
assertTrue(TestPolicyManager.isTestFrameworkClass(sortedPrefixes, "a.bc.a"));
101+
102+
assertFalse(TestPolicyManager.isTestFrameworkClass(sortedPrefixes, "a"));
103+
assertFalse(TestPolicyManager.isTestFrameworkClass(sortedPrefixes, "a.ba"));
104+
assertFalse(TestPolicyManager.isTestFrameworkClass(sortedPrefixes, "a.bcc"));
105+
}
63106
}

x-pack/plugin/core/src/main/plugin-metadata/entitlement-policy.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ org.apache.httpcomponents.httpasyncclient:
1818
- manage_threads
1919
unboundid.ldapsdk:
2020
- set_https_connection_properties # TODO: review if we need this once we have proper test coverage
21-
- inbound_network # For com.unboundid.ldap.listener.LDAPListener
2221
- outbound_network
2322
- manage_threads
2423
- write_system_properties:

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.script.ScriptService;
3434
import org.elasticsearch.script.mustache.MustacheScriptEngine;
3535
import org.elasticsearch.test.ESTestCase;
36+
import org.elasticsearch.test.ESTestCase.EntitledTestPackages;
3637
import org.elasticsearch.threadpool.TestThreadPool;
3738
import org.elasticsearch.threadpool.ThreadPool;
3839
import org.elasticsearch.watcher.ResourceWatcherService;
@@ -106,6 +107,7 @@
106107
* The username used to authenticate then has to be in the form of CN=user. Finally the username needs to be added as an
107108
* additional bind DN with a password in the test setup since it really is not a DN in the ldif file
108109
*/
110+
@EntitledTestPackages(value = { "com.unboundid.ldap.listener" }) // tests start LDAP server that listens for incoming connections
109111
public class ActiveDirectoryRealmTests extends ESTestCase {
110112

111113
private static final String PASSWORD = "password";

x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.core.TimeValue;
3030
import org.elasticsearch.env.TestEnvironment;
3131
import org.elasticsearch.test.ESTestCase;
32+
import org.elasticsearch.test.ESTestCase.EntitledTestPackages;
3233
import org.elasticsearch.watcher.ResourceWatcherService;
3334
import org.elasticsearch.xpack.core.XPackSettings;
3435
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
@@ -73,6 +74,7 @@
7374
import static org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings.URLS_SETTING;
7475
import static org.hamcrest.Matchers.is;
7576

77+
@EntitledTestPackages(value = { "com.unboundid.ldap.listener" }) // tests start LDAP server that listens for incoming connections
7678
public abstract class LdapTestCase extends ESTestCase {
7779

7880
protected static final RealmConfig.RealmIdentifier REALM_IDENTIFIER = new RealmConfig.RealmIdentifier("ldap", "ldap1");

0 commit comments

Comments
 (0)