Skip to content

Commit f12e055

Browse files
Security test workflow only run security behavior tests (#1459)
1 parent 120004e commit f12e055

File tree

9 files changed

+218
-91
lines changed

9 files changed

+218
-91
lines changed

.github/workflows/security-test-workflow.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
- name: Run integration tests
4646
run: |
4747
chown -R 1000:1000 `pwd`
48-
su `id -un 1000` -c "./gradlew integTest -Dsecurity=true -Dhttps=true --tests '*IT'"
48+
su `id -un 1000` -c "./gradlew integTest -Dsecurity=true -Dhttps=true --tests '*SecurityBehaviorIT'"
4949
- name: Upload failed logs
5050
uses: actions/upload-artifact@v4
5151
if: failure()

DEVELOPER_GUIDE.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- [Build](#build)
77
- [Building from the command line](#building-from-the-command-line)
88
- [Code Coverage](#code-coverage)
9+
- [Security Behavior Tests](#security-behavior-tests)
910
- [Debugging](#debugging)
1011
- [Using IntelliJ IDEA](#using-intellij-idea)
1112
- [Submitting Changes](#submitting-changes)
@@ -61,6 +62,7 @@ However, to build the `index management` plugin project, we also use the OpenSea
6162
10. `./gradlew indexmanagementBwcCluster#fullRestartClusterTask -Dtests.security.manager=false` launches a cluster with three nodes of bwc version of OpenSearch with index management and tests backwards compatibility by performing a full restart on the cluster upgrading all the nodes with the current version of OpenSearch with index management.
6263
11. `./gradlew bwcTestSuite -Dtests.security.manager=false` runs all the above bwc tests combined.
6364
12. `./gradlew integTestRemote -Dtests.rest.cluster=localhost:9200 -Dtests.cluster=localhost:9200 -Dtests.clustername="docker-cluster" -Dhttps=true -Duser=admin -Dpassword=admin` launches integration tests against a local cluster and run tests with security
65+
13. `./gradlew integTest -Dsecurity=true -Dhttps=true --tests '*SecurityBehaviorIT'` runs all security behavior tests with security enabled
6466

6567
When launching a cluster using one of the above commands, logs are placed in `build/testclusters/integTest-0/logs`. Though the logs are teed to the console, in practices it's best to check the actual log file.
6668

@@ -89,6 +91,66 @@ After running with coverage enabled, reports are generated in:
8991
- **HTML Report**: `build/reports/jacoco/test/html/index.html` (human readable)
9092
- **XML Report**: `build/reports/jacoco/test/jacocoTestReport.xml` (for tools like Codecov)
9193

94+
### Security Behavior Tests
95+
96+
Security behavior tests ensure that the Index Management plugin properly enforces access controls and permissions. These tests validate that users can only perform operations they are authorized for and receive appropriate error responses when access is denied.
97+
98+
#### Overview
99+
100+
Security behavior tests extend the `SecurityRestTestCase` base class and test various permission scenarios:
101+
- API endpoint permission enforcement
102+
- Policy based authentication and authorization
103+
104+
#### Running Security Tests
105+
106+
Security tests require additional flags and must be run against a cluster with security enabled:
107+
108+
```bash
109+
# Run all security behavior tests
110+
./gradlew integTest -Dsecurity=true -Dhttps=true --tests '*SecurityBehaviorIT'
111+
112+
# Run a specific security test class
113+
./gradlew integTest -Dsecurity=true -Dhttps=true --tests '*PolicySecurityBehaviorIT'
114+
115+
# Run security tests against a remote cluster with authentication
116+
./gradlew integTestRemote -Dtests.rest.cluster=localhost:9200 -Dtests.cluster=localhost:9200 -Dtests.clustername="docker-cluster" -Dhttps=true -Duser=admin -Dpassword=admin
117+
```
118+
119+
#### Writing New Security Tests
120+
121+
When adding new security tests, follow these guidelines:
122+
123+
1. **Extend SecurityRestTestCase**: All security tests should extend this base class
124+
2. **Test both success and failure scenarios**: Verify authorized access works and unauthorized access is properly denied
125+
3. **Use appropriate HTTP status codes**: Expect 401 (Unauthorized) or 403 (Forbidden) for denied access
126+
4. **Test role-based permissions**: Create users with specific roles and test their access levels
127+
5. **Include cleanup**: Ensure users and roles created during tests are properly cleaned up
128+
129+
Example test structure:
130+
```kotlin
131+
@TestLogging("level:DEBUG", reason = "Debug for tests.")
132+
class MyFeatureSecurityBehaviorIT : SecurityRestTestCase() {
133+
134+
@Before
135+
fun setupUsersAndRoles() {
136+
// Create test users and roles
137+
}
138+
139+
@After
140+
fun cleanup() {
141+
// Clean up test users and roles
142+
}
143+
144+
fun testAuthorizedAccess() {
145+
// Test that authorized users can perform operations
146+
}
147+
148+
fun testUnauthorizedAccess() {
149+
// Test that unauthorized users receive proper error responses
150+
}
151+
}
152+
```
153+
92154
### Debugging
93155

94156
Sometimes it is useful to attach a debugger to either the OpenSearch cluster or the integ tests to see what's going on. When running unit tests, hit **Debug** from the IDE's gutter to debug the tests. For the OpenSearch cluster or the integ tests, first, make sure start a debugger listening on port `5005`.

src/test/kotlin/org/opensearch/indexmanagement/IndexStateManagementSecurityBehaviorIT.kt renamed to src/test/kotlin/org/opensearch/indexmanagement/security/IndexStateManagementSecurityBehaviorIT.kt

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
/*
2+
* Copyright OpenSearch Contributors
23
* SPDX-License-Identifier: Apache-2.0
3-
*
4-
* The OpenSearch Contributors require contributions made to
5-
* this file be licensed under the Apache-2.0 license or a
6-
* compatible open source license.
7-
*
8-
* Modifications Copyright OpenSearch Contributors. See
9-
* GitHub history for details.
104
*/
115

12-
package org.opensearch.indexmanagement
6+
package org.opensearch.indexmanagement.security
137

148
import org.junit.After
159
import org.junit.Before
1610
import org.opensearch.client.RestClient
1711
import org.opensearch.commons.rest.SecureRestClientBuilder
1812
import org.opensearch.core.rest.RestStatus
13+
import org.opensearch.indexmanagement.ADD_POLICY
14+
import org.opensearch.indexmanagement.BULK_WRITE_INDEX
15+
import org.opensearch.indexmanagement.CREATE_INDEX
16+
import org.opensearch.indexmanagement.DELETE_POLICY
17+
import org.opensearch.indexmanagement.EXPLAIN_INDEX
18+
import org.opensearch.indexmanagement.EXPLAIN_ROLLUP
19+
import org.opensearch.indexmanagement.GET_INDEX_MAPPING
20+
import org.opensearch.indexmanagement.GET_POLICIES
21+
import org.opensearch.indexmanagement.GET_POLICY
22+
import org.opensearch.indexmanagement.GET_ROLLUP
23+
import org.opensearch.indexmanagement.INDEX_ROLLUP
24+
import org.opensearch.indexmanagement.MANAGED_INDEX
25+
import org.opensearch.indexmanagement.PUT_INDEX_MAPPING
26+
import org.opensearch.indexmanagement.SEARCH_INDEX
27+
import org.opensearch.indexmanagement.UPDATE_ROLLUP
28+
import org.opensearch.indexmanagement.WRITE_INDEX
29+
import org.opensearch.indexmanagement.WRITE_POLICY
1930
import org.opensearch.indexmanagement.common.model.dimension.DateHistogram
2031
import org.opensearch.indexmanagement.common.model.dimension.Terms
2132
import org.opensearch.indexmanagement.indexstatemanagement.action.RollupAction
@@ -26,6 +37,7 @@ import org.opensearch.indexmanagement.indexstatemanagement.randomErrorNotificati
2637
import org.opensearch.indexmanagement.indexstatemanagement.settings.ManagedIndexSettings
2738
import org.opensearch.indexmanagement.indexstatemanagement.step.rollup.AttemptCreateRollupJobStep
2839
import org.opensearch.indexmanagement.indexstatemanagement.step.rollup.WaitForRollupCompletionStep
40+
import org.opensearch.indexmanagement.makeRequest
2941
import org.opensearch.indexmanagement.rollup.model.ISMRollup
3042
import org.opensearch.indexmanagement.rollup.model.RollupMetadata
3143
import org.opensearch.indexmanagement.rollup.model.RollupMetrics
@@ -34,6 +46,7 @@ import org.opensearch.indexmanagement.rollup.model.metric.Max
3446
import org.opensearch.indexmanagement.rollup.model.metric.Min
3547
import org.opensearch.indexmanagement.rollup.model.metric.Sum
3648
import org.opensearch.indexmanagement.rollup.model.metric.ValueCount
49+
import org.opensearch.indexmanagement.waitFor
3750
import org.opensearch.test.junit.annotations.TestLogging
3851
import java.time.Instant
3952
import java.time.temporal.ChronoUnit
@@ -53,7 +66,7 @@ class IndexStateManagementSecurityBehaviorIT : SecurityRestTestCase() {
5366

5467
@Before
5568
fun setupUsersAndRoles() {
56-
updateClusterSetting(ManagedIndexSettings.JITTER.key, "0.0", false)
69+
updateClusterSetting(ManagedIndexSettings.Companion.JITTER.key, "0.0", false)
5770

5871
val helpdeskClusterPermissions =
5972
listOf(
@@ -301,7 +314,7 @@ class IndexStateManagementSecurityBehaviorIT : SecurityRestTestCase() {
301314
updateManagedIndexConfigStartTime(managedIndexConfig)
302315
waitFor {
303316
assertEquals(
304-
AttemptCreateRollupJobStep.getSuccessMessage(rollupId, indexName),
317+
AttemptCreateRollupJobStep.Companion.getSuccessMessage(rollupId, indexName),
305318
getExplainManagedIndexMetaData(indexName).info?.get("message"),
306319
)
307320
}
@@ -317,7 +330,7 @@ class IndexStateManagementSecurityBehaviorIT : SecurityRestTestCase() {
317330
updateManagedIndexConfigStartTime(managedIndexConfig)
318331
waitFor {
319332
assertEquals(
320-
WaitForRollupCompletionStep.getJobCompletionMessage(rollupId, indexName),
333+
WaitForRollupCompletionStep.Companion.getJobCompletionMessage(rollupId, indexName),
321334
getExplainManagedIndexMetaData(indexName).info?.get("message"),
322335
)
323336
}
Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
package org.opensearch.indexmanagement.controlcenter.notification
6+
package org.opensearch.indexmanagement.security
77

88
import org.junit.After
99
import org.junit.Before
@@ -15,7 +15,12 @@ import org.opensearch.indexmanagement.DELETE_LRON_CONFIG
1515
import org.opensearch.indexmanagement.GET_LRON_CONFIG
1616
import org.opensearch.indexmanagement.INDEX_LRON_CONFIG
1717
import org.opensearch.indexmanagement.IndexManagementPlugin
18-
import org.opensearch.indexmanagement.SecurityRestTestCase
18+
import org.opensearch.indexmanagement.controlcenter.notification.getResourceURI
19+
import org.opensearch.indexmanagement.controlcenter.notification.initNodeIdsInRestIT
20+
import org.opensearch.indexmanagement.controlcenter.notification.nodeIdsInRestIT
21+
import org.opensearch.indexmanagement.controlcenter.notification.randomLRONConfig
22+
import org.opensearch.indexmanagement.controlcenter.notification.randomTaskId
23+
import org.opensearch.indexmanagement.controlcenter.notification.toJsonString
1924

2025
@Suppress("UNCHECKED_CAST")
2126
class LRONConfigSecurityBehaviorIT : SecurityRestTestCase() {
@@ -65,12 +70,12 @@ class LRONConfigSecurityBehaviorIT : SecurityRestTestCase() {
6570
testUserClient?.close()
6671
deleteUser(testUser)
6772
deleteRole(testRole)
68-
deleteIndexByName(IndexManagementPlugin.CONTROL_CENTER_INDEX)
73+
deleteIndexByName(IndexManagementPlugin.Companion.CONTROL_CENTER_INDEX)
6974
}
7075

7176
fun `test index LRONConfig with security using POST`() {
7277
// super user
73-
val request = Request("POST", IndexManagementPlugin.LRON_BASE_URI)
78+
val request = Request("POST", IndexManagementPlugin.Companion.LRON_BASE_URI)
7479
request.setJsonEntity(randomLRONConfig(taskId = randomTaskId(nodeId = nodeIdsInRestIT.random())).toJsonString())
7580
executeRequest(request, RestStatus.OK, superUserClient!!)
7681
// test user
@@ -88,7 +93,7 @@ class LRONConfigSecurityBehaviorIT : SecurityRestTestCase() {
8893

8994
fun `test index LRONConfig dry run with security using POST`() {
9095
// super user
91-
val request = Request("POST", IndexManagementPlugin.LRON_BASE_URI)
96+
val request = Request("POST", IndexManagementPlugin.Companion.LRON_BASE_URI)
9297
request.addParameter("dry_run", "true")
9398
request.setJsonEntity(randomLRONConfig(taskId = randomTaskId(nodeId = nodeIdsInRestIT.random())).toJsonString())
9499
executeRequest(request, RestStatus.OK, superUserClient!!)
@@ -107,7 +112,7 @@ class LRONConfigSecurityBehaviorIT : SecurityRestTestCase() {
107112
fun `test update LRONConfig with security using PUT`() {
108113
// super user
109114
val lronConfig = randomLRONConfig(taskId = randomTaskId(nodeId = nodeIdsInRestIT.random()))
110-
val createRequest = Request("POST", IndexManagementPlugin.LRON_BASE_URI)
115+
val createRequest = Request("POST", IndexManagementPlugin.Companion.LRON_BASE_URI)
111116
createRequest.setJsonEntity(lronConfig.toJsonString())
112117
executeRequest(createRequest, RestStatus.OK, superUserClient!!)
113118
val updateRequest = Request("PUT", getResourceURI(lronConfig.taskId, lronConfig.actionName))
@@ -129,7 +134,7 @@ class LRONConfigSecurityBehaviorIT : SecurityRestTestCase() {
129134
fun `test delete LRONConfig with security`() {
130135
// super user
131136
val lronConfig = randomLRONConfig(taskId = randomTaskId(nodeId = nodeIdsInRestIT.random()))
132-
val createRequest = Request("POST", IndexManagementPlugin.LRON_BASE_URI)
137+
val createRequest = Request("POST", IndexManagementPlugin.Companion.LRON_BASE_URI)
133138
createRequest.setJsonEntity(lronConfig.toJsonString())
134139
executeRequest(createRequest, RestStatus.OK, superUserClient!!)
135140
val deleteRequest = Request("DELETE", getResourceURI(lronConfig.taskId, lronConfig.actionName))
@@ -151,7 +156,7 @@ class LRONConfigSecurityBehaviorIT : SecurityRestTestCase() {
151156
fun `test get LRONConfig with security`() {
152157
// super user
153158
val lronConfig = randomLRONConfig(taskId = randomTaskId(nodeId = nodeIdsInRestIT.random()))
154-
val createRequest = Request("POST", IndexManagementPlugin.LRON_BASE_URI)
159+
val createRequest = Request("POST", IndexManagementPlugin.Companion.LRON_BASE_URI)
155160
createRequest.setJsonEntity(lronConfig.toJsonString())
156161
executeRequest(createRequest, RestStatus.OK, superUserClient!!)
157162
val getRequest = Request("GET", getResourceURI(lronConfig.taskId, lronConfig.actionName))
@@ -171,13 +176,13 @@ class LRONConfigSecurityBehaviorIT : SecurityRestTestCase() {
171176

172177
fun `test get LRONConfigs with security`() {
173178
// super user
174-
val createRequest = Request("POST", IndexManagementPlugin.LRON_BASE_URI)
179+
val createRequest = Request("POST", IndexManagementPlugin.Companion.LRON_BASE_URI)
175180
randomList(1, 15) {
176181
createRequest.setJsonEntity(randomLRONConfig(taskId = randomTaskId(nodeId = nodeIdsInRestIT.random())).toJsonString())
177182
executeRequest(createRequest, RestStatus.OK, superUserClient!!).asMap()
178183
}
179184

180-
val getRequest = Request("GET", IndexManagementPlugin.LRON_BASE_URI)
185+
val getRequest = Request("GET", IndexManagementPlugin.Companion.LRON_BASE_URI)
181186
executeRequest(getRequest, RestStatus.OK, superUserClient!!)
182187

183188
// test user

src/test/kotlin/org/opensearch/indexmanagement/PolicySecurityBehaviorIT.kt renamed to src/test/kotlin/org/opensearch/indexmanagement/security/PolicySecurityBehaviorIT.kt

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
/*
2+
* Copyright OpenSearch Contributors
23
* SPDX-License-Identifier: Apache-2.0
3-
*
4-
* The OpenSearch Contributors require contributions made to
5-
* this file be licensed under the Apache-2.0 license or a
6-
* compatible open source license.
7-
*
8-
* Modifications Copyright OpenSearch Contributors. See
9-
* GitHub history for details.
104
*/
115

12-
package org.opensearch.indexmanagement
6+
package org.opensearch.indexmanagement.security
137

148
import org.junit.After
159
import org.junit.Before
@@ -18,17 +12,25 @@ import org.opensearch.client.ResponseException
1812
import org.opensearch.client.RestClient
1913
import org.opensearch.commons.rest.SecureRestClientBuilder
2014
import org.opensearch.core.rest.RestStatus
21-
import org.opensearch.indexmanagement.IndexManagementPlugin.Companion.INDEX_MANAGEMENT_INDEX
15+
import org.opensearch.indexmanagement.BULK_WRITE_INDEX
16+
import org.opensearch.indexmanagement.CREATE_INDEX
17+
import org.opensearch.indexmanagement.GET_INDEX_MAPPING
18+
import org.opensearch.indexmanagement.IndexManagementPlugin
19+
import org.opensearch.indexmanagement.MANAGED_INDEX
20+
import org.opensearch.indexmanagement.PUT_INDEX_MAPPING
21+
import org.opensearch.indexmanagement.SEARCH_INDEX
22+
import org.opensearch.indexmanagement.WRITE_INDEX
2223
import org.opensearch.indexmanagement.indexstatemanagement.action.AliasAction
2324
import org.opensearch.indexmanagement.indexstatemanagement.model.Policy
2425
import org.opensearch.indexmanagement.indexstatemanagement.model.State
2526
import org.opensearch.indexmanagement.indexstatemanagement.randomErrorNotification
27+
import org.opensearch.indexmanagement.indexstatemanagement.settings.ManagedIndexSettings
2628
import org.opensearch.indexmanagement.indexstatemanagement.transport.action.addpolicy.AddPolicyAction
27-
import org.opensearch.test.OpenSearchTestCase
2829
import org.opensearch.test.junit.annotations.TestLogging
2930
import java.time.Instant
3031
import java.time.temporal.ChronoUnit
3132
import java.util.Locale
33+
import kotlin.collections.get
3234

3335
@TestLogging("level:DEBUG", reason = "Debug for tests.")
3436
class PolicySecurityBehaviorIT : SecurityRestTestCase() {
@@ -42,11 +44,11 @@ class PolicySecurityBehaviorIT : SecurityRestTestCase() {
4244

4345
@Before
4446
fun setupUsersAndRoles() {
45-
// updateClusterSetting(ManagedIndexSettings.JITTER.key, "0.0", false)
47+
updateClusterSetting(ManagedIndexSettings.Companion.JITTER.key, "0.0", false)
4648

4749
val custerPermissions =
4850
listOf(
49-
AddPolicyAction.NAME,
51+
AddPolicyAction.Companion.NAME,
5052
)
5153

5254
val indexPermissions =
@@ -75,12 +77,12 @@ class PolicySecurityBehaviorIT : SecurityRestTestCase() {
7577
deleteUser(ismUser)
7678
deleteRole(HELPDESK_ROLE)
7779

78-
deleteIndexByName("$INDEX_MANAGEMENT_INDEX")
80+
deleteIndexByName("${IndexManagementPlugin.Companion.INDEX_MANAGEMENT_INDEX}")
7981
}
8082

8183
fun `test add policy`() {
82-
val notPermittedIndexPrefix = OpenSearchTestCase.randomAlphaOfLength(10).lowercase(Locale.getDefault())
83-
val policyId = OpenSearchTestCase.randomAlphaOfLength(10)
84+
val notPermittedIndexPrefix = randomAlphaOfLength(10).lowercase(Locale.getDefault())
85+
val policyId = randomAlphaOfLength(10)
8486

8587
val permittedindices = mutableListOf<String>()
8688
val notPermittedindices = mutableListOf<String>()

src/test/kotlin/org/opensearch/indexmanagement/RollupSecurityBehaviorIT.kt renamed to src/test/kotlin/org/opensearch/indexmanagement/security/RollupSecurityBehaviorIT.kt

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
/*
2+
* Copyright OpenSearch Contributors
23
* SPDX-License-Identifier: Apache-2.0
3-
*
4-
* The OpenSearch Contributors require contributions made to
5-
* this file be licensed under the Apache-2.0 license or a
6-
* compatible open source license.
7-
*
8-
* Modifications Copyright OpenSearch Contributors. See
9-
* GitHub history for details.
104
*/
115

12-
package org.opensearch.indexmanagement
6+
package org.opensearch.indexmanagement.security
137

148
import org.junit.After
159
import org.junit.Before
1610
import org.opensearch.client.RestClient
1711
import org.opensearch.commons.rest.SecureRestClientBuilder
1812
import org.opensearch.core.rest.RestStatus
13+
import org.opensearch.indexmanagement.BULK_WRITE_INDEX
14+
import org.opensearch.indexmanagement.CREATE_INDEX
15+
import org.opensearch.indexmanagement.DELETE_ROLLUP
16+
import org.opensearch.indexmanagement.EXPLAIN_ROLLUP
17+
import org.opensearch.indexmanagement.GET_INDEX_MAPPING
18+
import org.opensearch.indexmanagement.GET_ROLLUP
19+
import org.opensearch.indexmanagement.INDEX_ROLLUP
20+
import org.opensearch.indexmanagement.MANAGED_INDEX
21+
import org.opensearch.indexmanagement.PUT_INDEX_MAPPING
22+
import org.opensearch.indexmanagement.SEARCH_INDEX
23+
import org.opensearch.indexmanagement.UPDATE_ROLLUP
24+
import org.opensearch.indexmanagement.WRITE_INDEX
1925
import org.opensearch.indexmanagement.common.model.dimension.DateHistogram
2026
import org.opensearch.indexmanagement.indexstatemanagement.settings.ManagedIndexSettings
2127
import org.opensearch.indexmanagement.rollup.model.Rollup
@@ -27,6 +33,7 @@ import org.opensearch.indexmanagement.rollup.model.metric.Min
2733
import org.opensearch.indexmanagement.rollup.model.metric.Sum
2834
import org.opensearch.indexmanagement.rollup.model.metric.ValueCount
2935
import org.opensearch.indexmanagement.rollup.randomRollup
36+
import org.opensearch.indexmanagement.waitFor
3037
import org.opensearch.jobscheduler.spi.schedule.IntervalSchedule
3138
import org.opensearch.test.junit.annotations.TestLogging
3239
import java.time.Instant
@@ -45,7 +52,7 @@ class RollupSecurityBehaviorIT : SecurityRestTestCase() {
4552

4653
@Before
4754
fun setupUsersAndRoles() {
48-
updateClusterSetting(ManagedIndexSettings.JITTER.key, "0.0", false)
55+
updateClusterSetting(ManagedIndexSettings.Companion.JITTER.key, "0.0", false)
4956

5057
// Init super transform user
5158
val helpdeskClusterPermissions =

0 commit comments

Comments
 (0)