diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/ADTestHelpers.kt b/alerting/src/test/kotlin/org/opensearch/alerting/ADTestHelpers.kt index ea7b4d7c2..d6058b0ec 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/ADTestHelpers.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/ADTestHelpers.kt @@ -524,6 +524,6 @@ fun randomADMonitor( fun randomADUser(backendRole: String = OpenSearchRestTestCase.randomAlphaOfLength(10)): User { return User( OpenSearchRestTestCase.randomAlphaOfLength(10), listOf(backendRole), - listOf(OpenSearchRestTestCase.randomAlphaOfLength(10), "all_access"), listOf("test_attr=test") + listOf(OpenSearchRestTestCase.randomAlphaOfLength(10), ALL_ACCESS_ROLE), listOf("test_attr=test") ) } diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/AlertingRestTestCase.kt b/alerting/src/test/kotlin/org/opensearch/alerting/AlertingRestTestCase.kt index e6f14fbbd..e36231383 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/AlertingRestTestCase.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/AlertingRestTestCase.kt @@ -893,6 +893,12 @@ abstract class AlertingRestTestCase : ODFERestTestCase() { client().makeRequest("DELETE", "/_plugins/_security/api/rolesmapping/$name") } + fun deleteRoleAndRoleMapping(role: String, roleMapping: String) { + deleteRoleMapping(role) + deleteRole(role) + deleteRoleMapping(roleMapping) + } + fun createUserWithTestData(user: String, index: String, role: String, backendRole: String) { createUser(user, user, arrayOf(backendRole)) createTestIndex(index) diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/TestHelpers.kt b/alerting/src/test/kotlin/org/opensearch/alerting/TestHelpers.kt index 60d632dc9..ec370549f 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/TestHelpers.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/TestHelpers.kt @@ -227,13 +227,21 @@ fun randomEmailGroup( fun randomScript(source: String = "return " + OpenSearchRestTestCase.randomBoolean().toString()): Script = Script(source) +val ADMIN = "admin" val ALERTING_BASE_URI = "/_plugins/_alerting/monitors" +val ALERTING_FULL_ACCESS_ROLE = "alerting_full_access" +val ALL_ACCESS_ROLE = "all_access" val DESTINATION_BASE_URI = "/_plugins/_alerting/destinations" val LEGACY_OPENDISTRO_ALERTING_BASE_URI = "/_opendistro/_alerting/monitors" val LEGACY_OPENDISTRO_DESTINATION_BASE_URI = "/_opendistro/_alerting/destinations" val ALWAYS_RUN = Script("return true") val NEVER_RUN = Script("return false") val DRYRUN_MONITOR = mapOf("dryrun" to "true") +val TEST_HR_INDEX = "hr_data" +val TEST_NON_HR_INDEX = "not_hr_data" +val TEST_HR_ROLE = "hr_role" +val TEST_HR_BACKEND_ROLE = "HR" +val TERM_DLS_QUERY = "{\"term\": { \"accessible\": true}}" fun randomTemplateScript( source: String, @@ -420,7 +428,7 @@ fun randomUser(): User { OpenSearchRestTestCase.randomAlphaOfLength(10), OpenSearchRestTestCase.randomAlphaOfLength(10) ), - listOf(OpenSearchRestTestCase.randomAlphaOfLength(10), "all_access"), + listOf(OpenSearchRestTestCase.randomAlphaOfLength(10), ALL_ACCESS_ROLE), listOf("test_attr=test") ) } diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/model/DestinationTests.kt b/alerting/src/test/kotlin/org/opensearch/alerting/model/DestinationTests.kt index a9668bab7..527d5dc54 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/model/DestinationTests.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/model/DestinationTests.kt @@ -26,6 +26,7 @@ package org.opensearch.alerting.model +import org.opensearch.alerting.ADMIN import org.opensearch.alerting.model.destination.Chime import org.opensearch.alerting.model.destination.CustomWebhook import org.opensearch.alerting.model.destination.Destination @@ -193,8 +194,8 @@ class DestinationTests : OpenSearchTestCase() { "POST", mutableMapOf(), mutableMapOf(), - "admin", - "admin" + ADMIN, + ADMIN ), null ) diff --git a/alerting/src/test/kotlin/org/opensearch/alerting/resthandler/SecureMonitorRestApiIT.kt b/alerting/src/test/kotlin/org/opensearch/alerting/resthandler/SecureMonitorRestApiIT.kt index d87268ccf..9cf6995ea 100644 --- a/alerting/src/test/kotlin/org/opensearch/alerting/resthandler/SecureMonitorRestApiIT.kt +++ b/alerting/src/test/kotlin/org/opensearch/alerting/resthandler/SecureMonitorRestApiIT.kt @@ -15,10 +15,18 @@ import org.apache.http.entity.ContentType import org.apache.http.nio.entity.NStringEntity import org.junit.After import org.junit.Before +import org.opensearch.alerting.ADMIN import org.opensearch.alerting.ALERTING_BASE_URI +import org.opensearch.alerting.ALERTING_FULL_ACCESS_ROLE +import org.opensearch.alerting.ALL_ACCESS_ROLE import org.opensearch.alerting.ALWAYS_RUN import org.opensearch.alerting.AlertingRestTestCase import org.opensearch.alerting.DRYRUN_MONITOR +import org.opensearch.alerting.TERM_DLS_QUERY +import org.opensearch.alerting.TEST_HR_BACKEND_ROLE +import org.opensearch.alerting.TEST_HR_INDEX +import org.opensearch.alerting.TEST_HR_ROLE +import org.opensearch.alerting.TEST_NON_HR_INDEX import org.opensearch.alerting.aggregation.bucketselectorext.BucketSelectorExtAggregationBuilder import org.opensearch.alerting.assertUserNull import org.opensearch.alerting.core.model.SearchInput @@ -78,14 +86,14 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { fun `test create monitor with an user with alerting role`() { if (!securityEnabled()) return - createUserWithTestData(user, "hr_data", "hr_role", "HR") - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserWithTestData(user, TEST_HR_INDEX, TEST_HR_ROLE, TEST_HR_BACKEND_ROLE) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) try { // randomMonitor has a dummy user, api ignores the User passed as part of monitor, it picks user info from the logged-in user. val monitor = randomQueryLevelMonitor().copy( inputs = listOf( SearchInput( - indices = listOf("hr_data"), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) + indices = listOf(TEST_HR_INDEX), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) ) ) ) @@ -94,21 +102,19 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { assertUserNull(createResponse?.asMap()!!["monitor"] as HashMap) } finally { - deleteRoleMapping("hr_role") - deleteRole("hr_role") - deleteRoleMapping("alerting_full_access") + deleteRoleAndRoleMapping(TEST_HR_ROLE, ALERTING_FULL_ACCESS_ROLE) } } fun `test create monitor with an user without alerting role`() { if (!securityEnabled()) return - createUserWithTestData(user, "hr_data", "hr_role", "HR") + createUserWithTestData(user, TEST_HR_INDEX, TEST_HR_ROLE, TEST_HR_BACKEND_ROLE) try { val monitor = randomQueryLevelMonitor().copy( inputs = listOf( SearchInput( - indices = listOf("hr_data"), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) + indices = listOf(TEST_HR_INDEX), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) ) ) ) @@ -117,21 +123,21 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { } catch (e: ResponseException) { assertEquals("Unexpected status", RestStatus.FORBIDDEN, e.response.restStatus()) } finally { - deleteRoleMapping("hr_role") - deleteRole("hr_role") + deleteRoleMapping(TEST_HR_ROLE) + deleteRole(TEST_HR_ROLE) } } fun `test create monitor with an user without index read role`() { if (!securityEnabled()) return - createUserWithTestData(user, "hr_data", "hr_role", "HR") - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserWithTestData(user, TEST_HR_INDEX, TEST_HR_ROLE, TEST_HR_BACKEND_ROLE) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) try { val monitor = randomQueryLevelMonitor().copy( inputs = listOf( SearchInput( - indices = listOf("not_hr_data"), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) + indices = listOf(TEST_NON_HR_INDEX), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) ) ) ) @@ -141,9 +147,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { } catch (e: ResponseException) { assertEquals("Unexpected status", RestStatus.FORBIDDEN, e.response.restStatus()) } finally { - deleteRoleMapping("hr_role") - deleteRole("hr_role") - deleteRoleMapping("alerting_full_access") + deleteRoleAndRoleMapping(TEST_HR_ROLE, ALERTING_FULL_ACCESS_ROLE) } } @@ -314,7 +318,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { } // add alerting roles and search as userOne - must return 1 docs - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) try { val userOneSearchResponse = userClient?.makeRequest( "POST", @@ -325,7 +329,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { assertEquals("Search monitor failed", RestStatus.OK, userOneSearchResponse?.restStatus()) assertEquals("Monitor not found during search", 1, getDocs(userOneSearchResponse)) } finally { - deleteRoleMapping("alerting_full_access") + deleteRoleMapping(ALERTING_FULL_ACCESS_ROLE) } } @@ -361,7 +365,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { } // add alerting roles and search as userOne - must return 0 docs - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) try { val userOneSearchResponse = userClient?.makeRequest( "POST", @@ -372,7 +376,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { assertEquals("Search monitor failed", RestStatus.OK, userOneSearchResponse?.restStatus()) assertEquals("Monitor not found during search", 0, getDocs(userOneSearchResponse)) } finally { - deleteRoleMapping("alerting_full_access") + deleteRoleMapping(ALERTING_FULL_ACCESS_ROLE) } } @@ -404,12 +408,12 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { } // add alerting roles and search as userOne - must return 0 docs - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) try { val responseMap = getAlerts(userClient as RestClient, inputMap).asMap() assertEquals(4, responseMap["totalAlerts"]) } finally { - deleteRoleMapping("alerting_full_access") + deleteRoleMapping(ALERTING_FULL_ACCESS_ROLE) } } @@ -420,7 +424,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { enableFilterBy() putAlertMappings() - val adminUser = User("admin", listOf("admin"), listOf("all_access"), listOf()) + val adminUser = User(ADMIN, listOf(ADMIN), listOf(ALL_ACCESS_ROLE), listOf()) var monitor = createRandomMonitor(refresh = true).copy(user = adminUser) createAlert(randomAlert(monitor).copy(state = Alert.State.ACKNOWLEDGED)) createAlert(randomAlert(monitor).copy(state = Alert.State.COMPLETED)) @@ -444,12 +448,12 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { } // add alerting roles and search as userOne - must return 0 docs - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) try { val responseMap = getAlerts(userClient as RestClient, inputMap).asMap() assertEquals(0, responseMap["totalAlerts"]) } finally { - deleteRoleMapping("alerting_full_access") + deleteRoleMapping(ALERTING_FULL_ACCESS_ROLE) } } @@ -461,7 +465,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { val action = randomAction(template = randomTemplateScript("Hello {{ctx.monitor.name}}"), destinationId = createDestination().id) val inputs = listOf( SearchInput( - indices = kotlin.collections.listOf("not_hr_data"), + indices = kotlin.collections.listOf(TEST_NON_HR_INDEX), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) ) ) @@ -471,9 +475,9 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { ) // Make sure the elevating the permissions fails execute. - val adminUser = User("admin", listOf("admin"), listOf("all_access"), listOf()) + val adminUser = User(ADMIN, listOf(ADMIN), listOf(ALL_ACCESS_ROLE), listOf()) var modifiedMonitor = monitor.copy(user = adminUser) - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) try { val response = executeMonitor(userClient as RestClient, modifiedMonitor, params = DRYRUN_MONITOR) @@ -482,7 +486,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { assertTrue("Missing monitor error message", (inputResults?.get("error") as String).isNotEmpty()) assertTrue((inputResults.get("error") as String).contains("no permissions for [indices:data/read/search]")) } finally { - deleteRoleMapping("alerting_full_access") + deleteRoleMapping(ALERTING_FULL_ACCESS_ROLE) } } @@ -491,14 +495,14 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { return enableFilterBy() - createUserWithTestData(user, "hr_data", "hr_role", "HR") - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserWithTestData(user, TEST_HR_INDEX, TEST_HR_ROLE, TEST_HR_BACKEND_ROLE) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) try { // randomMonitor has a dummy user, api ignores the User passed as part of monitor, it picks user info from the logged-in user. val monitor = randomQueryLevelMonitor().copy( inputs = listOf( SearchInput( - indices = listOf("hr_data"), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) + indices = listOf(TEST_HR_INDEX), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery()) ) ) ) @@ -522,28 +526,25 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { assertEquals("Search monitor failed", RestStatus.OK, adminSearchResponse.restStatus()) assertEquals("Monitor not found during search", 1, getDocs(adminSearchResponse)) } finally { - deleteRoleMapping("hr_role") - deleteRole("hr_role") - deleteRoleMapping("alerting_full_access") + deleteRoleAndRoleMapping(TEST_HR_ROLE, ALERTING_FULL_ACCESS_ROLE) } } fun `test execute query-level monitor with user having partial index permissions`() { if (!securityEnabled()) return - val testIndex = "hr_data" createUserWithDocLevelSecurityTestData( user, - testIndex, - "hr_role", - "HR", - "{\"term\": { \"accessible\": true}}" + TEST_HR_INDEX, + TEST_HR_ROLE, + TEST_HR_BACKEND_ROLE, + TERM_DLS_QUERY ) - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) // Add a doc that is accessible to the user indexDoc( - testIndex, "1", + TEST_HR_INDEX, "1", """ { "test_field": "a", @@ -554,7 +555,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { // Add a second doc that is not accesible to the user indexDoc( - testIndex, "2", + TEST_HR_INDEX, "2", """ { "test_field": "b", @@ -563,7 +564,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { """.trimIndent() ) - val input = SearchInput(indices = listOf(testIndex), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery())) + val input = SearchInput(indices = listOf(TEST_HR_INDEX), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery())) val triggerScript = """ // make sure there is exactly one hit return ctx.results[0].hits.hits.size() == 1 @@ -577,28 +578,25 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { val alerts = searchAlerts(monitor) assertEquals("Incorrect number of alerts", 1, alerts.size) } finally { - deleteRoleMapping("hr_role") - deleteRole("hr_role") - deleteRoleMapping("alerting_full_access") + deleteRoleAndRoleMapping(TEST_HR_ROLE, ALERTING_FULL_ACCESS_ROLE) } } fun `test execute bucket-level monitor with user having partial index permissions`() { if (!securityEnabled()) return - val testIndex = "hr_data" createUserWithDocLevelSecurityTestData( user, - testIndex, - "hr_role", - "HR", - "{\"term\": { \"accessible\": true}}" + TEST_HR_INDEX, + TEST_HR_ROLE, + TEST_HR_BACKEND_ROLE, + TERM_DLS_QUERY ) - createUserRolesMapping("alerting_full_access", arrayOf(user)) + createUserRolesMapping(ALERTING_FULL_ACCESS_ROLE, arrayOf(user)) // Add a doc that is accessible to the user indexDoc( - testIndex, "1", + TEST_HR_INDEX, "1", """ { "test_field": "a", @@ -609,7 +607,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { // Add a second doc that is not accesible to the user indexDoc( - testIndex, "2", + TEST_HR_INDEX, "2", """ { "test_field": "b", @@ -623,7 +621,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { ) val compositeAgg = CompositeAggregationBuilder("composite_agg", compositeSources) val input = SearchInput( - indices = listOf(testIndex), + indices = listOf(TEST_HR_INDEX), query = SearchSourceBuilder().size(0).query(QueryBuilders.matchAllQuery()).aggregation(compositeAgg) ) val triggerScript = """ @@ -647,9 +645,7 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() { val alerts = searchAlerts(monitor) assertEquals("Incorrect number of alerts", 1, alerts.size) } finally { - deleteRoleMapping("hr_role") - deleteRole("hr_role") - deleteRoleMapping("alerting_full_access") + deleteRoleAndRoleMapping(TEST_HR_ROLE, ALERTING_FULL_ACCESS_ROLE) } } }