Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding ITs to check for Default Role Permissions #299

Merged
merged 5 commits into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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