diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a7245b0195..ebcc316bcf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,55 +8,69 @@ env:
jobs:
build:
name: build
- runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
jdk: [8, 11, 14]
+ platform: ["ubuntu-latest", "windows-latest"]
+ runs-on: ${{ matrix.platform }}
steps:
- - name: Set up JDK
+ - name: Set up JDK for build and test on 8 and 11
+ if: matrix.jdk != '14'
+ uses: actions/setup-java@v2
+ with:
+ distribution: temurin # Temurin is a distribution of adoptium
+ java-version: ${{ matrix.jdk }}
+
+ - name: Set up JDK for build and test on 14
+ if: matrix.jdk == '14'
uses: actions/setup-java@v1
with:
+ distribution: temurin
java-version: ${{ matrix.jdk }}
- name: Checkout security
uses: actions/checkout@v2
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ - name: Build and Test
+ uses: gradle/gradle-build-action@v2
with:
- languages: java
+ arguments: |
+ build test -Dbuild.snapshot=false
+ -x checkstyleMain
+ -x checkstyleTest
+
+ - name: Coverage
+ uses: codecov/codecov-action@v1
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ files: ./build/reports/jacoco/test/jacocoTestReport.xml
- - name: Cache Gradle packages
- uses: actions/cache@v2
+ - uses: actions/upload-artifact@v3
+ if: always()
with:
+ name: ${{ matrix.platform }}-JDK${{ matrix.jdk }}-reports
path: |
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
- restore-keys: |
- ${{ runner.os }}-gradle-
-
-
- - name: Checkstyle
- run: ./gradlew clean checkstyleMain checkstyleTest
-
- - name: Package
- run: ./gradlew clean build -Dbuild.snapshot=false -x test
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ ./build/reports/
- - name: Test
- run: OPENDISTRO_SECURITY_TEST_OPENSSL_OPT=true ./gradlew test -i
+ - name: check archive for debugging
+ if: always()
+ run: echo "Check the artifact ${{ matrix.platform }}-JDK${{ matrix.jdk }}-reports for detailed test results"
- - name: Coverage
- uses: codecov/codecov-action@v1
+ code-ql:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-java@v1
with:
- token: ${{ secrets.CODECOV_TOKEN }}
- files: ./build/jacoco/test/jacocoTestReport.xml
+ java-version: 11
+ - uses: github/codeql-action/init@v1
+ with:
+ languages: java
+ - run: ./gradlew clean build -Dbuild.snapshot=false -x test
+ - uses: github/codeql-action/analyze@v1
build-artifact-names:
runs-on: ubuntu-latest
@@ -72,18 +86,15 @@ jobs:
security_plugin_version_no_snapshot=$(echo $security_plugin_version | sed 's/-SNAPSHOT//g')
security_plugin_version_only_number=$(echo $security_plugin_version_no_snapshot | cut -d- -f1)
test_qualifier=alpha2
-
echo "SECURITY_PLUGIN_VERSION=$security_plugin_version" >> $GITHUB_ENV
echo "SECURITY_PLUGIN_VERSION_NO_SNAPSHOT=$security_plugin_version_no_snapshot" >> $GITHUB_ENV
echo "SECURITY_PLUGIN_VERSION_ONLY_NUMBER=$security_plugin_version_only_number" >> $GITHUB_ENV
echo "TEST_QUALIFIER=$test_qualifier" >> $GITHUB_ENV
-
- run: |
echo ${{ env.SECURITY_PLUGIN_VERSION }}
echo ${{ env.SECURITY_PLUGIN_VERSION_NO_SNAPSHOT }}
echo ${{ env.SECURITY_PLUGIN_VERSION_ONLY_NUMBER }}
echo ${{ env.TEST_QUALIFIER }}
-
- run: ./gradlew clean assemble && test -s ./build/opensearch-security-${{ env.SECURITY_PLUGIN_VERSION }}.jar
- run: ./gradlew clean assemble -Dbuild.snapshot=false && test -s ./build/opensearch-security-${{ env.SECURITY_PLUGIN_VERSION_NO_SNAPSHOT }}.jar
@@ -96,10 +107,9 @@ jobs:
## EXISTING_OS_VERSION outputs the major version, example as 2
EXISTING_OS_VERSION=$(./gradlew properties | grep opensearch.version | cut -d':' -f2- | awk '{$1=$1};1' | cut -d '-' -f1 | cut -d '.' -f1)
## INCREMENT_OS_VERSION in an increment of 1, example if EXISTING_OS_VERSION is 2, INCREMENT_OS_VERSION is 3
- INCREMENT_OS_VERSION=$((++EXISTING_OS_VERSION))
+ INCREMENT_OS_VERSION=$((++EXISTING_OS_VERSION))
./gradlew clean updateVersion -DnewVersion=$INCREMENT_OS_VERSION.0.0-SNAPSHOT
test `./gradlew properties | grep opensearch.version | cut -d':' -f2- | awk '{$1=$1};1'` = $INCREMENT_OS_VERSION.0.0-SNAPSHOT
-
- name: List files in the build directory if there was an error
run: ls -al ./build/
if: failure()
diff --git a/build.gradle b/build.gradle
index 4162538228..b91a25c390 100644
--- a/build.gradle
+++ b/build.gradle
@@ -42,6 +42,7 @@ plugins {
id "nebula.ospackage" version "9.0.0"
id "com.google.osdetector" version "1.7.0"
id "org.gradle.test-retry" version "1.3.1"
+ id "com.github.spotbugs" version "5.0.13"
}
import org.gradle.crypto.checksum.Checksum
@@ -164,6 +165,7 @@ publishing {
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
+ options.warnings = false
}
static def getTimestamp() {
@@ -223,6 +225,13 @@ testsJar {
libsDirName = '.'
}
+spotbugs {
+ includeFilter = file('spotbugs-include.xml')
+}
+
+spotbugsTest {
+ enabled = false
+}
test {
maxParallelForks = 3
diff --git a/spotbugs-include.xml b/spotbugs-include.xml
new file mode 100644
index 0000000000..dd6062700d
--- /dev/null
+++ b/spotbugs-include.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java b/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java
index 25c547e4e2..c2791a02b8 100644
--- a/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java
+++ b/src/main/java/com/amazon/dlic/auth/http/saml/AuthTokenProcessorHandler.java
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
@@ -155,7 +156,7 @@ private AuthTokenProcessorAction.Response handleImpl(RestRequest restRequest, Re
SettingsException {
if (token_log.isDebugEnabled()) {
try {
- token_log.debug("SAMLResponse for {}\n{}", samlRequestId, new String(Util.base64decoder(samlResponseBase64), "UTF-8"));
+ token_log.debug("SAMLResponse for {}\n{}", samlRequestId, new String(Util.base64decoder(samlResponseBase64), StandardCharsets.UTF_8));
} catch (Exception e) {
token_log.warn(
"SAMLResponse for {} cannot be decoded from base64\n{}",
diff --git a/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java b/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java
index 04dd285b5c..bd479c0db2 100644
--- a/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java
+++ b/src/main/java/org/opensearch/security/auditlog/impl/AbstractAuditLog.java
@@ -454,7 +454,7 @@ public void logDocumentWritten(ShardId shardId, GetResult originalResult, Index
try (XContentParser parser = XContentHelper.createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, originalResult.internalSourceRef(), XContentType.JSON)) {
Object base64 = parser.map().values().iterator().next();
if(base64 instanceof String) {
- originalSource = (new String(BaseEncoding.base64().decode((String) base64)));
+ originalSource = (new String(BaseEncoding.base64().decode((String) base64), StandardCharsets.UTF_8));
} else {
originalSource = XContentHelper.convertToJson(originalResult.internalSourceRef(), false, XContentType.JSON);
}
@@ -465,7 +465,7 @@ public void logDocumentWritten(ShardId shardId, GetResult originalResult, Index
try (XContentParser parser = XContentHelper.createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, currentIndex.source(), XContentType.JSON)) {
Object base64 = parser.map().values().iterator().next();
if(base64 instanceof String) {
- currentSource = (new String(BaseEncoding.base64().decode((String) base64)));
+ currentSource = new String(BaseEncoding.base64().decode((String) base64), StandardCharsets.UTF_8);
} else {
currentSource = XContentHelper.convertToJson(currentIndex.source(), false, XContentType.JSON);
}
@@ -492,7 +492,7 @@ public void logDocumentWritten(ShardId shardId, GetResult originalResult, Index
try (XContentParser parser = XContentHelper.createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, currentIndex.source(), XContentType.JSON)) {
Object base64 = parser.map().values().iterator().next();
if(base64 instanceof String) {
- msg.addSecurityConfigContentToRequestBody(new String(BaseEncoding.base64().decode((String) base64)), id);
+ msg.addSecurityConfigContentToRequestBody(new String(BaseEncoding.base64().decode((String) base64), StandardCharsets.UTF_8), id);
} else {
msg.addSecurityConfigTupleToRequestBody(new Tuple(XContentType.JSON, currentIndex.source()), id);
}
diff --git a/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java b/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java
index cf348ab120..def54fb041 100644
--- a/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java
+++ b/src/main/java/org/opensearch/security/auditlog/impl/AuditMessage.java
@@ -1,16 +1,12 @@
/*
- * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
*
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * or in the "license" file accompanying this file. This file 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.
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
*/
package org.opensearch.security.auditlog.impl;
@@ -433,10 +429,38 @@ public String getRequestType() {
return (String) this.auditInfo.get(TRANSPORT_REQUEST_TYPE);
}
+ public RestRequest.Method getRequestMethod() {
+ return (RestRequest.Method) this.auditInfo.get(REST_REQUEST_METHOD);
+ }
+
public AuditCategory getCategory() {
return msgCategory;
}
+ public Origin getOrigin() {
+ return (Origin) this.auditInfo.get(ORIGIN);
+ }
+
+ public String getPrivilege() {
+ return (String) this.auditInfo.get(PRIVILEGE);
+ }
+
+ public String getExceptionStackTrace() {
+ return (String) this.auditInfo.get(EXCEPTION);
+ }
+
+ public String getRequestBody() {
+ return (String) this.auditInfo.get(REQUEST_BODY);
+ }
+
+ public String getNodeId() {
+ return (String) this.auditInfo.get(NODE_ID);
+ }
+
+ public String getDocId() {
+ return (String) this.auditInfo.get(ID);
+ }
+
@Override
public String toString() {
try {
diff --git a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java
index 0043373c47..8b98b1e039 100644
--- a/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java
+++ b/src/main/java/org/opensearch/security/configuration/ConfigurationLoaderSecurity7.java
@@ -31,6 +31,7 @@
package org.opensearch.security.configuration;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -275,7 +276,7 @@ private SecurityDynamicConfiguration> toConfig(GetResponse singleGetResponse,
parser.nextToken();
- final String jsonAsString = SecurityUtils.replaceEnvVars(new String(parser.binaryValue()), settings);
+ final String jsonAsString = SecurityUtils.replaceEnvVars(new String(parser.binaryValue(), StandardCharsets.UTF_8), settings);
final JsonNode jsonNode = DefaultObjectMapper.readTree(jsonAsString);
int configVersion = 1;
diff --git a/src/main/java/org/opensearch/security/ssl/DefaultSecurityKeyStore.java b/src/main/java/org/opensearch/security/ssl/DefaultSecurityKeyStore.java
index c84e8a05de..982364fd73 100644
--- a/src/main/java/org/opensearch/security/ssl/DefaultSecurityKeyStore.java
+++ b/src/main/java/org/opensearch/security/ssl/DefaultSecurityKeyStore.java
@@ -28,6 +28,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.File;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
diff --git a/src/main/java/org/opensearch/security/support/ConfigHelper.java b/src/main/java/org/opensearch/security/support/ConfigHelper.java
index 17f6ea5135..de3e772d8b 100644
--- a/src/main/java/org/opensearch/security/support/ConfigHelper.java
+++ b/src/main/java/org/opensearch/security/support/ConfigHelper.java
@@ -31,10 +31,13 @@
package org.opensearch.security.support;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
import org.opensearch.security.securityconf.impl.Meta;
import org.apache.logging.log4j.Logger;
@@ -96,7 +99,7 @@ public static void uploadFile(Client tc, String filepath, String index, CType cT
public static Reader createFileOrStringReader(CType cType, int configVersion, String filepath, boolean populateEmptyIfFileMissing) throws Exception {
Reader reader;
if (!populateEmptyIfFileMissing || new File(filepath).exists()) {
- reader = new FileReader(filepath);
+ reader = new InputStreamReader(new FileInputStream(filepath), StandardCharsets.UTF_8);
} else {
reader = new StringReader(createEmptySdcYaml(cType, configVersion));
}
@@ -148,7 +151,7 @@ public static SecurityDynamicConfiguration fromYamlReader(Reader yamlRead
}
public static SecurityDynamicConfiguration fromYamlFile(String filepath, CType ctype, int version, long seqNo, long primaryTerm) throws IOException {
- return fromYamlReader(new FileReader(filepath), ctype, version, seqNo, primaryTerm);
+ return fromYamlReader(new InputStreamReader(new FileInputStream(filepath), StandardCharsets.UTF_8), ctype, version, seqNo, primaryTerm);
}
public static SecurityDynamicConfiguration fromYamlString(String yamlString, CType ctype, int version, long seqNo, long primaryTerm) throws IOException {
diff --git a/src/main/java/org/opensearch/security/support/SecurityUtils.java b/src/main/java/org/opensearch/security/support/SecurityUtils.java
index 2da1d0e252..61b3eb0242 100644
--- a/src/main/java/org/opensearch/security/support/SecurityUtils.java
+++ b/src/main/java/org/opensearch/security/support/SecurityUtils.java
@@ -50,9 +50,10 @@
public final class SecurityUtils {
protected final static Logger log = LogManager.getLogger(SecurityUtils.class);
- private static final Pattern ENV_PATTERN = Pattern.compile("\\$\\{env\\.([\\w]+)((\\:\\-)?[\\w]*)\\}");
- private static final Pattern ENVBC_PATTERN = Pattern.compile("\\$\\{envbc\\.([\\w]+)((\\:\\-)?[\\w]*)\\}");
- private static final Pattern ENVBASE64_PATTERN = Pattern.compile("\\$\\{envbase64\\.([\\w]+)((\\:\\-)?[\\w]*)\\}");
+ private static final String ENV_PATTERN_SUFFIX = "\\.([\\w=():\\-_.]+?)(\\:\\-[\\w=():\\-_.]*)?\\}";
+ static final Pattern ENV_PATTERN = Pattern.compile("\\$\\{env" + ENV_PATTERN_SUFFIX);
+ static final Pattern ENVBC_PATTERN = Pattern.compile("\\$\\{envbc" + ENV_PATTERN_SUFFIX);
+ static final Pattern ENVBASE64_PATTERN = Pattern.compile("\\$\\{envbase64" + ENV_PATTERN_SUFFIX);
public static Locale EN_Locale = forEN();
diff --git a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java
index a80f60c560..5681efbf6e 100644
--- a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java
+++ b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java
@@ -33,8 +33,10 @@
import java.io.ByteArrayInputStream;
import java.io.Console;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.InetSocketAddress;
@@ -911,8 +913,8 @@ private static boolean retrieveFile(final Client tc, final String filepath, fina
}
- System.out.println("Will retrieve '"+type+"/" +id+"' into "+filepath+" "+(legacy?"(legacy mode)":""));
- try (Writer writer = new FileWriter(filepath)) {
+ System.out.println("Will retrieve '"+"/" +id+"' into "+filepath+" "+(legacy?"(legacy mode)":""));
+ try (Writer writer = new OutputStreamWriter(new FileOutputStream(filepath), StandardCharsets.UTF_8)) {
final GetResponse response = tc.get(new GetRequest(index).type(type).id(id).refresh(true).realtime(false)).actionGet();
diff --git a/src/test/java/org/opensearch/security/auditlog/AuditLogTestSuite.java b/src/test/java/org/opensearch/security/auditlog/AuditLogTestSuite.java
deleted file mode 100644
index 9c3be2c528..0000000000
--- a/src/test/java/org/opensearch/security/auditlog/AuditLogTestSuite.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright OpenSearch Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * or in the "license" file accompanying this file. This file 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.opensearch.security.auditlog;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-import org.opensearch.security.auditlog.compliance.ComplianceAuditlogTest;
-import org.opensearch.security.auditlog.compliance.RestApiComplianceAuditlogTest;
-import org.opensearch.security.auditlog.impl.AuditlogTest;
-import org.opensearch.security.auditlog.impl.DelegateTest;
-import org.opensearch.security.auditlog.impl.DisabledCategoriesTest;
-import org.opensearch.security.auditlog.impl.IgnoreAuditUsersTest;
-import org.opensearch.security.auditlog.impl.TracingTests;
-import org.opensearch.security.auditlog.integration.BasicAuditlogTest;
-import org.opensearch.security.auditlog.integration.SSLAuditlogTest;
-import org.opensearch.security.auditlog.routing.FallbackTest;
-import org.opensearch.security.auditlog.routing.RouterTest;
-import org.opensearch.security.auditlog.routing.RoutingConfigurationTest;
-import org.opensearch.security.auditlog.sink.KafkaSinkTest;
-import org.opensearch.security.auditlog.sink.SinkProviderTLSTest;
-import org.opensearch.security.auditlog.sink.SinkProviderTest;
-import org.opensearch.security.auditlog.sink.WebhookAuditLogTest;
-
-@RunWith(Suite.class)
-
-@Suite.SuiteClasses({
- ComplianceAuditlogTest.class,
- RestApiComplianceAuditlogTest.class,
- AuditlogTest.class,
- DelegateTest.class,
- DisabledCategoriesTest.class,
- IgnoreAuditUsersTest.class,
- TracingTests.class,
- BasicAuditlogTest.class,
- SSLAuditlogTest.class,
- FallbackTest.class,
- RouterTest.class,
- RoutingConfigurationTest.class,
- SinkProviderTest.class,
- SinkProviderTLSTest.class,
- WebhookAuditLogTest.class,
- KafkaSinkTest.class
-})
-public class AuditLogTestSuite {
-
-}
diff --git a/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java b/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java
index 4f6d1444e8..5ae312f248 100644
--- a/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java
+++ b/src/test/java/org/opensearch/security/auditlog/compliance/ComplianceAuditlogTest.java
@@ -1,56 +1,60 @@
/*
- * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
*
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * or in the "license" file accompanying this file. This file 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.
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
*/
package org.opensearch.security.auditlog.compliance;
-import org.opensearch.security.auditlog.AuditTestUtils;
-import org.opensearch.security.auditlog.config.AuditConfig;
-import org.opensearch.security.compliance.ComplianceConfig;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
import com.google.common.collect.ImmutableMap;
import org.apache.http.Header;
import org.apache.http.HttpStatus;
-import org.opensearch.action.index.IndexRequest;
-import org.opensearch.action.support.WriteRequest.RefreshPolicy;
import org.opensearch.client.transport.TransportClient;
-import org.opensearch.common.settings.Settings;
import org.junit.Assert;
import org.junit.Test;
-import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse;
+import org.opensearch.action.index.IndexRequest;
+import org.opensearch.action.support.WriteRequest.RefreshPolicy;
+import org.opensearch.common.settings.Settings;
import org.opensearch.security.auditlog.AbstractAuditlogiUnitTest;
+import org.opensearch.security.auditlog.AuditTestUtils;
+import org.opensearch.security.auditlog.AuditLog.Origin;
+import org.opensearch.security.auditlog.config.AuditConfig;
+import org.opensearch.security.auditlog.impl.AuditCategory;
+import org.opensearch.security.auditlog.impl.AuditMessage;
import org.opensearch.security.auditlog.integration.TestAuditlogImpl;
+import org.opensearch.security.auditlog.integration.TestAuditlogImpl.MessagesNotFoundException;
+import org.opensearch.security.compliance.ComplianceConfig;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.test.DynamicSecurityConfig;
-import org.opensearch.security.test.helper.rest.RestHelper;
-
-import java.util.Collections;
+import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.core.AnyOf.anyOf;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThrows;
public class ComplianceAuditlogTest extends AbstractAuditlogiUnitTest {
@Test
public void testSourceFilter() throws Exception {
-
Settings additionalSettings = Settings.builder()
.put("plugins.security.audit.type", TestAuditlogImpl.class.getName())
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, true)
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, true)
.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_EXTERNAL_CONFIG_ENABLED, false)
- //.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_WRITE_WATCHED_INDICES, "emp")
.put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_READ_WATCHED_FIELDS, "emp")
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_TRANSPORT_CATEGORIES, "authenticated,GRANTED_PRIVILEGES")
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_REST_CATEGORIES, "authenticated,GRANTED_PRIVILEGES")
@@ -61,13 +65,12 @@ public void testSourceFilter() throws Exception {
final String keystore = rh.keystore;
rh.sendAdminCertificate = true;
rh.keystore = "auditlog/kirk-keystore.jks";
- rh.executePutRequest("emp/doc/0?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}", new Header[0]);
- rh.executePutRequest("emp/doc/1?refresh", "{\"Designation\" : \"IT\", \"Gender\" : \"male\", \"Salary\" : 200}", new Header[0]);
- rh.executePutRequest("emp/doc/2?refresh", "{\"Designation\" : \"IT\", \"Gender\" : \"female\", \"Salary\" : 300}", new Header[0]);
+ rh.executePutRequest("emp/_doc/0?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}", new Header[0]);
+ rh.executePutRequest("emp/_doc/1?refresh", "{\"Designation\" : \"IT\", \"Gender\" : \"male\", \"Salary\" : 200}", new Header[0]);
+ rh.executePutRequest("emp/_doc/2?refresh", "{\"Designation\" : \"IT\", \"Gender\" : \"female\", \"Salary\" : 300}", new Header[0]);
rh.sendAdminCertificate = sendAdminCertificate;
rh.keystore = keystore;
- System.out.println("#### test source includes");
String search = "{" +
" \"_source\":[" +
" \"Gender\""+
@@ -81,17 +84,16 @@ public void testSourceFilter() throws Exception {
" }" +
"}";
- TestAuditlogImpl.clear();
- HttpResponse response = rh.executePostRequest("_search?pretty", search, encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- System.out.println(response.getBody());
- Thread.sleep(1500);
- System.out.println(TestAuditlogImpl.sb.toString());
- Assert.assertTrue(TestAuditlogImpl.messages.size() >= 1);
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_DOC_READ"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("Designation"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("Salary"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("Gender"));
+ final AuditMessage message = TestAuditlogImpl.doThenWaitForMessage(() -> {
+ final HttpResponse response = rh.executePostRequest("_search?pretty", search, encodeBasicHeader("admin", "admin"));
+ Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+ });
+
+ assertThat(message.getCategory(), equalTo(AuditCategory.COMPLIANCE_DOC_READ));
+ assertThat(message.getRequestBody(), not(containsString("Designation")));
+ assertThat(message.getRequestBody(), not(containsString("Salary")));
+ assertThat(message.getRequestBody(), containsString("Gender"));
+
Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
}
@@ -103,8 +105,6 @@ public void testComplianceEnable() throws Exception {
setup(additionalSettings);
- final boolean sendAdminCertificate = rh.sendAdminCertificate;
- final String keystore = rh.keystore;
rh.sendAdminCertificate = true;
rh.keystore = "auditlog/kirk-keystore.jks";
@@ -113,21 +113,38 @@ public void testComplianceEnable() throws Exception {
updateAuditConfig(AuditTestUtils.createAuditPayload(auditConfig));
// make an event happen
- TestAuditlogImpl.clear();
- rh.executePutRequest("emp/doc/0?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}");
- assertTrue(TestAuditlogImpl.messages.toString().contains("COMPLIANCE_DOC_WRITE"));
+ List messages;
+ try {
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executePutRequest("emp/_doc/0?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}");
+ System.out.println(rh.executeGetRequest("_cat/shards?v"));
+ }, 7);
+ } catch (final MessagesNotFoundException ex) {
+ // indices:admin/mapping/auto_put can be logged twice, this handles if they were not found
+ assertThat("Too many missing audit log messages", ex.getMissingCount(), equalTo(2));
+ messages = ex.getFoundMessages();
+ }
+
+ messages.stream().filter(msg -> msg.getCategory().equals(AuditCategory.COMPLIANCE_DOC_WRITE))
+ .findFirst().orElseThrow(() -> new RuntimeException("Missing COMPLIANCE message"));
+ final List indexCreation = messages.stream().filter(msg -> "indices:admin/auto_create".equals(msg.getPrivilege())).collect(Collectors.toList());
+ assertThat(indexCreation.size(), equalTo(2));
+
+ final List mappingCreation = messages.stream().filter(msg -> "indices:admin/mapping/auto_put".equals(msg.getPrivilege())).collect(Collectors.toList());
+ assertThat(mappingCreation.size(), anyOf(equalTo(4), equalTo(2)));
+
// disable compliance
auditConfig = new AuditConfig(true, AuditConfig.Filter.DEFAULT , ComplianceConfig.from(ImmutableMap.of("enabled", false, "write_watched_indices", Collections.singletonList("emp")), additionalSettings));
updateAuditConfig(AuditTestUtils.createAuditPayload(auditConfig));
- // make an event happen
- TestAuditlogImpl.clear();
- rh.executePutRequest("emp/doc/1?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}");
- assertFalse(TestAuditlogImpl.messages.toString().contains("COMPLIANCE_DOC_WRITE"));
-
- rh.sendAdminCertificate = sendAdminCertificate;
- rh.keystore = keystore;
+ // trigger an event that it not captured by the audit log
+ final MessagesNotFoundException ex = assertThrows(MessagesNotFoundException.class, () -> {
+ TestAuditlogImpl.doThenWaitForMessage(() -> {
+ rh.executePutRequest("emp/_doc/1?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}");
+ });
+ });
+ assertThat(ex.getMissingCount(), equalTo(1));
}
@Test
@@ -149,13 +166,12 @@ public void testSourceFilterMsearch() throws Exception {
final String keystore = rh.keystore;
rh.sendAdminCertificate = true;
rh.keystore = "auditlog/kirk-keystore.jks";
- rh.executePutRequest("emp/doc/0?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}", new Header[0]);
- rh.executePutRequest("emp/doc/1?refresh", "{\"Designation\" : \"IT\", \"Gender\" : \"male\", \"Salary\" : 200}", new Header[0]);
- rh.executePutRequest("emp/doc/2?refresh", "{\"Designation\" : \"IT\", \"Gender\" : \"female\", \"Salary\" : 300}", new Header[0]);
+ rh.executePutRequest("emp/_doc/0?refresh", "{\"Designation\" : \"CEO\", \"Gender\" : \"female\", \"Salary\" : 100}", new Header[0]);
+ rh.executePutRequest("emp/_doc/1?refresh", "{\"Designation\" : \"IT\", \"Gender\" : \"male\", \"Salary\" : 200}", new Header[0]);
+ rh.executePutRequest("emp/_doc/2?refresh", "{\"Designation\" : \"IT\", \"Gender\" : \"female\", \"Salary\" : 300}", new Header[0]);
rh.sendAdminCertificate = sendAdminCertificate;
rh.keystore = keystore;
- System.out.println("#### test source includes");
String search = "{}"+System.lineSeparator()
+ "{" +
" \"_source\":[" +
@@ -184,18 +200,24 @@ public void testSourceFilterMsearch() throws Exception {
" }" +
"}"+System.lineSeparator();
- TestAuditlogImpl.clear();
- HttpResponse response = rh.executePostRequest("_msearch?pretty", search, encodeBasicHeader("admin", "admin"));
- assertNotContains(response, "*exception*");
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- Thread.sleep(1500);
- System.out.println(TestAuditlogImpl.sb.toString());
- Assert.assertTrue("Was "+TestAuditlogImpl.messages.size(), TestAuditlogImpl.messages.size() == 2);
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_DOC_READ"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("Salary"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("Gender"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("Designation"));
- Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
+ final List messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ HttpResponse response = rh.executePostRequest("_msearch?pretty", search, encodeBasicHeader("admin", "admin"));
+ assertNotContains(response, "*exception*");
+ Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+ }, 2);
+
+
+ final AuditMessage desginationMsg = messages.stream().filter(msg -> msg.getRequestBody().contains("Designation")).findFirst().get();
+ assertThat(desginationMsg.getCategory(), equalTo(AuditCategory.COMPLIANCE_DOC_READ));
+ assertThat(desginationMsg.getRequestBody(), containsString("Designation"));
+ assertThat(desginationMsg.getRequestBody(), not(containsString("Salary")));
+
+ final AuditMessage genderMsg = messages.stream().filter(msg -> msg.getRequestBody().contains("Gender")).findFirst().get();
+ assertThat(genderMsg.getCategory(), equalTo(AuditCategory.COMPLIANCE_DOC_READ));
+ assertThat(genderMsg.getRequestBody(), containsString("Gender"));
+ assertThat(genderMsg.getRequestBody(), not(containsString("Salary")));
+
+ Assert.assertTrue(validateMsgs(messages));
}
@Test
@@ -213,42 +235,40 @@ public void testInternalConfig() throws Exception {
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_REST_CATEGORIES, "authenticated,GRANTED_PRIVILEGES")
.build();
- TestAuditlogImpl.clear();
setup(additionalSettings);
- try (TransportClient tc = getInternalTransportClient()) {
- for(IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) {
- tc.index(ir).actionGet();
+ final List expectedDocumentsTypes = Arrays.asList("config", "actiongroups", "internalusers", "roles", "rolesmapping", "tenants", "audit");
+ final List messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ try (TransportClient tc = getInternalTransportClient()) {
+ for(IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) {
+ tc.index(ir).actionGet();
+ }
+
}
- }
+ HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin"));
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+ }, 7);
- HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- Thread.sleep(1500);
- System.out.println(TestAuditlogImpl.sb.toString());
- Assert.assertTrue(TestAuditlogImpl.messages.size() > 25);
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_READ"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_WRITE"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("anonymous_auth_enabled"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("indices:data/read/suggest"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("internalusers"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("opendistro_security_all_access"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("indices:data/read/suggest"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("eyJzZWFyY2hndWFy"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("eyJBTEwiOlsiaW"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("eyJhZG1pbiI6e"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("eyJzZ19hb"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("eyJzZ19hbGx"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().contains("dvcmYiOnsiY2x"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\\\"op\\\":\\\"remove\\\",\\\"path\\\":\\\"/opendistro_security_worf\\\""));
- Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
+ final List documentIds = messages.stream().map(AuditMessage::getDocId).distinct().collect(Collectors.toList());
+ assertThat(documentIds, equalTo(expectedDocumentsTypes));
+
+ messages.stream().collect(Collectors.groupingBy(AuditMessage::getDocId)).entrySet().forEach((e) -> {
+ final String docId = e.getKey();
+ final List messagesByDocId = e.getValue();
+ assertThat("Doc " + docId + " should have a write config message",
+ messagesByDocId.stream().map(AuditMessage::getCategory).collect(Collectors.toList()),
+ equalTo(Arrays.asList(AuditCategory.COMPLIANCE_INTERNAL_CONFIG_WRITE))
+ );
+ });
+
+ Assert.assertTrue(validateMsgs(messages));
}
@Test
public void testExternalConfig() throws Exception {
- Settings additionalSettings = Settings.builder()
+ final Settings additionalSettings = Settings.builder()
.put("plugins.security.audit.type", TestAuditlogImpl.class.getName())
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, false)
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_REST, false)
@@ -259,27 +279,39 @@ public void testExternalConfig() throws Exception {
.put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_REST_CATEGORIES, "authenticated,GRANTED_PRIVILEGES")
.build();
- TestAuditlogImpl.clear();
-
- setup(additionalSettings);
+ final List allMessages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ try {
+ setup(additionalSettings);
+ } catch (final Exception ex) {
+ throw new RuntimeException(ex);
+ }
- try (TransportClient tc = getInternalTransportClient()) {
+ try (TransportClient tc = getInternalTransportClient()) {
- for(IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) {
- tc.index(ir).actionGet();
+ for(IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) {
+ tc.index(ir).actionGet();
+ }
+
}
- }
+ final HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin"));
+ Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+ }, 18);
- HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- System.out.println(response.getBody());
- Thread.sleep(1500);
- System.out.println(TestAuditlogImpl.sb.toString());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("external_configuration"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_EXTERNAL_CONFIG"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("opensearch_yml"));
- Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
+ // Filter out transport layer messages
+ final List messages = allMessages.stream().filter(msg -> msg.getOrigin() != Origin.TRANSPORT).collect(Collectors.toList());
+
+ // Record the updated config, and then for each node record that the config was updated
+ assertThat(messages.get(0).getCategory(), equalTo(AuditCategory.COMPLIANCE_INTERNAL_CONFIG_WRITE));
+ assertThat(messages.get(1).getCategory(), equalTo(AuditCategory.COMPLIANCE_EXTERNAL_CONFIG));
+ assertThat(messages.get(2).getCategory(), equalTo(AuditCategory.COMPLIANCE_EXTERNAL_CONFIG));
+ assertThat(messages.get(3).getCategory(), equalTo(AuditCategory.COMPLIANCE_EXTERNAL_CONFIG));
+
+ // Make sure that the config update messsages are for each node in the cluster
+ assertThat(messages.get(1).getNodeId(), not(equalTo(messages.get(2).getNodeId())));
+ assertThat(messages.get(2).getNodeId(), not(equalTo(messages.get(3).getNodeId())));
+
+ Assert.assertTrue(validateMsgs(messages));
}
@Test
@@ -307,68 +339,29 @@ public void testUpdate() throws Exception {
.actionGet();
}
- TestAuditlogImpl.clear();
+ final MessagesNotFoundException ex1 = assertThrows(MessagesNotFoundException.class, () -> {
+ TestAuditlogImpl.doThenWaitForMessage(() -> {
+ final String body = "{\"doc\": {\"Age\":123}}";
+ final HttpResponse response = rh.executePostRequest("humanresources/_doc/100?pretty", body, encodeBasicHeader("admin", "admin"));
+ Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+ });
+ });
+ assertThat(ex1.getMissingCount(), equalTo(1));
+
+
+ final MessagesNotFoundException ex2 = assertThrows(MessagesNotFoundException.class, () -> {
+ TestAuditlogImpl.doThenWaitForMessage(() -> {
+ final String body = "{\"doc\": {\"Age\":456}}";
+ final HttpResponse response = rh.executePostRequest("humanresources/_update/100?pretty", body, encodeBasicHeader("admin", "admin"));
+ Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+ });
+ });
+ assertThat(ex2.getMissingCount(), equalTo(1));
- String body = "{\"doc\": {\"Age\":123}}";
-
- HttpResponse response = rh.executePostRequest("humanresources/employees/100/_update?pretty", body, encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- System.out.println(response.getBody());
- Thread.sleep(1500);
Assert.assertTrue(TestAuditlogImpl.messages.isEmpty());
Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
}
- @Test
- public void testUpdatePerf() throws Exception {
-
- Settings additionalSettings = Settings.builder()
- .put("plugins.security.audit.type", TestAuditlogImpl.class.getName())
- .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, false)
- .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_REST, false)
- .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, true)
- .put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_EXTERNAL_CONFIG_ENABLED, false)
- .put(ConfigConstants.SECURITY_COMPLIANCE_HISTORY_INTERNAL_CONFIG_ENABLED, false)
- .put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_WRITE_WATCHED_INDICES, "humanresources")
- .put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_READ_WATCHED_FIELDS, "humanresources,*")
- .build();
-
- setup(additionalSettings);
- TestAuditlogImpl.clear();
-
- /*try (TransportClient tc = getInternalTransportClient()) {
- for(int i=0; i<5000; i++) {
-
- tc.prepareIndex("humanresources", "employees")
- //.setRefreshPolicy(RefreshPolicy.IMMEDIATE)
- .setSource("Age", 456+i)
- .execute();
- }
- }*/
-
-
-
- for(int i=0; i<1; i++) {
- HttpResponse response = rh.executePostRequest("humanresources/employees/"+i+"", "{\"customer\": {\"Age\":"+i+"}}", encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
- response = rh.executePostRequest("humanresources/employees/"+i+"", "{\"customer\": {\"Age\":"+(i+2)+"}}", encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- response = rh.executePostRequest("humanresources/employees/"+i+"/_update?pretty", "{\"doc\": {\"doesel\":"+(i+3)+"}}", encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- }
-
- /*Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- System.out.println(response.getBody());
- Thread.sleep(1500);
- Assert.assertTrue(TestAuditlogImpl.messages.isEmpty());
- Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));*/
-
- Thread.sleep(1500);
- System.out.println("Messages: "+TestAuditlogImpl.messages.size());
- //System.out.println(TestAuditlogImpl.sb.toString());
-
- }
-
@Test
public void testWriteHistory() throws Exception {
@@ -392,24 +385,18 @@ public void testWriteHistory() throws Exception {
.actionGet();
}
- TestAuditlogImpl.clear();
-
- String body = "{\"doc\": {\"Age\":123}}";
-
- HttpResponse response = rh.executePostRequest("humanresources/employees/100/_update?pretty", body, encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- System.out.println(response.getBody());
- Thread.sleep(1500);
- System.out.println(TestAuditlogImpl.sb.toString());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().split(".*audit_compliance_diff_content.*replace.*").length == 2);
-
- body = "{\"Age\":555}";
- TestAuditlogImpl.clear();
- response = rh.executePostRequest("humanresources/employees/100?pretty", body, encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- System.out.println(response.getBody());
- Thread.sleep(1500);
- System.out.println(TestAuditlogImpl.sb.toString());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().split(".*audit_compliance_diff_content.*replace.*").length == 2);
+ TestAuditlogImpl.doThenWaitForMessage(() -> {
+ final String body = "{\"doc\": {\"Age\":123}}";
+ final HttpResponse response = rh.executePostRequest("humanresources/_doc/100?pretty", body, encodeBasicHeader("admin", "admin"));
+ Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+ });
+ Assert.assertTrue(TestAuditlogImpl.sb.toString().split(".*audit_compliance_diff_content.*replace.*").length == 1);
+
+ TestAuditlogImpl.doThenWaitForMessage(() -> {
+ final String body = "{\"doc\": {\"Age\":555}}";
+ final HttpResponse response = rh.executePostRequest("humanresources/_update/100?pretty", body, encodeBasicHeader("admin", "admin"));
+ Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+ });
+ Assert.assertTrue(TestAuditlogImpl.sb.toString().split(".*audit_compliance_diff_content.*replace.*").length == 1);
}
}
diff --git a/src/test/java/org/opensearch/security/auditlog/integration/BasicAuditlogTest.java b/src/test/java/org/opensearch/security/auditlog/integration/BasicAuditlogTest.java
index 9c436849ae..779e528cf5 100644
--- a/src/test/java/org/opensearch/security/auditlog/integration/BasicAuditlogTest.java
+++ b/src/test/java/org/opensearch/security/auditlog/integration/BasicAuditlogTest.java
@@ -1,29 +1,26 @@
/*
- * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
*
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * or in the "license" file accompanying this file. This file 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.
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
*/
package org.opensearch.security.auditlog.integration;
-import org.opensearch.security.auditlog.AuditTestUtils;
-import org.opensearch.security.auditlog.config.AuditConfig;
-import org.opensearch.security.auditlog.impl.AuditCategory;
-import org.opensearch.security.compliance.ComplianceConfig;
+import java.util.Collections;
+import java.util.List;
+
import com.google.common.collect.ImmutableMap;
import org.apache.http.Header;
import org.apache.http.HttpStatus;
-import org.apache.http.NoHttpResponseException;
import org.apache.http.message.BasicHeader;
+import org.junit.Assert;
+import org.junit.Test;
+
import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.opensearch.action.admin.indices.create.CreateIndexRequest;
@@ -32,19 +29,28 @@
import org.opensearch.action.search.SearchResponse;
import org.opensearch.action.support.WriteRequest.RefreshPolicy;
import org.opensearch.client.transport.TransportClient;
+import org.opensearch.client.Client;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.util.concurrent.ThreadContext.StoredContext;
import org.opensearch.common.xcontent.XContentType;
-import org.junit.Assert;
-import org.junit.Test;
-
import org.opensearch.security.auditlog.AbstractAuditlogiUnitTest;
+import org.opensearch.security.auditlog.AuditLog.Origin;
+import org.opensearch.security.auditlog.AuditTestUtils;
+import org.opensearch.security.auditlog.config.AuditConfig;
+import org.opensearch.security.auditlog.impl.AuditCategory;
import org.opensearch.security.auditlog.impl.AuditMessage;
+import org.opensearch.security.compliance.ComplianceConfig;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.test.helper.file.FileHelper;
import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse;
-import java.util.Collections;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.opensearch.rest.RestRequest.Method.DELETE;
+import static org.opensearch.rest.RestRequest.Method.GET;
+import static org.opensearch.rest.RestRequest.Method.PATCH;
+import static org.opensearch.rest.RestRequest.Method.POST;
+import static org.opensearch.rest.RestRequest.Method.PUT;
public class BasicAuditlogTest extends AbstractAuditlogiUnitTest {
@@ -77,30 +83,29 @@ public void testAuditLogEnable() throws Exception {
@Test
public void testSimpleAuthenticated() throws Exception {
+ final Settings settings = Settings.builder()
+ .put("plugins.security.audit.type", TestAuditlogImpl.class.getName())
+ .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_TRANSPORT_CATEGORIES, "NONE")
+ .build();
+ verifyAuthenticated(settings);
+ }
- Settings additionalSettings = Settings.builder()
- .put("plugins.security.audit.type", TestAuditlogImpl.class.getName())
- .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, true)
- .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, true)
- .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_TRANSPORT_CATEGORIES, "authenticated")
- .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_CONFIG_DISABLED_REST_CATEGORIES, "authenticated")
- .build();
+ private void verifyAuthenticated(final Settings settings) throws Exception {
+ setup(settings);
- setup(additionalSettings);
- setupStarfleetIndex();
- TestAuditlogImpl.clear();
- System.out.println("#### testSimpleAuthenticated");
- HttpResponse response = rh.executeGetRequest("_search", encodeBasicHeader("admin", "admin"));
- Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
- Thread.sleep(1500);
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- System.out.println(TestAuditlogImpl.sb.toString());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("GRANTED_PRIVILEGES"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("indices:data/read/search"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("REST"));
- Assert.assertFalse(TestAuditlogImpl.sb.toString().toLowerCase().contains("authorization"));
- Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
+ final List messages = TestAuditlogImpl.doThenWaitForMessages(
+ () -> {
+ final HttpResponse response = rh.executeGetRequest("_search", encodeBasicHeader("admin", "admin"));
+ assertThat(response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+ },
+ /* expectedCount*/ 1);
+
+ assertThat(messages.size(), equalTo(1));
+
+ assertThat(messages.get(0).getCategory(), equalTo(AuditCategory.GRANTED_PRIVILEGES));
+ assertThat(messages.get(0).getOrigin(), equalTo(Origin.REST));
+ assertThat(messages.get(0).getPrivilege(), equalTo("indices:data/read/search"));
}
@Test
@@ -118,22 +123,18 @@ public void testSSLPlainText() throws Exception {
.build();
setup(additionalSettings);
- TestAuditlogImpl.clear();
-
- try {
- nonSslRestHelper().executeGetRequest("_search", encodeBasicHeader("admin", "admin"));
- Assert.fail();
- } catch (NoHttpResponseException e) {
- //expected
- }
-
- Thread.sleep(1500);
- System.out.println(TestAuditlogImpl.sb.toString());
- Assert.assertFalse(TestAuditlogImpl.messages.isEmpty());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("SSL_EXCEPTION"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("exception_stacktrace"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("not an SSL/TLS record"));
- Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
+ final List messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ final RuntimeException ex = Assert.assertThrows(RuntimeException.class,
+ () -> nonSslRestHelper().executeGetRequest("_search", encodeBasicHeader("admin", "admin")));
+ Assert.assertEquals("org.apache.http.NoHttpResponseException", ex.getCause().getClass().getName());
+ }, 4);
+
+ // All of the messages should be the same as the http client is attempting multiple times.
+ messages.stream().forEach((message) -> {
+ Assert.assertEquals(AuditCategory.SSL_EXCEPTION, message.getCategory());
+ Assert.assertTrue(message.getExceptionStackTrace().contains("not an SSL/TLS record"));
+ });
+ Assert.assertTrue(validateMsgs(messages));
}
@Test
@@ -396,7 +397,7 @@ public void testJustAuthenticated() throws Exception {
public void testSecurityIndexAttempt() throws Exception {
- HttpResponse response = rh.executePutRequest(".opendistro_security/config/0", "{}", encodeBasicHeader("admin", "admin"));
+ HttpResponse response = rh.executePutRequest(".opendistro_security/_doc/0", "{}", encodeBasicHeader("admin", "admin"));
Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("MISSING_PRIVILEGES"));
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("OPENDISTRO_SECURITY_INDEX_ATTEMPT"));
@@ -460,15 +461,15 @@ public void testBulkAuth() throws Exception {
System.out.println("#### testBulkAuth");
String bulkBody =
- "{ \"index\" : { \"_index\" : \"test\", \"_type\" : \"type1\", \"_id\" : \"1\" } }"+System.lineSeparator()+
+ "{ \"index\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }"+System.lineSeparator()+
"{ \"field1\" : \"value1\" }" +System.lineSeparator()+
- "{ \"index\" : { \"_index\" : \"worf\", \"_type\" : \"type1\", \"_id\" : \"2\" } }"+System.lineSeparator()+
+ "{ \"index\" : { \"_index\" : \"worf\", \"_id\" : \"2\" } }"+System.lineSeparator()+
"{ \"field2\" : \"value2\" }"+System.lineSeparator()+
- "{ \"update\" : {\"_id\" : \"1\", \"_type\" : \"type1\", \"_index\" : \"test\"} }"+System.lineSeparator()+
+ "{ \"update\" : {\"_id\" : \"1\", \"_index\" : \"test\"} }"+System.lineSeparator()+
"{ \"doc\" : {\"field\" : \"valuex\"} }"+System.lineSeparator()+
- "{ \"delete\" : { \"_index\" : \"test\", \"_type\" : \"type1\", \"_id\" : \"1\" } }"+System.lineSeparator()+
- "{ \"create\" : { \"_index\" : \"test\", \"_type\" : \"type1\", \"_id\" : \"1\" } }"+System.lineSeparator()+
+ "{ \"delete\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }"+System.lineSeparator()+
+ "{ \"create\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }"+System.lineSeparator()+
"{ \"field1\" : \"value3x\" }"+System.lineSeparator();
@@ -490,15 +491,15 @@ public void testBulkAuth() throws Exception {
public void testBulkNonAuth() throws Exception {
String bulkBody =
- "{ \"index\" : { \"_index\" : \"test\", \"_type\" : \"type1\", \"_id\" : \"1\" } }"+System.lineSeparator()+
+ "{ \"index\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }"+System.lineSeparator()+
"{ \"field1\" : \"value1\" }" +System.lineSeparator()+
- "{ \"index\" : { \"_index\" : \"worf\", \"_type\" : \"type1\", \"_id\" : \"2\" } }"+System.lineSeparator()+
+ "{ \"index\" : { \"_index\" : \"worf\", \"_id\" : \"2\" } }"+System.lineSeparator()+
"{ \"field2\" : \"value2\" }"+System.lineSeparator()+
- "{ \"update\" : {\"_id\" : \"1\", \"_type\" : \"type1\", \"_index\" : \"test\"} }"+System.lineSeparator()+
+ "{ \"update\" : {\"_id\" : \"1\", \"_index\" : \"test\"} }"+System.lineSeparator()+
"{ \"doc\" : {\"field\" : \"valuex\"} }"+System.lineSeparator()+
- "{ \"delete\" : { \"_index\" : \"test\", \"_type\" : \"type1\", \"_id\" : \"1\" } }"+System.lineSeparator()+
- "{ \"create\" : { \"_index\" : \"test\", \"_type\" : \"type1\", \"_id\" : \"1\" } }"+System.lineSeparator()+
+ "{ \"delete\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }"+System.lineSeparator()+
+ "{ \"create\" : { \"_index\" : \"test\", \"_id\" : \"1\" } }"+System.lineSeparator()+
"{ \"field1\" : \"value3x\" }"+System.lineSeparator();
HttpResponse response = rh.executePostRequest("_bulk", bulkBody, encodeBasicHeader("worf", "worf"));
@@ -522,10 +523,10 @@ public void testUpdateSettings() throws Exception {
String json =
"{"+
"\"persistent\" : {"+
- "\"discovery.zen.minimum_master_nodes\" : 1"+
+ "\"indices.recovery.*\" : null"+
"},"+
"\"transient\" : {"+
- "\"discovery.zen.minimum_master_nodes\" : 1"+
+ "\"indices.recovery.*\" : null"+
"}"+
"}";
@@ -534,8 +535,8 @@ public void testUpdateSettings() throws Exception {
System.out.println(TestAuditlogImpl.sb.toString());
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("AUTHENTICATED"));
Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("cluster:admin/settings/update"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("discovery.zen.minimum_master_nodes"));
- //may vary because we log may hit master directly or not
+ Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("indices.recovery.*"));
+ //may vary because we log may hit cluster manager directly or not
Assert.assertTrue(TestAuditlogImpl.messages.size() > 1);
Assert.assertTrue(validateMsgs(TestAuditlogImpl.messages));
}
@@ -813,6 +814,10 @@ public void testIndexRequests() throws Exception {
Assert.assertTrue(auditlogs.contains("\"audit_transport_request_type\" : \"DeleteIndexRequest\","));
}
+ private String messageRestRequestMethod(AuditMessage msg) {
+ return msg.getAsMap().get("audit_rest_request_method").toString();
+ }
+
@Test
public void testRestMethod() throws Exception {
final Settings settings = Settings.builder()
@@ -823,66 +828,70 @@ public void testRestMethod() throws Exception {
.build();
setup(settings);
final Header adminHeader = encodeBasicHeader("admin", "admin");
+ List messages;
// test GET
- TestAuditlogImpl.clear();
- rh.executeGetRequest("test", adminHeader);
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"GET\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executeGetRequest("test", adminHeader);
+ }, 1);
+ Assert.assertEquals(GET, messages.get(0).getRequestMethod());
// test PUT
- TestAuditlogImpl.clear();
- rh.executePutRequest("test/_doc/0", "{}", adminHeader);
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"PUT\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executePutRequest("test/_doc/0", "{}", adminHeader);
+ }, 1);
+ Assert.assertEquals(PUT, messages.get(0).getRequestMethod());
// test DELETE
- TestAuditlogImpl.clear();
- rh.executeDeleteRequest("test", adminHeader);
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"DELETE\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executeDeleteRequest("test", adminHeader);
+ }, 1);
+ Assert.assertEquals(DELETE, messages.get(0).getRequestMethod());
// test POST
- TestAuditlogImpl.clear();
- rh.executePostRequest("test/_doc", "{}", adminHeader);
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"POST\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executePostRequest("test/_doc", "{}", adminHeader);
+ }, 1);
+ Assert.assertEquals(POST, messages.get(0).getRequestMethod());
// test PATCH
- TestAuditlogImpl.clear();
- rh.executePatchRequest("/_opendistro/_security/api/audit", "[]");
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"PATCH\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executePatchRequest("/_opendistro/_security/api/audit", "[]");
+ }, 1);
+ Assert.assertEquals(PATCH, messages.get(0).getRequestMethod());
// test MISSING_PRIVILEGES
// admin does not have REST role here
- TestAuditlogImpl.clear();
- rh.executePatchRequest("/_opendistro/_security/api/audit", "[]", adminHeader);
- Assert.assertEquals(2, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("MISSING_PRIVILEGES"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("AUTHENTICATED"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"PATCH\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executePatchRequest("/_opendistro/_security/api/audit", "[]", adminHeader);
+ }, 2);
+ // The intital request is authenicated
+ Assert.assertEquals(PATCH, messages.get(0).getRequestMethod());
+ Assert.assertEquals(AuditCategory.AUTHENTICATED, messages.get(0).getCategory());
+ // The secondary request does not have permissions
+ Assert.assertEquals(PATCH, messages.get(1).getRequestMethod());
+ Assert.assertEquals(AuditCategory.MISSING_PRIVILEGES, messages.get(1).getCategory());
// test AUTHENTICATED
- TestAuditlogImpl.clear();
- rh.executeGetRequest("test", adminHeader);
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("AUTHENTICATED"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"GET\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executeGetRequest("test", adminHeader);
+ }, 1);
+ Assert.assertEquals(AuditCategory.AUTHENTICATED, messages.get(0).getCategory());
+ Assert.assertEquals(GET, messages.get(0).getRequestMethod());
// test FAILED_LOGIN
- TestAuditlogImpl.clear();
- rh.executeGetRequest("test", encodeBasicHeader("random", "random"));
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("FAILED_LOGIN"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"GET\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executeGetRequest("test", encodeBasicHeader("random", "random"));
+ }, 1);
+ Assert.assertEquals(AuditCategory.FAILED_LOGIN, messages.get(0).getCategory());
+ Assert.assertEquals(GET, messages.get(0).getRequestMethod());
// test BAD_HEADERS
- TestAuditlogImpl.clear();
- rh.executeGetRequest("test", new BasicHeader("_opendistro_security_user", "xxx"));
- Assert.assertEquals(1, TestAuditlogImpl.messages.size());
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("BAD_HEADERS"));
- Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("\"audit_rest_request_method\" : \"GET\""));
+ messages = TestAuditlogImpl.doThenWaitForMessages(() -> {
+ rh.executeGetRequest("test", new BasicHeader("_opendistro_security_user", "xxx"));
+ }, 1);
+ Assert.assertEquals(AuditCategory.BAD_HEADERS, messages.get(0).getCategory());
+ Assert.assertEquals(GET, messages.get(0).getRequestMethod());
}
@Test
diff --git a/src/test/java/org/opensearch/security/auditlog/integration/TestAuditlogImpl.java b/src/test/java/org/opensearch/security/auditlog/integration/TestAuditlogImpl.java
index ecafbfa469..b91ddee9fe 100644
--- a/src/test/java/org/opensearch/security/auditlog/integration/TestAuditlogImpl.java
+++ b/src/test/java/org/opensearch/security/auditlog/integration/TestAuditlogImpl.java
@@ -1,47 +1,128 @@
/*
- * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
*
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * or in the "license" file accompanying this file. This file 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.
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
*/
package org.opensearch.security.auditlog.integration;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
import org.opensearch.common.settings.Settings;
-
import org.opensearch.security.auditlog.impl.AuditMessage;
import org.opensearch.security.auditlog.sink.AuditLogSink;
public class TestAuditlogImpl extends AuditLogSink {
+ /** Use the results of `doThenWaitForMessages(...)` instead */
+ @Deprecated
public static List messages = new ArrayList(100);
+ /** Check messages indvidually instead of searching this string */
+ @Deprecated
public static StringBuffer sb = new StringBuffer();
+ private static final AtomicReference countDownRef = new AtomicReference<>();
+ private static final AtomicReference> messagesRef = new AtomicReference<>();
public TestAuditlogImpl(String name, Settings settings, String settingsPrefix, AuditLogSink fallbackSink) {
super(name, settings, null, fallbackSink);
}
-
- public synchronized boolean doStore(AuditMessage msg) {
+ public synchronized boolean doStore(AuditMessage msg) {
+ if (messagesRef.get() == null || countDownRef.get() == null) {
+ // Ignore any messages that are sent before TestAuditlogImpl is waiting.
+ return true;
+ }
sb.append(msg.toPrettyString()+System.lineSeparator());
- messages.add(msg);
+ messagesRef.get().add(msg);
+ countDownRef.get().countDown();
return true;
}
+ /** Unneeded after switching to `doThenWaitForMessages(...)` as data is automatically flushed */
+ @Deprecated
public static synchronized void clear() {
- sb.setLength(0);
- messages.clear();
+ doThenWaitForMessages(() -> {}, 0);
+ }
+
+ /**
+ * Perform an action and then wait until the expected number of messages have been found.
+ */
+ public static List doThenWaitForMessages(final Runnable action, final int expectedCount) {
+ final List missedMessages = new ArrayList<>();
+ final List messages = new ArrayList<>();
+ final CountDownLatch latch = resetAuditStorage(expectedCount, messages);
+
+ try {
+ action.run();
+ final int maxSecondsToWaitForMessages = 1;
+ boolean foundAll = false;
+ foundAll = latch.await(maxSecondsToWaitForMessages, TimeUnit.SECONDS);
+ // After the wait has prevent any new messages from being recieved
+ resetAuditStorage(0, missedMessages);
+ if (!foundAll || messages.size() != expectedCount) {
+ throw new MessagesNotFoundException(expectedCount, messages);
+ }
+ } catch (final InterruptedException e) {
+ throw new RuntimeException("Unexpected exception", e);
+ }
+
+ // Do not check for missed messages if no messages were expected
+ if (expectedCount != 0) {
+ try {
+ Thread.sleep(100);
+ if (missedMessages.size() != 0) {
+ final String missedMessagesErrorMessage = new StringBuilder()
+ .append("Audit messages were missed! ")
+ .append("Found " + (missedMessages.size()) + " messages.")
+ .append("Messages found during this time: \n\n")
+ .append(missedMessages.stream()
+ .map(AuditMessage::toString)
+ .collect(Collectors.joining("\n")))
+ .toString();
+
+ throw new RuntimeException(missedMessagesErrorMessage);
+ }
+ } catch (final Exception e) {
+ throw new RuntimeException("Unexpected exception", e);
+ }
+ }
+
+ // Next usage of this class might be using raw stringbuilder / list so reset before that test might run
+ resetAuditStorage(0, new ArrayList<>());
+ return new ArrayList<>(messages);
+ }
+
+ /**
+ * Resets all of the mechanics for fresh messages to be captured
+ *
+ * @param expectedMessageCount The number of messages before the latch is signalled, indicating all messages have been recieved
+ * @param message Where messages will be stored after being recieved
+ */
+ private static CountDownLatch resetAuditStorage(int expectedMessageCount, List messages) {
+ final CountDownLatch latch = new CountDownLatch(expectedMessageCount);
+ countDownRef.set(latch);
+ messagesRef.set(messages);
+
+ TestAuditlogImpl.sb = new StringBuffer();
+ TestAuditlogImpl.messages = messages;
+ return latch;
+ }
+
+ /**
+ * Perform an action and then wait until a single message has been found.
+ */
+ public static AuditMessage doThenWaitForMessage(final Runnable action) {
+ return doThenWaitForMessages(action, 1).get(0);
}
@Override
@@ -49,5 +130,38 @@ public boolean isHandlingBackpressure() {
return true;
}
+ public static class MessagesNotFoundException extends RuntimeException {
+ private final int expectedCount;
+ private final int missingCount;
+ private final List foundMessages;
+ public MessagesNotFoundException(final int expectedCount, List foundMessages) {
+ super(MessagesNotFoundException.createDetailMessage(expectedCount, foundMessages));
+ this.expectedCount = expectedCount;
+ this.missingCount = expectedCount - foundMessages.size();
+ this.foundMessages = foundMessages;
+ }
+ public int getExpectedCount() {
+ return expectedCount;
+ }
+
+ public int getMissingCount() {
+ return missingCount;
+ }
+
+ public List getFoundMessages() {
+ return foundMessages;
+ }
+
+ private static String createDetailMessage(final int expectedCount, final List foundMessages) {
+ return new StringBuilder()
+ .append("Did not receive all " + expectedCount + " audit messages after a short wait. ")
+ .append("Missing " + (expectedCount - foundMessages.size()) + " messages.")
+ .append("Messages found during this time: \n\n")
+ .append(foundMessages.stream()
+ .map(AuditMessage::toString)
+ .collect(Collectors.joining("\n")))
+ .toString();
+ }
+ }
}
diff --git a/src/test/java/org/opensearch/security/auditlog/sink/SinkProviderTLSTest.java b/src/test/java/org/opensearch/security/auditlog/sink/SinkProviderTLSTest.java
index 4f71ca577c..f59420fee4 100644
--- a/src/test/java/org/opensearch/security/auditlog/sink/SinkProviderTLSTest.java
+++ b/src/test/java/org/opensearch/security/auditlog/sink/SinkProviderTLSTest.java
@@ -15,8 +15,11 @@
package org.opensearch.security.auditlog.sink;
+import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
+import java.net.ServerSocket;
+import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
@@ -59,11 +62,13 @@ public void testTlsConfigurationNoFallback() throws Exception {
TestHttpHandler handler = new TestHttpHandler();
- server = ServerBootstrap.bootstrap().setListenerPort(8083).setServerInfo("Test/1.1").setSslContext(createSSLContext()).registerHandler("*", handler).create();
+ int port = findFreePort();
+ server = ServerBootstrap.bootstrap().setListenerPort(port).setServerInfo("Test/1.1").setSslContext(createSSLContext()).registerHandler("*", handler).create();
server.start();
- Builder builder = Settings.builder().loadFromPath(FileHelper.getAbsoluteFilePathFromClassPath("auditlog/endpoints/sink/configuration_tls.yml"));
+ final byte[] configAsBytes = getConfigurationAsString(port).getBytes(StandardCharsets.UTF_8);
+ Builder builder = Settings.builder().loadFromStream("configuration_tls.yml", new ByteArrayInputStream(configAsBytes), false);
builder.put("path.home", "/");
// replace some values with absolute paths for unit tests
@@ -141,4 +146,58 @@ private void assertStringContainsAllKeysAndValues(String in) {
Assert.assertTrue(in, in.contains("8.8.8.8"));
//Assert.assertTrue(in, in.contains("CN=kirk,OU=client,O=client,L=test,C=DE"));
}
+
+ private int findFreePort() {
+ try (ServerSocket serverSocket = new ServerSocket(0)) {
+ return serverSocket.getLocalPort();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to find free port", e);
+ }
+ }
+
+ private String getConfigurationAsString(final int port) {
+ return "plugins.security.ssl.transport.enabled: true\n" +
+"plugins.security.ssl.transport.keystore_filepath: \"transport.keystore_filepath\"\n" +
+"plugins.security.ssl.transport.truststore_filepath: \"transport.truststore_filepath\"\n" +
+"plugins.security.ssl.transport.enforce_hostname_verification: true\n" +
+"plugins.security.ssl.transport.resolve_hostname: true\n" +
+"plugins.security.ssl.transport.enable_openssl_if_available: true\n" +
+"plugins.security.ssl.http.enabled: true\n" +
+"plugins.security.ssl.http.keystore_filepath: \"http.keystore_filepath\"\n" +
+"plugins.security.ssl.http.truststore_filepath: \"http.truststore_filepath\"\n" +
+"plugins.security.ssl.http.enable_openssl_if_available: true\n" +
+"plugins.security.ssl.http.clientauth_mode: OPTIONAL\n" +
+"\n" +
+"plugins.security:\n" +
+" audit:\n" +
+" type: webhook\n" +
+" config:\n" +
+" webhook:\n" +
+" url: https://localhost:" + port + "\n" +
+" format: JSON\n" +
+" ssl:\n" +
+" verify: true\n" +
+" pemtrustedcas_filepath: dyn\n" +
+" endpoints:\n" +
+" endpoint1:\n" +
+" type: webhook\n" +
+" config:\n" +
+" webhook:\n" +
+" url: https://localhost:" + port + "\n" +
+" format: JSON\n" +
+" ssl:\n" +
+" verify: true\n" +
+" pemtrustedcas_filepath: dyn\n" +
+" endpoint2:\n" +
+" type: webhook\n" +
+" config:\n" +
+" webhook:\n" +
+" url: https://localhost:" + port + "\n" +
+" format: JSON\n" +
+" ssl:\n" +
+" verify: true\n" +
+" pemtrustedcas_content: dyn\n" +
+" fallback:\n" +
+" type: org.opensearch.security.auditlog.helper.LoggingSink";
+ }
}
diff --git a/src/test/java/org/opensearch/security/auditlog/sink/WebhookAuditLogTest.java b/src/test/java/org/opensearch/security/auditlog/sink/WebhookAuditLogTest.java
index e28f818397..2efe9ad40c 100644
--- a/src/test/java/org/opensearch/security/auditlog/sink/WebhookAuditLogTest.java
+++ b/src/test/java/org/opensearch/security/auditlog/sink/WebhookAuditLogTest.java
@@ -16,7 +16,9 @@
package org.opensearch.security.auditlog.sink;
import java.io.FileInputStream;
+import java.io.IOException;
import java.io.InputStream;
+import java.net.ServerSocket;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
@@ -222,15 +224,16 @@ public void noServerRunningHttpTest() throws Exception {
public void postGetHttpTest() throws Exception {
TestHttpHandler handler = new TestHttpHandler();
+ int port = findFreePort();
server = ServerBootstrap.bootstrap()
- .setListenerPort(8080)
+ .setListenerPort(port)
.setServerInfo("Test/1.1")
.registerHandler("*", handler)
.create();
server.start();
- String url = "http://localhost:8080/endpoint";
+ String url = "http://localhost:" + port + "/endpoint";
// SLACK
Settings settings = Settings.builder()
@@ -327,15 +330,16 @@ public void httpsTestWithoutTLSServer() throws Exception {
TestHttpHandler handler = new TestHttpHandler();
+ int port = findFreePort();
server = ServerBootstrap.bootstrap()
- .setListenerPort(8081)
+ .setListenerPort(port)
.setServerInfo("Test/1.1")
.registerHandler("*", handler)
.create();
server.start();
- String url = "https://localhost:8081/endpoint";
+ String url = "https://localhost:" + port + "/endpoint";
Settings settings = Settings.builder()
.put("plugins.security.audit.config.webhook.url", url)
@@ -363,9 +367,9 @@ public void httpsTestWithoutTLSServer() throws Exception {
public void httpsTest() throws Exception {
TestHttpHandler handler = new TestHttpHandler();
-
+ int port = findFreePort();
server = ServerBootstrap.bootstrap()
- .setListenerPort(8090)
+ .setListenerPort(port)
.setServerInfo("Test/1.1")
.setSslContext(createSSLContext())
.registerHandler("*", handler)
@@ -374,7 +378,7 @@ public void httpsTest() throws Exception {
server.start();
AuditMessage msg = MockAuditMessageFactory.validAuditMessage();
- String url = "https://localhost:8090/endpoint";
+ String url = "https://localhost:" + port + "/endpoint";
// try with ssl verification on, no trust ca, must fail
Settings settings = Settings.builder()
@@ -445,8 +449,8 @@ public void httpsTest() throws Exception {
@Test
public void httpsTestPemDefault() throws Exception {
- final int port = 8088;
- TestHttpHandler handler = new TestHttpHandler();
+ final int port = findFreePort();
+ TestHttpHandler handler = new TestHttpHandler();
server = ServerBootstrap.bootstrap()
.setListenerPort(port)
@@ -561,9 +565,10 @@ public void httpsTestPemDefault() throws Exception {
public void httpsTestPemEndpoint() throws Exception {
TestHttpHandler handler = new TestHttpHandler();
+ int port = findFreePort();
server = ServerBootstrap.bootstrap()
- .setListenerPort(8091)
+ .setListenerPort(port)
.setServerInfo("Test/1.1")
.setSslContext(createSSLContext())
.registerHandler("*", handler)
@@ -573,7 +578,7 @@ public void httpsTestPemEndpoint() throws Exception {
AuditMessage msg = MockAuditMessageFactory.validAuditMessage();
LoggingSink fallback = new LoggingSink("test", Settings.EMPTY, null, null);
- String url = "https://localhost:8091/endpoint";
+ String url = "https://localhost:" + port + "/endpoint";
// test default with filepath
handler.reset();
@@ -658,9 +663,10 @@ public void httpsTestPemEndpoint() throws Exception {
public void httpsTestPemContentEndpoint() throws Exception {
TestHttpHandler handler = new TestHttpHandler();
+ int port = findFreePort();
server = ServerBootstrap.bootstrap()
- .setListenerPort(8086)
+ .setListenerPort(port)
.setServerInfo("Test/1.1")
.setSslContext(createSSLContext())
.registerHandler("*", handler)
@@ -670,7 +676,7 @@ public void httpsTestPemContentEndpoint() throws Exception {
AuditMessage msg = MockAuditMessageFactory.validAuditMessage();
LoggingSink fallback = new LoggingSink("test", Settings.EMPTY, null, null);
- String url = "https://localhost:8086/endpoint";
+ String url = "https://localhost:" + port + "/endpoint";
// test with filecontent
handler.reset();
@@ -731,4 +737,12 @@ private void assertStringContainsAllKeysAndValues(String in) {
Assert.assertTrue(in, in.contains("8.8.8.8"));
//Assert.assertTrue(in, in.contains("CN=kirk,OU=client,O=client,L=test,C=DE"));
}
+
+ private int findFreePort() {
+ try (ServerSocket serverSocket = new ServerSocket(0)) {
+ return serverSocket.getLocalPort();
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to find free port", e);
+ }
+ }
}
diff --git a/src/test/java/org/opensearch/security/auth/limiting/HeapBasedRateTrackerTest.java b/src/test/java/org/opensearch/security/auth/limiting/HeapBasedRateTrackerTest.java
index 3637211eec..ae0fa500d2 100644
--- a/src/test/java/org/opensearch/security/auth/limiting/HeapBasedRateTrackerTest.java
+++ b/src/test/java/org/opensearch/security/auth/limiting/HeapBasedRateTrackerTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import org.junit.Ignore;
import org.junit.Test;
import org.opensearch.security.util.ratetracking.HeapBasedRateTracker;
@@ -39,6 +40,7 @@ public void simpleTest() throws Exception {
}
@Test
+ @Ignore // https://github.com/opensearch-project/security/issues/2193
public void expiryTest() throws Exception {
HeapBasedRateTracker tracker = new HeapBasedRateTracker<>(100, 5, 100_000);
@@ -78,6 +80,7 @@ public void expiryTest() throws Exception {
}
@Test
+ @Ignore // https://github.com/opensearch-project/security/issues/2193
public void maxTwoTriesTest() throws Exception {
HeapBasedRateTracker tracker = new HeapBasedRateTracker<>(100, 2, 100_000);
diff --git a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java
index 3d4f290652..cf1cb18eb2 100644
--- a/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java
+++ b/src/test/java/org/opensearch/security/dlic/dlsfls/DlsTest.java
@@ -375,7 +375,7 @@ public void testDlsWithMinDocCountZeroAggregations() throws Exception {
// Significant Text Aggregation is not impacted.
// Non-admin user with setting "min_doc_count=0". Expected to only have access to buckets for dept_manager".
- String query3 = "{\"aggregations\":{\"significant_termX\":{\"significant_terms\":{\"field\":\"termX.keyword\",\"min_doc_count\":0}}}}";
+ String query3 = "{\"size\":100,\"aggregations\":{\"significant_termX\":{\"significant_terms\":{\"field\":\"termX.keyword\",\"min_doc_count\":0}}}}";
HttpResponse response5 = rh.executePostRequest("logs*/_search", query3, encodeBasicHeader("dept_manager", "password"));
Assert.assertEquals(HttpStatus.SC_OK, response5.getStatusCode());
@@ -386,7 +386,7 @@ public void testDlsWithMinDocCountZeroAggregations() throws Exception {
Assert.assertFalse(response5.getBody(), response5.getBody().contains("\"termX\":\"E\""));
// Non-admin user without setting "min_doc_count". Expected to only have access to buckets for dept_manager".
- String query4 = "{\"aggregations\":{\"significant_termX\":{\"significant_terms\":{\"field\":\"termX.keyword\"}}}}";
+ String query4 = "{\"size\":100,\"aggregations\":{\"significant_termX\":{\"significant_terms\":{\"field\":\"termX.keyword\"}}}}";
HttpResponse response6 = rh.executePostRequest("logs*/_search", query4, encodeBasicHeader("dept_manager", "password"));
@@ -419,7 +419,7 @@ public void testDlsWithMinDocCountZeroAggregations() throws Exception {
// Histogram Aggregation is not impacted.
// Non-admin user with setting "min_doc_count=0". Expected to only have access to buckets for dept_manager".
- String query5 = "{\"aggs\":{\"amount\":{\"histogram\":{\"field\":\"amount\",\"interval\":1,\"min_doc_count\":0}}}}";
+ String query5 = "{\"size\":100,\"aggs\":{\"amount\":{\"histogram\":{\"field\":\"amount\",\"interval\":1,\"min_doc_count\":0}}}}";
HttpResponse response9 = rh.executePostRequest("logs*/_search", query5, encodeBasicHeader("dept_manager", "password"));
@@ -431,7 +431,7 @@ public void testDlsWithMinDocCountZeroAggregations() throws Exception {
Assert.assertFalse(response9.getBody(), response9.getBody().contains("\"termX\":\"E\""));
// Non-admin user without setting "min_doc_count". Expected to only have access to buckets for dept_manager".
- String query6 = "{\"aggs\":{\"amount\":{\"histogram\":{\"field\":\"amount\",\"interval\":1}}}}";
+ String query6 = "{\"size\":100,\"aggs\":{\"amount\":{\"histogram\":{\"field\":\"amount\",\"interval\":1}}}}";
HttpResponse response10 = rh.executePostRequest("logs*/_search", query6, encodeBasicHeader("dept_manager", "password"));
@@ -465,7 +465,7 @@ public void testDlsWithMinDocCountZeroAggregations() throws Exception {
// Date Histogram Aggregation is not impacted.
// Non-admin user with setting "min_doc_count=0". Expected to only have access to buckets for dept_manager".
- String query7 = "{\"aggs\":{\"timestamp\":{\"date_histogram\":{\"field\":\"timestamp\",\"calendar_interval\":\"month\",\"min_doc_count\":0}}}}";
+ String query7 = "{\"size\":100,\"aggs\":{\"timestamp\":{\"date_histogram\":{\"field\":\"timestamp\",\"calendar_interval\":\"month\",\"min_doc_count\":0}}}}";
HttpResponse response13 = rh.executePostRequest("logs*/_search", query7, encodeBasicHeader("dept_manager", "password"));
@@ -477,7 +477,7 @@ public void testDlsWithMinDocCountZeroAggregations() throws Exception {
Assert.assertFalse(response13.getBody(), response13.getBody().contains("\"termX\":\"E\""));
// Non-admin user without setting "min_doc_count". Expected to only have access to buckets for dept_manager".
- String query8 = "{\"aggs\":{\"timestamp\":{\"date_histogram\":{\"field\":\"timestamp\",\"calendar_interval\":\"month\"}}}}";
+ String query8 = "{\"size\":100,\"aggs\":{\"timestamp\":{\"date_histogram\":{\"field\":\"timestamp\",\"calendar_interval\":\"month\"}}}}";
HttpResponse response14 = rh.executePostRequest("logs*/_search", query8, encodeBasicHeader("dept_manager", "password"));
diff --git a/src/test/java/org/opensearch/security/ssl/OpenSSLTest.java b/src/test/java/org/opensearch/security/ssl/OpenSSLTest.java
index 3474901274..0b74dfb5d2 100644
--- a/src/test/java/org/opensearch/security/ssl/OpenSSLTest.java
+++ b/src/test/java/org/opensearch/security/ssl/OpenSSLTest.java
@@ -65,23 +65,10 @@ public static void restoreNettyDefaultAllocator() {
@Before
public void setup() {
+ Assume.assumeFalse(PlatformDependent.isWindows());
allowOpenSSL = true;
}
- @Test
- public void testEnsureOpenSSLAvailability() {
- //Assert.assertTrue("OpenSSL not available: "+String.valueOf(OpenSsl.unavailabilityCause()), OpenSsl.isAvailable());
-
- final String openSSLOptional = System.getenv("OPENDISTRO_SECURITY_TEST_OPENSSL_OPT");
- System.out.println("OPENDISTRO_SECURITY_TEST_OPENSSL_OPT "+openSSLOptional);
- if(!Boolean.parseBoolean(openSSLOptional)) {
- System.out.println("OpenSSL must be available");
- Assert.assertTrue("OpenSSL not available: "+String.valueOf(OpenSsl.unavailabilityCause()), OpenSsl.isAvailable());
- } else {
- System.out.println("OpenSSL can be available");
- }
- }
-
@Override
@Test
public void testHttps() throws Exception {
diff --git a/src/test/java/org/opensearch/security/ssl/SSLTest.java b/src/test/java/org/opensearch/security/ssl/SSLTest.java
index e19b68bd5f..eb3560cb2f 100644
--- a/src/test/java/org/opensearch/security/ssl/SSLTest.java
+++ b/src/test/java/org/opensearch/security/ssl/SSLTest.java
@@ -61,6 +61,7 @@
import org.opensearch.transport.Netty4Plugin;
import org.junit.Assert;
import org.junit.Assume;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -522,6 +523,7 @@ public void testTransportClientSSL() throws Exception {
}
@Test
+ @Ignore // Has an external dependency that is flaky, not used in main
public void testTransportClientSSLExternalContext() throws Exception {
final Settings settings = Settings.builder().put("plugins.security.ssl.transport.enabled", true)
diff --git a/src/test/java/org/opensearch/security/support/SecurityUtilsTest.java b/src/test/java/org/opensearch/security/support/SecurityUtilsTest.java
new file mode 100644
index 0000000000..920bc4596d
--- /dev/null
+++ b/src/test/java/org/opensearch/security/support/SecurityUtilsTest.java
@@ -0,0 +1,79 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+package org.opensearch.security.support;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.opensearch.security.support.SecurityUtils.ENVBASE64_PATTERN;
+import static org.opensearch.security.support.SecurityUtils.ENVBC_PATTERN;
+import static org.opensearch.security.support.SecurityUtils.ENV_PATTERN;
+
+public class SecurityUtilsTest {
+
+ private final Collection interestingEnvKeyNames = Arrays.asList(
+ "=ExitCode",
+ "=C:",
+ "ProgramFiles(x86)",
+ "INPUT_GRADLE-HOME-CACHE-CLEANUP",
+ "MYENV",
+ "MYENV:",
+ "MYENV::",
+ "JAVA_HOME_14.0.2_x64"
+ );
+ private final Collection namesFromThisRuntimeEnvironment = System.getenv().keySet();
+
+ @Test
+ public void checkInterestingNamesForEnvPattern() {
+ checkKeysWithPredicate(interestingEnvKeyNames, "env", asMatchPredicate(ENV_PATTERN));
+ }
+
+ @Test
+ public void checkRuntimeKeyNamesForEnvPattern() {
+ checkKeysWithPredicate(namesFromThisRuntimeEnvironment, "env", asMatchPredicate(ENV_PATTERN));
+ }
+
+ @Test
+ public void checkInterestingNamesForEnvbcPattern() {
+ checkKeysWithPredicate(interestingEnvKeyNames, "envbc", asMatchPredicate(ENVBC_PATTERN));
+ }
+
+ @Test
+ public void checkInterestingNamesForEnvBase64Pattern() {
+ checkKeysWithPredicate(interestingEnvKeyNames, "envbase64", asMatchPredicate(ENVBASE64_PATTERN));
+ }
+
+ private Predicate asMatchPredicate(final Pattern p) {
+ return (String s) -> p.matcher(s).matches();
+ }
+
+ private void checkKeysWithPredicate(Collection keys, String predicateName, Predicate predicate) {
+ keys.forEach(envKeyName -> {
+ final String prefixWithKeyName = "${" + predicateName + "." + envKeyName;
+
+ final String baseKeyName = prefixWithKeyName + "}";
+ assertThat("Testing " + envKeyName + ", " + baseKeyName,
+ predicate.test(baseKeyName),
+ equalTo(true));
+
+ final String baseKeyNameWithDefault = prefixWithKeyName + ":-tTt}";
+ assertThat("Testing " + envKeyName + " with defaultValue, " + baseKeyNameWithDefault,
+ predicate.test(baseKeyNameWithDefault),
+ equalTo(true));
+ });
+ }
+}
diff --git a/src/test/java/org/opensearch/security/test/AbstractSecurityUnitTest.java b/src/test/java/org/opensearch/security/test/AbstractSecurityUnitTest.java
index 5b72a6a03a..8108fc0482 100644
--- a/src/test/java/org/opensearch/security/test/AbstractSecurityUnitTest.java
+++ b/src/test/java/org/opensearch/security/test/AbstractSecurityUnitTest.java
@@ -199,23 +199,7 @@ protected void initialize(ClusterInfo info, Settings initTransportClientSettings
Assert.assertEquals(info.numNodes, cur.getNodes().size());
SearchResponse sr = tc.search(new SearchRequest(".opendistro_security")).actionGet();
- //Assert.assertEquals(5L, sr.getHits().getTotalHits());
-
sr = tc.search(new SearchRequest(".opendistro_security")).actionGet();
- //Assert.assertEquals(5L, sr.getHits().getTotalHits());
-
- String type=securityConfig.getType();
-
- Assert.assertTrue(tc.get(new GetRequest(".opendistro_security", type, "config")).actionGet().isExists());
- Assert.assertTrue(tc.get(new GetRequest(".opendistro_security", type,"internalusers")).actionGet().isExists());
- Assert.assertTrue(tc.get(new GetRequest(".opendistro_security", type,"roles")).actionGet().isExists());
- Assert.assertTrue(tc.get(new GetRequest(".opendistro_security", type,"rolesmapping")).actionGet().isExists());
- Assert.assertTrue(tc.get(new GetRequest(".opendistro_security", type,"actiongroups")).actionGet().isExists());
- Assert.assertFalse(tc.get(new GetRequest(".opendistro_security", type,"rolesmapping_xcvdnghtu165759i99465")).actionGet().isExists());
- Assert.assertTrue(tc.get(new GetRequest(".opendistro_security", type,"config")).actionGet().isExists());
- if (indexRequests.stream().anyMatch(i -> CType.NODESDN.toLCString().equals(i.id()))) {
- Assert.assertTrue(tc.get(new GetRequest(".opendistro_security", type,"nodesdn")).actionGet().isExists());
- }
}
}
diff --git a/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java b/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java
index c35a7a01be..32e8d993f8 100644
--- a/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java
+++ b/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java
@@ -32,6 +32,7 @@
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.Arrays;
@@ -143,48 +144,48 @@ public HttpResponse[] executeMultipleAsyncPutRequest(final int numOfRequests, fi
.toArray(s -> new HttpResponse[s]);
}
- public HttpResponse executeGetRequest(final String request, Header... header) throws Exception {
+ public HttpResponse executeGetRequest(final String request, Header... header) {
return executeRequest(new HttpGet(getHttpServerUri() + "/" + request), header);
}
- public HttpResponse executeHeadRequest(final String request, Header... header) throws Exception {
+ public HttpResponse executeHeadRequest(final String request, Header... header) {
return executeRequest(new HttpHead(getHttpServerUri() + "/" + request), header);
}
- public HttpResponse executeOptionsRequest(final String request) throws Exception {
+ public HttpResponse executeOptionsRequest(final String request) {
return executeRequest(new HttpOptions(getHttpServerUri() + "/" + request));
}
- public HttpResponse executePutRequest(final String request, String body, Header... header) throws Exception {
+ public HttpResponse executePutRequest(final String request, String body, Header... header) {
HttpPut uriRequest = new HttpPut(getHttpServerUri() + "/" + request);
if (body != null && !body.isEmpty()) {
- uriRequest.setEntity(new StringEntity(body));
+ uriRequest.setEntity(createStringEntity(body));
}
return executeRequest(uriRequest, header);
}
- public HttpResponse executeDeleteRequest(final String request, Header... header) throws Exception {
+ public HttpResponse executeDeleteRequest(final String request, Header... header) {
return executeRequest(new HttpDelete(getHttpServerUri() + "/" + request), header);
}
- public HttpResponse executePostRequest(final String request, String body, Header... header) throws Exception {
+ public HttpResponse executePostRequest(final String request, String body, Header... header) {
HttpPost uriRequest = new HttpPost(getHttpServerUri() + "/" + request);
if (body != null && !body.isEmpty()) {
- uriRequest.setEntity(new StringEntity(body));
+ uriRequest.setEntity(createStringEntity(body));
}
return executeRequest(uriRequest, header);
}
- public HttpResponse executePatchRequest(final String request, String body, Header... header) throws Exception {
+ public HttpResponse executePatchRequest(final String request, String body, Header... header) {
HttpPatch uriRequest = new HttpPatch(getHttpServerUri() + "/" + request);
if (body != null && !body.isEmpty()) {
- uriRequest.setEntity(new StringEntity(body));
+ uriRequest.setEntity(createStringEntity(body));
}
return executeRequest(uriRequest, header);
}
- public HttpResponse executeRequest(HttpUriRequest uriRequest, Header... header) throws Exception {
+ public HttpResponse executeRequest(HttpUriRequest uriRequest, Header... header) {
CloseableHttpClient httpClient = null;
try {
@@ -204,13 +205,27 @@ public HttpResponse executeRequest(HttpUriRequest uriRequest, Header... header)
HttpResponse res = new HttpResponse(httpClient.execute(uriRequest));
log.debug(res.getBody());
return res;
+ } catch (final Exception e) {
+ throw new RuntimeException(e);
} finally {
if (httpClient != null) {
- httpClient.close();
+ try {
+ httpClient.close();
+ } catch (final Exception e) {
+ throw new RuntimeException(e);
+ }
}
}
}
+
+ private StringEntity createStringEntity(String body) {
+ try {
+ return new StringEntity(body);
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
protected final String getHttpServerUri() {
final String address = "http" + (enableHTTPClientSSL ? "s" : "") + "://" + clusterInfo.httpHost + ":" + clusterInfo.httpPort;