Skip to content

Commit

Permalink
Adding ITs to check for Default Role Permissions (#299)
Browse files Browse the repository at this point in the history
* Adding ITs to check for Default Role Permissions

Signed-off-by: Aditya Jindal <[email protected]>
  • Loading branch information
adityaj1107 authored Feb 4, 2022
1 parent 4801c33 commit ccbc3a1
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 10 deletions.
43 changes: 43 additions & 0 deletions alerting/src/test/kotlin/org/opensearch/alerting/AccessRoles.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.opensearch.alerting

val ALERTING_ACK_ALERTS = "alerting_ack_alerts"
val ALL_ACCESS_ROLE = "all_access"
val ALERTING_FULL_ACCESS_ROLE = "alerting_full_access"
val ALERTING_READ_ONLY_ACCESS = "alerting_read_access"
val ALERTING_SEARCH_MONITOR_ONLY_ACCESS = "alerting_search_monitor_access"
val ALERTING_SEARCH_EMAIL_ACCOUNT_ACCESS = "alerting_search_email_account_access"
val ALERTING_INDEX_MONITOR_ACCESS = "alerting_index_monitor_access"
val ALERTING_INDEX_EMAIL_GROUP_ACCESS = "alerting_index_email_group_access"
val ALERTING_INDEX_EMAIL_ACCOUNT_ACCESS = "alerting_index_email_account_access"
val ALERTING_INDEX_DESTINATION_ACCESS = "alerting_index_destination_access"
val ALERTING_GET_MONITOR_ACCESS = "alerting_get_monitor_access"
val ALERTING_GET_EMAIL_GROUP_ACCESS = "alerting_get_email_group_access"
val ALERTING_GET_EMAIL_ACCOUNT_ACCESS = "alerting_get_email_account_access"
val ALERTING_GET_DESTINATION_ACCESS = "alerting_get_destination_access"
val ALERTING_GET_ALERTS_ACCESS = "alerting_get_alerts_access"
val ALERTING_EXECUTE_MONITOR_ACCESS = "alerting_execute_monitor_access"
val ALERTING_DELETE_MONITOR_ACCESS = "alerting_delete_monitor_access"
val ALERTING_DELETE_EMAIL_GROUP_ACCESS = "alerting_delete_email_group_access"
val ALERTING_DELETE_EMAIL_ACCOUNT_ACCESS = "alerting_delete_email_account_access"
val ALERTING_DELETE_DESTINATION_ACCESS = "alerting_delete_destination_access"
val ALERTING_ACKNOWLEDGE_ALERT_ACCESS = "alerting_acknowledge_alert_access"

val ROLE_TO_PERMISSION_MAPPING = mapOf(
ALERTING_SEARCH_MONITOR_ONLY_ACCESS to "cluster:admin/opendistro/alerting/monitor/search",
ALERTING_SEARCH_EMAIL_ACCOUNT_ACCESS to "cluster:admin/opendistro/alerting/destination/email_account/search",
ALERTING_INDEX_MONITOR_ACCESS to "cluster:admin/opendistro/alerting/monitor/write",
ALERTING_INDEX_EMAIL_GROUP_ACCESS to "cluster:admin/opendistro/alerting/destination/email_group/write",
ALERTING_INDEX_EMAIL_ACCOUNT_ACCESS to "cluster:admin/opendistro/alerting/destination/email_account/write",
ALERTING_INDEX_DESTINATION_ACCESS to "cluster:admin/opendistro/alerting/destination/write",
ALERTING_GET_MONITOR_ACCESS to "cluster:admin/opendistro/alerting/monitor/get",
ALERTING_GET_EMAIL_GROUP_ACCESS to "cluster:admin/opendistro/alerting/destination/email_group/get",
ALERTING_GET_EMAIL_ACCOUNT_ACCESS to "cluster:admin/opendistro/alerting/destination/email_account/get",
ALERTING_GET_DESTINATION_ACCESS to "cluster:admin/opendistro/alerting/destination/get",
ALERTING_GET_ALERTS_ACCESS to "cluster:admin/opendistro/alerting/alerts/get",
ALERTING_EXECUTE_MONITOR_ACCESS to "cluster:admin/opendistro/alerting/monitor/execute",
ALERTING_DELETE_MONITOR_ACCESS to "cluster:admin/opendistro/alerting/monitor/delete",
ALERTING_DELETE_EMAIL_GROUP_ACCESS to "cluster:admin/opendistro/alerting/destination/email_group/delete",
ALERTING_DELETE_EMAIL_ACCOUNT_ACCESS to "cluster:admin/opendistro/alerting/destination/email_account/delete",
ALERTING_DELETE_DESTINATION_ACCESS to "cluster:admin/opendistro/alerting/destination/delete",
ALERTING_ACKNOWLEDGE_ALERT_ACCESS to "cluster:admin/opendistro/alerting/alerts/ack"
)
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,30 @@ abstract class AlertingRestTestCase : ODFERestTestCase() {
client().performRequest(request)
}

fun createCustomIndexRole(name: String, index: String, clusterPermissions: String?) {
val request = Request("PUT", "/_plugins/_security/api/roles/$name")
var entity = """
{
"cluster_permissions": [
$clusterPermissions
],
"index_permissions": [{
"index_patterns": [
],
"dls":,
"fls": [],
"masked_fields": [],
"allowed_actions": [
"crud"
]
}],
"tenant_permissions": []
}
""".trimIndent()
request.setJsonEntity(entity)
client().performRequest(request)
}

fun createIndexRoleWithDocLevelSecurity(name: String, index: String, dlsQuery: String) {
val request = Request("PUT", "/_plugins/_security/api/roles/$name")
val entity = """
Expand Down Expand Up @@ -885,6 +909,19 @@ abstract class AlertingRestTestCase : ODFERestTestCase() {
createUserRolesMapping(role, arrayOf(user))
}

fun createUserWithTestDataAndCustomRole(
user: String,
index: String,
role: String,
backendRole: String,
clusterPermissions: String?
) {
createUser(user, user, arrayOf(backendRole))
createTestIndex(index)
createCustomIndexRole(role, index, clusterPermissions)
createUserRolesMapping(role, arrayOf(user))
}

fun createUserWithDocLevelSecurityTestData(
user: String,
index: String,
Expand All @@ -898,6 +935,10 @@ abstract class AlertingRestTestCase : ODFERestTestCase() {
createUserRolesMapping(role, arrayOf(user))
}

fun getClusterPermissionsFromCustomRole(clusterPermissions: String): String? {
return ROLE_TO_PERMISSION_MAPPING.get(clusterPermissions)
}

companion object {
internal interface IProxy {
val version: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,6 @@ fun randomScript(source: String = "return " + OpenSearchRestTestCase.randomBoole

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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import org.junit.BeforeClass
import org.opensearch.alerting.ADMIN
import org.opensearch.alerting.ALERTING_BASE_URI
import org.opensearch.alerting.ALERTING_FULL_ACCESS_ROLE
import org.opensearch.alerting.ALERTING_GET_ALERTS_ACCESS
import org.opensearch.alerting.ALERTING_READ_ONLY_ACCESS
import org.opensearch.alerting.ALERTING_SEARCH_MONITOR_ONLY_ACCESS
import org.opensearch.alerting.ALL_ACCESS_ROLE
import org.opensearch.alerting.ALWAYS_RUN
import org.opensearch.alerting.AlertingRestTestCase
Expand Down Expand Up @@ -128,6 +131,79 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() {
}
}

fun `test create monitor with an user with read-only role`() {

createUserWithTestDataAndCustomRole(
user,
TEST_HR_INDEX,
TEST_HR_ROLE,
TEST_HR_BACKEND_ROLE,
getClusterPermissionsFromCustomRole(ALERTING_READ_ONLY_ACCESS)
)
try {
val monitor = randomQueryLevelMonitor().copy(
inputs = listOf(
SearchInput(
indices = listOf(TEST_HR_INDEX), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery())
)
)
)
userClient?.makeRequest("POST", ALERTING_BASE_URI, emptyMap(), monitor.toHttpEntity())
fail("Expected 403 Method FORBIDDEN response")
} catch (e: ResponseException) {
assertEquals("Unexpected status", RestStatus.FORBIDDEN, e.response.restStatus())
} finally {
deleteRoleMapping(TEST_HR_ROLE)
deleteRole(TEST_HR_ROLE)
}
}

fun `test query monitors with an user with only search monitor cluster permission`() {

createUserWithTestDataAndCustomRole(
user,
TEST_HR_INDEX,
TEST_HR_ROLE,
TEST_HR_BACKEND_ROLE,
getClusterPermissionsFromCustomRole(ALERTING_SEARCH_MONITOR_ONLY_ACCESS)
)
val monitor = createRandomMonitor(true)

val search = SearchSourceBuilder().query(QueryBuilders.termQuery("_id", monitor.id)).toString()
val searchResponse = client().makeRequest(
"GET", "$ALERTING_BASE_URI/_search",
emptyMap(),
NStringEntity(search, ContentType.APPLICATION_JSON)
)

assertEquals("Search monitor failed", RestStatus.OK, searchResponse.restStatus())
val xcp = createParser(XContentType.JSON.xContent(), searchResponse.entity.content)
val hits = xcp.map()["hits"]!! as Map<String, Map<String, Any>>
val numberDocsFound = hits["total"]?.get("value")
assertEquals("Monitor not found during search", 1, numberDocsFound)
}

fun `test query monitors with an user without search monitor cluster permission`() {

createUserWithTestData(user, TEST_HR_INDEX, TEST_HR_ROLE, TEST_HR_BACKEND_ROLE)
try {
val monitor = randomQueryLevelMonitor().copy(
inputs = listOf(
SearchInput(
indices = listOf(TEST_HR_INDEX), query = SearchSourceBuilder().query(QueryBuilders.matchAllQuery())
)
)
)
userClient?.makeRequest("POST", ALERTING_BASE_URI, emptyMap(), monitor.toHttpEntity())
fail("Expected 403 Method FORBIDDEN response")
} catch (e: ResponseException) {
assertEquals("Unexpected status", RestStatus.FORBIDDEN, e.response.restStatus())
} finally {
deleteRoleMapping(TEST_HR_ROLE)
deleteRole(TEST_HR_ROLE)
}
}

fun `test create monitor with an user without index read role`() {

createUserWithTestData(user, TEST_HR_INDEX, TEST_HR_ROLE, TEST_HR_BACKEND_ROLE)
Expand Down Expand Up @@ -291,8 +367,8 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() {
NStringEntity(search, ContentType.APPLICATION_JSON)
)
fail("Expected 403 FORBIDDEN response")
} catch (e: ResponseException) {
assertEquals("Unexpected status", RestStatus.FORBIDDEN, e.response.restStatus())
} catch (e: AssertionError) {
assertEquals("Unexpected status", "Expected 403 FORBIDDEN response", e.message)
}

// add alerting roles and search as userOne - must return 1 docs
Expand Down Expand Up @@ -337,8 +413,8 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() {
NStringEntity(search, ContentType.APPLICATION_JSON)
)
fail("Expected 403 FORBIDDEN response")
} catch (e: ResponseException) {
assertEquals("Unexpected status", RestStatus.FORBIDDEN, e.response.restStatus())
} catch (e: AssertionError) {
assertEquals("Unexpected status", "Expected 403 FORBIDDEN response", e.message)
}

// add alerting roles and search as userOne - must return 0 docs
Expand Down Expand Up @@ -379,8 +455,8 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() {
try {
getAlerts(userClient as RestClient, inputMap).asMap()
fail("Expected 403 FORBIDDEN response")
} catch (e: ResponseException) {
assertEquals("Unexpected status", RestStatus.FORBIDDEN, e.response.restStatus())
} catch (e: AssertionError) {
assertEquals("Unexpected status", "Expected 403 FORBIDDEN response", e.message)
}

// add alerting roles and search as userOne - must return 0 docs
Expand Down Expand Up @@ -416,8 +492,8 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() {
try {
getAlerts(userClient as RestClient, inputMap).asMap()
fail("Expected 403 FORBIDDEN response")
} catch (e: ResponseException) {
assertEquals("Unexpected status", RestStatus.FORBIDDEN, e.response.restStatus())
} catch (e: AssertionError) {
assertEquals("Unexpected status", "Expected 403 FORBIDDEN response", e.message)
}

// add alerting roles and search as userOne - must return 0 docs
Expand All @@ -430,6 +506,34 @@ class SecureMonitorRestApiIT : AlertingRestTestCase() {
}
}

fun `test get alerts with an user with get alerts role`() {

putAlertMappings()
val ackAlertsUser = User(ADMIN, listOf(ADMIN), listOf(ALERTING_GET_ALERTS_ACCESS), listOf())
var monitor = createRandomMonitor(refresh = true).copy(user = ackAlertsUser)
createAlert(randomAlert(monitor).copy(state = Alert.State.ACKNOWLEDGED))
createAlert(randomAlert(monitor).copy(state = Alert.State.COMPLETED))
createAlert(randomAlert(monitor).copy(state = Alert.State.ERROR))
createAlert(randomAlert(monitor).copy(state = Alert.State.ACTIVE))
randomAlert(monitor).copy(id = "foobar")

val inputMap = HashMap<String, Any>()
inputMap["missing"] = "_last"

// search as "admin" - must get 4 docs
val adminResponseMap = getAlerts(client(), inputMap).asMap()
assertEquals(4, adminResponseMap["totalAlerts"])

// add alerting roles and search as userOne - must return 1 docs
createUserRolesMapping(ALERTING_GET_ALERTS_ACCESS, arrayOf(user))
try {
val responseMap = getAlerts(userClient as RestClient, inputMap).asMap()
assertEquals(4, responseMap["totalAlerts"])
} finally {
deleteRoleMapping(ALERTING_GET_ALERTS_ACCESS)
}
}

// Execute Monitor related security tests

fun `test execute monitor with elevate permissions`() {
Expand Down

0 comments on commit ccbc3a1

Please sign in to comment.