Skip to content

Commit d507ebb

Browse files
authored
ComplianceAuditlogTest to use signal/wait (#1914)
Signed-off-by: Peter Nied <petern@amazon.com>
1 parent 00e2a5d commit d507ebb

2 files changed

Lines changed: 103 additions & 131 deletions

File tree

src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java

Lines changed: 77 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
package org.opensearch.security.auditlog.compliance;
1313

14+
import java.io.IOException;
1415
import java.util.Collections;
16+
import java.util.List;
1517

1618
import com.google.common.collect.ImmutableMap;
1719
import org.apache.http.Header;
@@ -30,26 +32,28 @@
3032
import org.opensearch.security.auditlog.AbstractAuditlogiUnitTest;
3133
import org.opensearch.security.auditlog.AuditTestUtils;
3234
import org.opensearch.security.auditlog.config.AuditConfig;
35+
import org.opensearch.security.auditlog.impl.AuditMessage;
3336
import org.opensearch.security.auditlog.integration.TestAuditlogImpl;
37+
import org.opensearch.security.auditlog.integration.TestAuditlogImpl.MessagesNotFoundException;
3438
import org.opensearch.security.compliance.ComplianceConfig;
3539
import org.opensearch.security.support.ConfigConstants;
3640
import org.opensearch.security.test.DynamicSecurityConfig;
3741
import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse;
3842

39-
import static org.junit.Assert.assertFalse;
43+
import static org.hamcrest.MatcherAssert.assertThat;
44+
import static org.hamcrest.core.IsEqual.equalTo;
45+
import static org.junit.Assert.assertThrows;
4046
import static org.junit.Assert.assertTrue;
4147

4248
public class ComplianceAuditlogTest extends AbstractAuditlogiUnitTest {
4349

4450
@Test
4551
public void testSourceFilter() throws Exception {
46-
4752
Settings additionalSettings = Settings.builder()
4853
.put("plugins.security.audit.type", TestAuditlogImpl.class.getName())
4954
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, true)
5055
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, true)
5156
.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_EXTERNAL_CONFIG_ENABLED, false)
52-
//.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_WRITE_WATCHED_INDICES, "emp")
5357
.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_READ_WATCHED_FIELDS, "emp")
5458
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_TRANSPORT_CATEGORIES, "authenticated,GRANTED_PRIVILEGES")
5559
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_REST_CATEGORIES, "authenticated,GRANTED_PRIVILEGES")
@@ -66,7 +70,6 @@ public void testSourceFilter() throws Exception {
6670
rh.sendAdminCertificate = sendAdminCertificate;
6771
rh.keystore = keystore;
6872

69-
System.out.println("#### test source includes");
7073
String search = "{" +
7174
" \"_source\":[" +
7275
" \"Gender\""+
@@ -80,13 +83,11 @@ public void testSourceFilter() throws Exception {
8083
" }" +
8184
"}";
8285

83-
TestAuditlogImpl.clear();
84-
HttpResponse response = rh.executePostRequest("_search?pretty", search, encodeBasicHeader("admin", "admin"));
85-
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
86-
System.out.println(response.getBody());
87-
Thread.sleep(1500);
88-
System.out.println(TestAuditlogImpl.sb.toString());
89-
Assert.assertTrue(TestAuditlogImpl.messages.size() >= 1);
86+
final AuditMessage message = TestAuditlogImpl.doThenWaitForMessage(() -> {
87+
final HttpResponse response = rh.executePostRequest("_search?pretty", search, encodeBasicHeader("admin", "admin"));
88+
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
89+
});
90+
9091
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_DOC_READ"));
9192
Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("Designation"));
9293
Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("Salary"));
@@ -102,8 +103,6 @@ public void testComplianceEnable() throws Exception {
102103

103104
setup(additionalSettings);
104105

105-
final boolean sendAdminCertificate = rh.sendAdminCertificate;
106-
final String keystore = rh.keystore;
107106
rh.sendAdminCertificate = true;
108107
rh.keystore = "auditlog/kirk-keystore.jks";
109108

@@ -112,21 +111,21 @@ public void testComplianceEnable() throws Exception {
112111
updateAuditConfig(AuditTestUtils.createAuditPayload(auditConfig));
113112

114113
// make an event happen
115-
TestAuditlogImpl.clear();
116-
rh.executePutRequest("emp/_doc/0?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}");
114+
TestAuditlogImpl.doThenWaitForMessages(() -> {
115+
rh.executePutRequest("emp/_doc/0?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}");
116+
}, 7);
117117
assertTrue(TestAuditlogImpl.messages.toString().contains("COMPLIANCE_DOC_WRITE"));
118-
119118
// disable compliance
120119
auditConfig = new AuditConfig(true, AuditConfig.Filter.DEFAULT , ComplianceConfig.from(ImmutableMap.of("enabled", false, "write_watched_indices", Collections.singletonList("emp")), additionalSettings));
121120
updateAuditConfig(AuditTestUtils.createAuditPayload(auditConfig));
122121

123-
// make an event happen
124-
TestAuditlogImpl.clear();
125-
rh.executePutRequest("emp/_doc/1?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}");
126-
assertFalse(TestAuditlogImpl.messages.toString().contains("COMPLIANCE_DOC_WRITE"));
127-
128-
rh.sendAdminCertificate = sendAdminCertificate;
129-
rh.keystore = keystore;
122+
// trigger an event that it not captured by the audit log
123+
final MessagesNotFoundException ex = assertThrows(MessagesNotFoundException.class, () -> {
124+
TestAuditlogImpl.doThenWaitForMessage(() -> {
125+
rh.executePutRequest("emp/_doc/1?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}");
126+
});
127+
});
128+
assertThat(ex.getMissingCount(), equalTo(1));
130129
}
131130

132131
@Test
@@ -154,7 +153,6 @@ public void testSourceFilterMsearch() throws Exception {
154153
rh.sendAdminCertificate = sendAdminCertificate;
155154
rh.keystore = keystore;
156155

157-
System.out.println("#### test source includes");
158156
String search = "{}"+System.lineSeparator()
159157
+ "{" +
160158
" \"_source\":[" +
@@ -211,22 +209,23 @@ public void testInternalConfig() throws Exception {
211209
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_REST_CATEGORIES, "authenticated,GRANTED_PRIVILEGES")
212210
.build();
213211

214-
TestAuditlogImpl.clear();
215212
setup(additionalSettings);
216213

217-
try (RestHighLevelClient restHighLevelClient = getRestClient(clusterInfo, "kirk-keystore.jks", "truststore.jks")) {
218-
for(IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) {
219-
restHighLevelClient.index(ir, RequestOptions.DEFAULT);
220-
GetResponse getDocumentResponse = restHighLevelClient.get(new GetRequest(ir.index(), ir.id()), RequestOptions.DEFAULT);
221-
Assert.assertTrue("Document not found:" + getDocumentResponse, getDocumentResponse.isExists());
214+
final List<AuditMessage> messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
215+
try (RestHighLevelClient restHighLevelClient = getRestClient(clusterInfo, "kirk-keystore.jks", "truststore.jks")) {
216+
for (IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) {
217+
restHighLevelClient.index(ir, RequestOptions.DEFAULT);
218+
GetResponse getDocumentResponse = restHighLevelClient.get(new GetRequest(ir.index(), ir.id()), RequestOptions.DEFAULT);
219+
assertThat(getDocumentResponse.isExists(), equalTo(true));
220+
}
221+
} catch (IOException ioe) {
222+
throw new RuntimeException("Unexpected exception", ioe);
222223
}
223-
}
224224

225-
HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin"));
226-
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
227-
Thread.sleep(1500);
228-
System.out.println(TestAuditlogImpl.sb.toString());
229-
Assert.assertTrue(TestAuditlogImpl.messages.size() >= 15);
225+
HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin"));
226+
assertThat(response.getStatusCode(), equalTo(HttpStatus.SC_OK));
227+
}, 14);
228+
230229
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_READ"));
231230
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_WRITE"));
232231
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("anonymous_auth_enabled"));
@@ -247,7 +246,7 @@ public void testInternalConfig() throws Exception {
247246
@Test
248247
public void testExternalConfig() throws Exception {
249248

250-
Settings additionalSettings = Settings.builder()
249+
final Settings additionalSettings = Settings.builder()
251250
.put("plugins.security.audit.type", TestAuditlogImpl.class.getName())
252251
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, false)
253252
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_REST, false)
@@ -258,23 +257,23 @@ public void testExternalConfig() throws Exception {
258257
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_REST_CATEGORIES, "authenticated,GRANTED_PRIVILEGES")
259258
.build();
260259

261-
TestAuditlogImpl.clear();
262-
263-
setup(additionalSettings);
264-
265-
try (Client tc = getClient()) {
260+
TestAuditlogImpl.doThenWaitForMessages(() -> {
261+
try {
262+
setup(additionalSettings);
263+
} catch (final Exception ex) {
264+
throw new RuntimeException(ex);
265+
}
266266

267-
for(IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) {
268-
tc.index(ir).actionGet();
267+
try (Client tc = getClient()) {
268+
for(IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) {
269+
tc.index(ir).actionGet();
270+
}
269271
}
270272

271-
}
273+
final HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin"));
274+
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
275+
}, 4);
272276

273-
HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin"));
274-
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
275-
System.out.println(response.getBody());
276-
Thread.sleep(1500);
277-
System.out.println(TestAuditlogImpl.sb.toString());
278277
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("external_configuration"));
279278
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_EXTERNAL_CONFIG"));
280279
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("opensearch_yml"));
@@ -306,73 +305,29 @@ public void testUpdate() throws Exception {
306305
.actionGet();
307306
}
308307

309-
TestAuditlogImpl.clear();
310-
311-
String body = "{\"doc\": {\"Age\":123}}";
312-
313-
HttpResponse response = rh.executePostRequest("humanresources/_doc/100?pretty", body, encodeBasicHeader("admin", "admin"));
314-
Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
315-
316-
body = "{\"doc\": {\"Age\":456}}";
308+
final MessagesNotFoundException ex1 = assertThrows(MessagesNotFoundException.class, () -> {
309+
TestAuditlogImpl.doThenWaitForMessage(() -> {
310+
final String body = "{\"doc\": {\"Age\":123}}";
311+
final HttpResponse response = rh.executePostRequest("humanresources/_doc/100?pretty", body, encodeBasicHeader("admin", "admin"));
312+
Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
313+
});
314+
});
315+
assertThat(ex1.getMissingCount(), equalTo(1));
316+
317+
318+
final MessagesNotFoundException ex2 = assertThrows(MessagesNotFoundException.class, () -> {
319+
TestAuditlogImpl.doThenWaitForMessage(() -> {
320+
final String body = "{\"doc\": {\"Age\":456}}";
321+
final HttpResponse response = rh.executePostRequest("humanresources/_update/100?pretty", body, encodeBasicHeader("admin", "admin"));
322+
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
323+
});
324+
});
325+
assertThat(ex2.getMissingCount(), equalTo(1));
317326

318-
response = rh.executePostRequest("humanresources/_update/100?pretty", body, encodeBasicHeader("admin", "admin"));
319-
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
320-
System.out.println(response.getBody());
321-
Thread.sleep(1500);
322327
Assert.assertTrue(TestAuditlogImpl.messages.isEmpty());
323328
Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
324329
}
325330

326-
@Test
327-
public void testUpdatePerf() throws Exception {
328-
329-
Settings additionalSettings = Settings.builder()
330-
.put("plugins.security.audit.type", TestAuditlogImpl.class.getName())
331-
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, false)
332-
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_REST, false)
333-
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, true)
334-
.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_EXTERNAL_CONFIG_ENABLED, false)
335-
.put(ConfigConstants.SECURITY_COMPLIANCE_HISTORY_INTERNAL_CONFIG_ENABLED, false)
336-
.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_WRITE_WATCHED_INDICES, "humanresources")
337-
.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_READ_WATCHED_FIELDS, "humanresources,*")
338-
.build();
339-
340-
setup(additionalSettings);
341-
TestAuditlogImpl.clear();
342-
343-
/*try (TransportClient tc = getInternalTransportClient()) {
344-
for(int i=0; i<5000; i++) {
345-
346-
tc.prepareIndex("humanresources", "employees")
347-
//.setRefreshPolicy(RefreshPolicy.IMMEDIATE)
348-
.setSource("Age", 456+i)
349-
.execute();
350-
}
351-
}*/
352-
353-
354-
355-
for(int i=0; i<1; i++) {
356-
HttpResponse response = rh.executePostRequest("humanresources/_doc/"+i+"", "{\"customer\": {\"Age\":"+i+"}}", encodeBasicHeader("admin", "admin"));
357-
Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
358-
response = rh.executePostRequest("humanresources/_doc/"+i+"", "{\"customer\": {\"Age\":"+(i+2)+"}}", encodeBasicHeader("admin", "admin"));
359-
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
360-
response = rh.executePostRequest("humanresources/_update/"+i+"?pretty", "{\"doc\": {\"doesel\":"+(i+3)+"}}", encodeBasicHeader("admin", "admin"));
361-
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
362-
}
363-
364-
/*Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
365-
System.out.println(response.getBody());
366-
Thread.sleep(1500);
367-
Assert.assertTrue(TestAuditlogImpl.messages.isEmpty());
368-
Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));*/
369-
370-
Thread.sleep(1500);
371-
System.out.println("Messages: "+TestAuditlogImpl.messages.size());
372-
//System.out.println(TestAuditlogImpl.sb.toString());
373-
374-
}
375-
376331
@Test
377332
public void testWriteHistory() throws Exception {
378333

@@ -387,7 +342,6 @@ public void testWriteHistory() throws Exception {
387342

388343
setup(additionalSettings);
389344

390-
391345
try (Client tc = getClient()) {
392346
tc.prepareIndex("humanresources")
393347
.setRefreshPolicy(RefreshPolicy.IMMEDIATE)
@@ -396,24 +350,18 @@ public void testWriteHistory() throws Exception {
396350
.actionGet();
397351
}
398352

399-
TestAuditlogImpl.clear();
400-
401-
String body = "{\"doc\": {\"Age\":123}}";
402-
403-
HttpResponse response = rh.executePostRequest("humanresources/_doc/100?pretty", body, encodeBasicHeader("admin", "admin"));
404-
Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
405-
System.out.println(response.getBody());
406-
Thread.sleep(1500);
407-
System.out.println(TestAuditlogImpl.sb.toString());
353+
TestAuditlogImpl.doThenWaitForMessage(() -> {
354+
final String body = "{\"doc\": {\"Age\":123}}";
355+
final HttpResponse response = rh.executePostRequest("humanresources/_doc/100?pretty", body, encodeBasicHeader("admin", "admin"));
356+
Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
357+
});
408358
Assert.assertTrue(TestAuditlogImpl.sb.toString().split(".*audit_compliance_diff_content.*replace.*").length == 1);
409359

410-
body = "{\"doc\": {\"Age\":555}}";
411-
TestAuditlogImpl.clear();
412-
response = rh.executePostRequest("humanresources/_update/100?pretty", body, encodeBasicHeader("admin", "admin"));
413-
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
414-
System.out.println(response.getBody());
415-
Thread.sleep(1500);
416-
System.out.println(TestAuditlogImpl.sb.toString());
360+
TestAuditlogImpl.doThenWaitForMessage(() -> {
361+
final String body = "{\"doc\": {\"Age\":555}}";
362+
final HttpResponse response = rh.executePostRequest("humanresources/_update/100?pretty", body, encodeBasicHeader("admin", "admin"));
363+
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
364+
});
417365
Assert.assertTrue(TestAuditlogImpl.sb.toString().split(".*audit_compliance_diff_content.*replace.*").length == 1);
418366
}
419367
}

src/test/java/org/opensearch/security/auditlog/integration/TestAuditlogImpl.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public TestAuditlogImpl(String name, Settings settings, String settingsPrefix, A
3838

3939
public synchronized boolean doStore(AuditMessage msg) {
4040
if (messagesRef.get() == null || countDownRef.get() == null) {
41-
throw new RuntimeException("No message latch is waiting");
41+
// Ignore any messages that are sent before TestAuditlogImpl is waiting.
42+
return true;
4243
}
4344
sb.append(msg.toPrettyString()+System.lineSeparator());
4445
messagesRef.get().add(msg);
@@ -69,7 +70,7 @@ public static List<AuditMessage> doThenWaitForMessages(final Runnable action, fi
6970
final int maxSecondsToWaitForMessages = 1;
7071
final boolean foundAll = latch.await(maxSecondsToWaitForMessages, TimeUnit.SECONDS);
7172
if (!foundAll) {
72-
throw new RuntimeException("Did not recieve all " + expectedCount +" audit messages after a short wait.");
73+
throw new MessagesNotFoundException(expectedCount, (int)latch.getCount());
7374
}
7475
if (messages.size() != expectedCount) {
7576
throw new RuntimeException("Unexpected number of messages, was expecting " + expectedCount + ", recieved " + messages.size());
@@ -80,10 +81,33 @@ public static List<AuditMessage> doThenWaitForMessages(final Runnable action, fi
8081
return new ArrayList<>(messages);
8182
}
8283

84+
/**
85+
* Perform an action and then wait until a single message has been found.
86+
*/
87+
public static AuditMessage doThenWaitForMessage(final Runnable action) {
88+
return doThenWaitForMessages(action, 1).get(0);
89+
}
90+
8391
@Override
8492
public boolean isHandlingBackpressure() {
8593
return true;
8694
}
8795

96+
public static class MessagesNotFoundException extends RuntimeException {
97+
private final int expectedCount;
98+
private final int missingCount;
99+
public MessagesNotFoundException(final int expectedCount, final int missingCount) {
100+
super("Did not recieve all " + expectedCount +" audit messages after a short wait, missing " + missingCount + " messages");
101+
this.expectedCount = expectedCount;
102+
this.missingCount = missingCount;
103+
}
88104

105+
public int getExpectedCount() {
106+
return expectedCount;
107+
}
108+
109+
public int getMissingCount() {
110+
return missingCount;
111+
}
112+
}
89113
}

0 commit comments

Comments
 (0)