1+ package org.opensearch.indexmanagement
2+
3+ import org.apache.http.entity.ContentType
4+ import org.apache.http.entity.StringEntity
5+ import org.junit.Assert
6+ import org.opensearch.client.Request
7+ import org.opensearch.client.Response
8+ import org.opensearch.client.RestClient
9+ import org.opensearch.commons.rest.SecureRestClientBuilder
10+ import org.opensearch.rest.RestStatus
11+ import org.opensearch.test.junit.annotations.TestLogging
12+
13+ @TestLogging(" level:DEBUG" , reason = " Debug for tests." )
14+ class SecurityBehaviorIT : IndexManagementRestTestCase () {
15+
16+ var financeUserClient: RestClient ? = null
17+ var hrUserClient: RestClient ? = null
18+ var adminUserClient: RestClient ? = null
19+ var noAuthUserClient: RestClient ? = null
20+
21+ fun `test security behavior for ISM` () {
22+ setupUsersAndRoles()
23+
24+ disableFilterBy()
25+ var financeResponse = createPolicy(" finance-policy" , 10 , financeUserClient)
26+ var hrResponse = createPolicy(" hr-policy" , 15 , hrUserClient)
27+ var adminResponse = createPolicy(" admin-policy" , 0 , adminUserClient)
28+ var noAuthResponse = createPolicy(" noauth-policy" , 100 , noAuthUserClient)
29+
30+ assertEquals(" User jane failed to create policy" , RestStatus .CREATED , financeResponse?.restStatus())
31+ assertEquals(" User jack failed to create policy" , RestStatus .CREATED , hrResponse?.restStatus())
32+ assertEquals(" User sam failed to create policy" , RestStatus .CREATED , adminResponse?.restStatus())
33+ assertEquals(" User noauth didn't fail to create policy" , RestStatus .FORBIDDEN , noAuthResponse?.restStatus())
34+
35+ financeResponse = getPolicies(financeUserClient)
36+ hrResponse = getPolicies(hrUserClient)
37+ adminResponse = getPolicies(adminUserClient)
38+ noAuthResponse = getPolicies(noAuthUserClient)
39+
40+ assertEquals(" User jane cannot get policies" , RestStatus .OK , financeResponse?.restStatus())
41+ assertEquals(" User jack cannot get policies" , RestStatus .OK , hrResponse?.restStatus())
42+ assertEquals(" User sam cannot get policies" , RestStatus .OK , adminResponse?.restStatus())
43+ assertEquals(" User noauth can get policies" , RestStatus .FORBIDDEN , noAuthResponse?.restStatus())
44+
45+ // Ensure all users can see each other policies
46+ assertEquals(" User jane not able to see all policies" , 3 , financeResponse?.asMap()?.get(" total_policies" ))
47+ assertEquals(" User jack not able to see all policies" , 3 , hrResponse?.asMap()?.get(" total_policies" ))
48+ assertEquals(" User sam not able to see all policies" , 3 , adminResponse?.asMap()?.get(" total_policies" ))
49+
50+ client().performRequest(Request (" PUT" , " /finance-1" ))
51+ client().performRequest(Request (" PUT" , " /hr-1" ))
52+ client().performRequest(Request (" PUT" , " /marketing-1" ))
53+
54+ financeResponse = explainManagedIndices(financeUserClient)
55+ hrResponse = explainManagedIndices(hrUserClient)
56+ adminResponse = explainManagedIndices(adminUserClient)
57+ noAuthResponse = explainManagedIndices(noAuthUserClient)
58+
59+ assertEquals(" User jane cannot get managed indices" , RestStatus .OK , financeResponse?.restStatus())
60+ assertEquals(" User jack cannot get managed indices" , RestStatus .OK , hrResponse?.restStatus())
61+ assertEquals(" User sam cannot get managed indices" , RestStatus .OK , adminResponse?.restStatus())
62+ assertEquals(" User noauth can get managed indices" , RestStatus .FORBIDDEN , noAuthResponse?.restStatus())
63+
64+ assertEquals(" User jane seeing more managed indices than allowed" , 1 , financeResponse?.asMap()?.get(" total_managed_indices" ))
65+ assertEquals(" User jack seeing more managed indices than allowed" , 1 , hrResponse?.asMap()?.get(" total_managed_indices" ))
66+ assertEquals(" User sam seeing more managed indices than allowed" , 3 , adminResponse?.asMap()?.get(" total_managed_indices" ))
67+
68+ // Enabling backend role filtering
69+ enableFilterBy()
70+ financeResponse = getPolicies(financeUserClient)
71+ hrResponse = getPolicies(hrUserClient)
72+ adminResponse = getPolicies(adminUserClient)
73+
74+ // Only admin can all policies other users only can see intersecting policies
75+ assertEquals(" User jane not able to see all policies" , 2 , financeResponse?.asMap()?.get(" total_policies" ))
76+ assertEquals(" User jack not able to see all policies" , 1 , hrResponse?.asMap()?.get(" total_policies" ))
77+ assertEquals(" User sam not able to see all policies" , 3 , adminResponse?.asMap()?.get(" total_policies" ))
78+
79+ disableFilterBy()
80+ }
81+
82+ private fun createPolicy (name : String , priority : Int , userClient : RestClient ? ): Response ? {
83+ val request = Request (" PUT" , " _plugins/_ism/policies/$name " )
84+ val json = """
85+ {
86+ "policy": {
87+ "description": "test policy",
88+ "default_state": "start",
89+ "states": [
90+ {
91+ "name": "start",
92+ "actions": [
93+ {
94+ "replica_count": {
95+ "number_of_replicas": 5
96+ }
97+ }
98+ ],
99+ "transitions": []
100+ }
101+ ],
102+ "ism_template": {
103+ "index_patterns": ["*"],
104+ "priority": $priority
105+ }
106+ }
107+ }
108+ """ .trimIndent()
109+ request.setJsonEntity(json)
110+ return userClient?.performRequest(request)
111+ }
112+
113+ private fun getPolicies (userClient : RestClient ? ): Response ? {
114+ val request = Request (" GET" , " _plugins/_ism/policies" )
115+ return userClient?.performRequest(request)
116+ }
117+
118+ private fun explainManagedIndices (userClient : RestClient ? ): Response ? {
119+ val request = Request (" GET" , " _plugins/_ism/explain" )
120+ return userClient?.performRequest(request)
121+ }
122+
123+ private fun createUser (name : String , pwd : String = "Test123 !", backendRoles : List <String > = listOf()) {
124+ val request = Request (" PUT" , " _plugins/_security/api/internalusers/$name " )
125+ val backendRolesStr = backendRoles.joinToString(" ," )
126+ val json = """
127+ {
128+ "password": $pwd ,
129+ "backend_roles": [$backendRolesStr ],
130+ "attributes":{}
131+ }
132+ """ .trimIndent()
133+ request.setJsonEntity(json)
134+ client().performRequest(request)
135+ }
136+
137+ private fun enableFilterBy () {
138+ val setting = """
139+ {
140+ "persistent": {
141+ "plugins.index_management.filter_by_backend_roles": "true"
142+ }
143+ }
144+ """ .trimIndent()
145+ val updateResponse = client().makeRequest(" PUT" , " _cluster/settings" , emptyMap(), StringEntity (setting, ContentType .APPLICATION_JSON ))
146+ assertEquals(updateResponse.statusLine.toString(), 200 , updateResponse.statusLine.statusCode)
147+ }
148+
149+ private fun disableFilterBy () {
150+ val setting = """
151+ {
152+ "persistent": {
153+ "plugins.index_management.filter_by_backend_roles": "false"
154+ }
155+ }
156+ """ .trimIndent()
157+ val updateResponse = client().makeRequest(" PUT" , " _cluster/settings" , emptyMap(), StringEntity (setting, ContentType .APPLICATION_JSON ))
158+ Assert .assertEquals(updateResponse.statusLine.toString(), 200 , updateResponse.statusLine.statusCode)
159+ }
160+
161+ private fun addUsersToRole (role : String , users : List <String >) {
162+ val request = Request (" PUT" , " /_plugins/_security/api/rolesmapping/$role " )
163+ val usersStr = users.joinToString(" ," )
164+ var entity = """
165+ {
166+ "backend_roles": [],
167+ "hosts": [],
168+ "users": [$usersStr ]
169+ }
170+ """ .trimIndent()
171+ request.setJsonEntity(entity)
172+ client().performRequest(request)
173+ }
174+
175+ private fun addRole (name : String , clusterPermissions : List <String >, indexPatterns : List <String >, indexPermissions : List <String >) {
176+ val request = Request (" PUT" , " /_plugins/_security/api/roles/$name " )
177+ val indexPatternsStr = indexPatterns.joinToString(" ," )
178+ val clusterPermissionsStr = clusterPermissions.joinToString(" ," )
179+ val indexPermissionsStr = indexPermissions.joinToString(" ," )
180+ val entity = """
181+ {
182+ "cluster_permissions": [$clusterPermissionsStr ],
183+ "index_permissions": [
184+ {
185+ "fls": [],
186+ "masked_fields": [],
187+ "allowed_actions": [$indexPermissionsStr ],
188+ "index_patterns": [$indexPatternsStr ]
189+ }
190+ ],
191+ "tenant_permissions": []
192+ }
193+ """ .trimIndent()
194+
195+ request.setJsonEntity(entity)
196+ client().performRequest(request)
197+ }
198+
199+ private fun setupUsersAndRoles () {
200+ // Create user jane with backend roles - ["finance", "general"]
201+ createUser(" jane" , backendRoles = listOf (" finance" , " hr" ))
202+
203+ // Create user jack with backend roles - ["hr"]
204+ createUser(" jack" , backendRoles = listOf (" hr" ))
205+
206+ // Create user sam with backend roles - ["general"]
207+ createUser(" sam" , backendRoles = listOf (" general" ))
208+
209+ // Create user auth with no backend roles
210+ createUser(" noauth" )
211+
212+ val clusterPermissions = listOf (
213+ " cluster:admin/opendistro/ism/*" ,
214+ " cluster:admin/opendistro/rollup/*" ,
215+ " cluster:admin/opendistro/transform/*" ,
216+ )
217+ val indexPermissions = listOf (
218+ " indices:admin/opensearch/ism/*" ,
219+ " indices:admin/mappings/get" ,
220+ " indices:data/read/search"
221+ )
222+ // Create role - "finance_im_role"
223+ addRole(" finance_im_role" , clusterPermissions, listOf (" finance-*" ), indexPermissions)
224+
225+ // Create role - "hr_im_role"
226+ addRole(" hr_im_role" , clusterPermissions, listOf (" hr-*" ), indexPermissions)
227+
228+ // add roles to all the users
229+ addUsersToRole(" finance_im_role" , listOf (" jane" ))
230+ addUsersToRole(" hr_im_role" , listOf (" jack" ))
231+ addUsersToRole(" all_access" , listOf (" sam" ))
232+
233+ financeUserClient = SecureRestClientBuilder (clusterHosts.toTypedArray(), isHttps(), " jane" , " Test123!" ).setSocketTimeout(60000 ).build()
234+ hrUserClient = SecureRestClientBuilder (clusterHosts.toTypedArray(), isHttps(), " jack" , " Test123!" ).setSocketTimeout(60000 ).build()
235+ adminUserClient = SecureRestClientBuilder (clusterHosts.toTypedArray(), isHttps(), " sam" , " Test123!" ).setSocketTimeout(60000 ).build()
236+ noAuthUserClient = SecureRestClientBuilder (clusterHosts.toTypedArray(), isHttps(), " noauth" , " Test123!" ).setSocketTimeout(60000 ).build()
237+ }
238+ }
0 commit comments