From e7529684f9fe6c0b71015ae2338228bdf2693d41 Mon Sep 17 00:00:00 2001 From: Bhavana Ramaram Date: Wed, 12 Jul 2023 03:52:02 -0700 Subject: [PATCH] IT Security Tests for model access control (#1095) * IT Security Tests for model access control Signed-off-by: Bhavana Ramaram * Fix assertion error Signed-off-by: Bhavana Ramaram * Fix format violations Signed-off-by: Bhavana Ramaram --------- Signed-off-by: Bhavana Ramaram --- .../TransportUpdateModelGroupAction.java | 6 +- .../TransportUpdateModelGroupActionTests.java | 2 +- .../ml/rest/MLCommonsRestTestCase.java | 3 +- .../ml/rest/MLModelGroupRestIT.java | 1200 +++++++++++++++-- .../opensearch/ml/rest/SecureMLRestIT.java | 2 +- 5 files changed, 1067 insertions(+), 146 deletions(-) diff --git a/plugin/src/main/java/org/opensearch/ml/action/model_group/TransportUpdateModelGroupAction.java b/plugin/src/main/java/org/opensearch/ml/action/model_group/TransportUpdateModelGroupAction.java index 2079b93917..825a0d8f23 100644 --- a/plugin/src/main/java/org/opensearch/ml/action/model_group/TransportUpdateModelGroupAction.java +++ b/plugin/src/main/java/org/opensearch/ml/action/model_group/TransportUpdateModelGroupAction.java @@ -218,7 +218,7 @@ private void validateRequestForAccessControl(MLUpdateModelGroupInput input, User && !modelAccessControlHelper.isAdmin(user) && !modelAccessControlHelper.isOwnerStillHasPermission(user, mlModelGroup)) { throw new IllegalArgumentException( - "You don’t have the specified backend role to update access control data. For more information, contact your administrator." + "You don’t have the specified backend role to update this model group. For more information, contact your administrator." ); } AccessMode accessMode = input.getModelAccessMode(); @@ -258,7 +258,9 @@ private boolean hasAccessControlChange(MLUpdateModelGroupInput input) { } private void validateSecurityDisabledOrModelAccessControlDisabled(MLUpdateModelGroupInput input) { - if (input.getModelAccessMode() != null || input.getIsAddAllBackendRoles() != null || input.getBackendRoles() != null) { + if (input.getModelAccessMode() != null + || input.getIsAddAllBackendRoles() != null + || !CollectionUtils.isEmpty(input.getBackendRoles())) { throw new IllegalArgumentException( "You cannot specify model access control parameters because the Security plugin or model access control is disabled on your cluster." ); diff --git a/plugin/src/test/java/org/opensearch/ml/action/model_group/TransportUpdateModelGroupActionTests.java b/plugin/src/test/java/org/opensearch/ml/action/model_group/TransportUpdateModelGroupActionTests.java index f5c2b5d497..1397f6bbe5 100644 --- a/plugin/src/test/java/org/opensearch/ml/action/model_group/TransportUpdateModelGroupActionTests.java +++ b/plugin/src/test/java/org/opensearch/ml/action/model_group/TransportUpdateModelGroupActionTests.java @@ -172,7 +172,7 @@ public void test_OwnerNoMoreHasPermissionException() { ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Exception.class); verify(actionListener).onFailure(argumentCaptor.capture()); assertEquals( - "You don’t have the specified backend role to update access control data. For more information, contact your administrator.", + "You don’t have the specified backend role to update this model group. For more information, contact your administrator.", argumentCaptor.getValue().getMessage() ); } diff --git a/plugin/src/test/java/org/opensearch/ml/rest/MLCommonsRestTestCase.java b/plugin/src/test/java/org/opensearch/ml/rest/MLCommonsRestTestCase.java index 13e40ed292..963fd91f76 100644 --- a/plugin/src/test/java/org/opensearch/ml/rest/MLCommonsRestTestCase.java +++ b/plugin/src/test/java/org/opensearch/ml/rest/MLCommonsRestTestCase.java @@ -657,13 +657,14 @@ public MLRegisterModelInput createRegisterModelInput(String modelGroupID) { } public MLRegisterModelGroupInput createRegisterModelGroupInput( + String name, List backendRoles, AccessMode modelAccessMode, Boolean isAddAllBackendRoles ) { return MLRegisterModelGroupInput .builder() - .name("modelGroupName") + .name(name) .description("This is a test model group") .backendRoles(backendRoles) .modelAccessMode(modelAccessMode) diff --git a/plugin/src/test/java/org/opensearch/ml/rest/MLModelGroupRestIT.java b/plugin/src/test/java/org/opensearch/ml/rest/MLModelGroupRestIT.java index 113d20460c..04d0fbece8 100644 --- a/plugin/src/test/java/org/opensearch/ml/rest/MLModelGroupRestIT.java +++ b/plugin/src/test/java/org/opensearch/ml/rest/MLModelGroupRestIT.java @@ -8,6 +8,8 @@ package org.opensearch.ml.rest; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Map; import org.apache.http.HttpHeaders; @@ -26,6 +28,7 @@ import org.opensearch.ml.common.transport.model_group.MLUpdateModelGroupInput; import org.opensearch.ml.utils.TestHelper; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; public class MLModelGroupRestIT extends MLCommonsRestTestCase { @@ -34,15 +37,21 @@ public class MLModelGroupRestIT extends MLCommonsRestTestCase { RestClient mlNoAccessClient; String mlReadOnlyUser = "ml_readonly"; RestClient mlReadOnlyClient; - String mlFullAccessNoIndexAccessUser = "ml_full_access_no_index_access"; - RestClient mlFullAccessNoIndexAccessClient; + String mlFullAccessUser = "ml_full_access"; RestClient mlFullAccessClient; - String mlNonAdminFullAccessWithoutBackendRoleUser = "ml_non_admin_full_access_without_backend_role_user"; - RestClient mlNonAdminFullAccessWithoutBackendRoleClient; - String mlNonOwnerFullAccessWithBackendRoleUser = "ml_non_owner_full_access_with_backend_role_user"; - RestClient mlNonOwnerFullAccessWithBackendRoleClient; + String user1 = "user1"; + RestClient user1Client; + + String user2 = "user2"; + RestClient user2Client; + + String user3 = "user3"; + RestClient user3Client; + + String user4 = "user4"; + RestClient user4Client; private String indexSearchAccessRole = "ml_test_index_all_search"; @@ -60,6 +69,20 @@ public class MLModelGroupRestIT extends MLCommonsRestTestCase { private String modelGroupId; private String password = "IntegTest@MLModelGroupRestIT123"; + public void disableModelAccessControl(boolean isSecurityEnabled) throws IOException { + Response response = TestHelper + .makeRequest( + client(), + "PUT", + "_cluster/settings", + null, + "{\"persistent\":{\"plugins.ml_commons.model_access_control_enabled\":" + isSecurityEnabled + "}}", + ImmutableList.of(new BasicHeader(HttpHeaders.USER_AGENT, "")) + ); + assertEquals(200, response.getStatusLine().getStatusCode()); + + } + @Before public void setup() throws IOException { Response response = TestHelper @@ -88,210 +111,1105 @@ public void setup() throws IOException { .setSocketTimeout(60000) .build(); - createUser(mlFullAccessNoIndexAccessUser, password, ImmutableList.of(opensearchBackendRole)); - mlFullAccessNoIndexAccessClient = new SecureRestClientBuilder( - getClusterHosts().toArray(new HttpHost[0]), - isHttps(), - mlFullAccessNoIndexAccessUser, - password - ).setSocketTimeout(60000).build(); - - createUser(mlFullAccessUser, password, ImmutableList.of(opensearchBackendRole)); + createUser(mlFullAccessUser, password, new ArrayList<>(Arrays.asList(opensearchBackendRole))); mlFullAccessClient = new SecureRestClientBuilder(getClusterHosts().toArray(new HttpHost[0]), isHttps(), mlFullAccessUser, password) .setSocketTimeout(60000) .build(); - createUser(mlNonAdminFullAccessWithoutBackendRoleUser, password, ImmutableList.of()); - mlNonAdminFullAccessWithoutBackendRoleClient = new SecureRestClientBuilder( - getClusterHosts().toArray(new HttpHost[0]), - isHttps(), - mlNonAdminFullAccessWithoutBackendRoleUser, - password - ).setSocketTimeout(60000).build(); - - createUser(mlNonOwnerFullAccessWithBackendRoleUser, password, ImmutableList.of(opensearchBackendRole)); - mlNonOwnerFullAccessWithBackendRoleClient = new SecureRestClientBuilder( - getClusterHosts().toArray(new HttpHost[0]), - isHttps(), - mlNonOwnerFullAccessWithBackendRoleUser, - password - ).setSocketTimeout(60000).build(); + createUser(user1, password, ImmutableList.of("IT", "HR")); + user1Client = new SecureRestClientBuilder(getClusterHosts().toArray(new HttpHost[0]), isHttps(), user1, password) + .setSocketTimeout(60000) + .build(); - createRoleMapping("ml_read_access", ImmutableList.of(mlReadOnlyUser)); - createRoleMapping( - "ml_full_access", - ImmutableList - .of( - mlFullAccessNoIndexAccessUser, - mlFullAccessUser, - mlNonAdminFullAccessWithoutBackendRoleUser, - mlNonOwnerFullAccessWithBackendRoleUser - ) - ); - createRoleMapping( - indexSearchAccessRole, - ImmutableList.of(mlFullAccessUser, mlNonAdminFullAccessWithoutBackendRoleUser, mlNonOwnerFullAccessWithBackendRoleUser) - ); + createUser(user2, password, ImmutableList.of("IT")); + user2Client = new SecureRestClientBuilder(getClusterHosts().toArray(new HttpHost[0]), isHttps(), user2, password) + .setSocketTimeout(60000) + .build(); - mlRegisterModelGroupInput = createRegisterModelGroupInput(ImmutableList.of(opensearchBackendRole), AccessMode.RESTRICTED, false); + createUser(user3, password, ImmutableList.of("Finance")); + user3Client = new SecureRestClientBuilder(getClusterHosts().toArray(new HttpHost[0]), isHttps(), user3, password) + .setSocketTimeout(60000) + .build(); - registerModelGroup(mlFullAccessClient, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { - this.modelGroupId = (String) registerModelGroupResult.get("model_group_id"); - }); + createUser(user4, password, ImmutableList.of()); + user4Client = new SecureRestClientBuilder(getClusterHosts().toArray(new HttpHost[0]), isHttps(), user4, password) + .setSocketTimeout(60000) + .build(); - mlUpdateModelGroupInput = createUpdateModelGroupInput( - this.modelGroupId, - "new_name", - "new description", - ImmutableList.of(opensearchBackendRole), - AccessMode.RESTRICTED, - false - ); + createRoleMapping("ml_read_access", ImmutableList.of(mlReadOnlyUser)); + createRoleMapping("ml_full_access", ImmutableList.of(mlFullAccessUser, user1, user2, user3, user4)); + createRoleMapping(indexSearchAccessRole, ImmutableList.of(mlFullAccessUser, user1, user2, user3, user4)); } @After public void deleteUserSetup() throws IOException { mlNoAccessClient.close(); mlReadOnlyClient.close(); - mlFullAccessNoIndexAccessClient.close(); mlFullAccessClient.close(); - mlNonAdminFullAccessWithoutBackendRoleClient.close(); - mlNonOwnerFullAccessWithBackendRoleClient.close(); + user1Client.close(); + user2Client.close(); + user3Client.close(); + user4Client.close(); deleteUser(mlNoAccessUser); deleteUser(mlReadOnlyUser); - deleteUser(mlFullAccessNoIndexAccessUser); deleteUser(mlFullAccessUser); - deleteUser(mlNonAdminFullAccessWithoutBackendRoleUser); - deleteUser(mlNonOwnerFullAccessWithBackendRoleUser); + deleteUser(user1); + deleteUser(user2); + deleteUser(user3); + deleteUser(user4); } public void test_registerModelGroup_withNoAccess() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, false); exceptionRule.expect(ResponseException.class); exceptionRule.expectMessage("no permissions for [cluster:admin/opensearch/ml/register_model_group]"); registerModelGroup(mlNoAccessClient, TestHelper.toJsonString(mlRegisterModelGroupInput), null); } public void test_registerModelGroup_WithReadOnlyMLAccess() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, false); exceptionRule.expect(ResponseException.class); exceptionRule.expectMessage("no permissions for [cluster:admin/opensearch/ml/register_model_group]"); registerModelGroup(mlReadOnlyClient, TestHelper.toJsonString(mlRegisterModelGroupInput), null); } - public void test_registerModelGroup_withFullAccess() throws IOException { + public void test_updateModelGroup_withNoAccess() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, false); + registerModelGroup(mlFullAccessClient, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { - assertTrue(registerModelGroupResult.containsKey("model_group_id")); + this.modelGroupId = (String) registerModelGroupResult.get("model_group_id"); }); - } - public void test_updateModelGroup_withNoAccess() throws IOException { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + this.modelGroupId, + "new_name", + "new description", + ImmutableList.of(opensearchBackendRole), + AccessMode.RESTRICTED, + false + ); + exceptionRule.expect(ResponseException.class); exceptionRule.expectMessage("no permissions for [cluster:admin/opensearch/ml/update_model_group]"); updateModelGroup(mlNoAccessClient, this.modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); } public void test_updateModelGroup_WithReadOnlyMLAccess() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, false); + + registerModelGroup(mlFullAccessClient, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + this.modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + }); + + mlUpdateModelGroupInput = createUpdateModelGroupInput( + this.modelGroupId, + "new_name", + "new description", + ImmutableList.of(opensearchBackendRole), + AccessMode.RESTRICTED, + false + ); + exceptionRule.expect(ResponseException.class); exceptionRule.expectMessage("no permissions for [cluster:admin/opensearch/ml/update_model_group]"); updateModelGroup(mlReadOnlyClient, this.modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); } - public void test_updateModelGroup_userIsOwner() throws IOException { - updateModelGroup( - mlFullAccessClient, - this.modelGroupId, - TestHelper.toJsonString(mlUpdateModelGroupInput), - registerModelGroupResult -> { - assertTrue(registerModelGroupResult.containsKey("status")); + public void test_searchModelGroup_withNoAccess() throws IOException { + exceptionRule.expect(ResponseException.class); + exceptionRule.expectMessage("no permissions for [cluster:admin/opensearch/ml/model_groups/search]"); + searchModelGroups(mlNoAccessClient, MATCH_ALL_QUERY, null); + } + + public void test_RegisterModelGroupForUser1WithAddAllBackendRoles() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.RESTRICTED, true); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + // User2 successfully updates model group with no access data + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name2", "description2", null, null, null); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + // User1 successfully updates model group with access data because user1 is owner + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + null, + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + // Admin successfully updates model group with access data + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + null, + "description", + Arrays.asList("IT", "HR"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + } catch (IOException e) { + assertNull(e); } - ); + // User1 fails to update model group when trying to give backend role as Finance + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + Arrays.asList("Finance"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don't have the backend roles specified.")); + } + // User2 fails to update model group with access data because user2 is not the owner + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + // User3 fails to update model group + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, null, null); + updateModelGroup(user3Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don't have permission to update this model group.")); + } + // User1 fails to update model group when specifying backend roles to public access mode + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + Arrays.asList("HR"), + AccessMode.PUBLIC, + null + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains("You can specify backend roles only for a model group with the restricted access mode.") + ); + } + }); } - public void test_updateModelGroup_userIsNonOwnerHasBackendRole() throws IOException { - MLUpdateModelGroupInput mlUpdateModelGroupInput = createUpdateModelGroupInput( - this.modelGroupId, - "new_name", - "new description", - null, - null, - null - ); - updateModelGroup( - mlNonOwnerFullAccessWithBackendRoleClient, - this.modelGroupId, - TestHelper.toJsonString(mlUpdateModelGroupInput), - registerModelGroupResult -> { - assertTrue(registerModelGroupResult.containsKey("status")); + public void test_RegisterModelGroupForUser1WithBackendRolesField() throws IOException { + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", ImmutableList.of("HR"), AccessMode.RESTRICTED, null); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + + try { + // Admin successfully updates model group with access data + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name2", + "description2", + ImmutableList.of("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + // User1 successfully updates model group to add all backend roles because user1 is the owner + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, null, null, null, AccessMode.RESTRICTED, true); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + // User1 successfully updates model group to HR because user1 is the owner + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name2", + "description2", + ImmutableList.of("HR"), + AccessMode.RESTRICTED, + false + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + } catch (IOException e) { + assertNull(e); + } + // User2 fails to update model group because user does not have HR backend role + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + ImmutableList.of("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + // User3 fails to update model group because user does not have HR backend role + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, null, null); + updateModelGroup(user3Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don't have permission to update this model group.")); + } + // User4 fails to update model group because the user does not have HR backend role + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, AccessMode.PRIVATE, null); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + // Admin fails to update model group when trying to set add_all_backend_roles to true + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + true + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Admin users cannot add all backend roles to a model group.")); + } + // User1 fails to update model group when setting add_all_backend_roles to false and not specifying any backend roles + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + false + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains("You have to specify backend roles when add all backend roles is set to false.") + ); + } + // User1 fails to update model group when neither setting add_all_backend_roles to true nor specifying any backend roles + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains( + "You must specify one or more backend roles or add all backend roles to register a restricted model group." + ) + ); + } + // User1 fails to update model group when trying to five Finance as backend roles + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + ImmutableList.of("Finance"), + AccessMode.RESTRICTED, + false + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don't have the backend roles specified.")); + } + }); + + } + + public void test_RegisterModelGroupForUser1WithPublic() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, null); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + String modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + // User2 successfully updates model group if no access data is specified + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, null, "description1", null, null, null); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + // User3 successfully updates model group if no access data is specified + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", null, null, null, null); + updateModelGroup(user3Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + // User4 successfully updates model group if no access data is specified + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name2", "description2", null, null, null); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + // Admin successfully updates model group to restricted + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + null, + null, + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + // User1 successfully updates model group with access data because user1 is the owner + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, AccessMode.PUBLIC, null); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + } catch (IOException e) { + assertNull(e); + } + // User2 fails to update model group when access data is given because user2 is non-owner + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name1", + null, + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + // User1 fails to update model group when specifying both backend_roles and add_all_backend_roles fields + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name2", + null, + Arrays.asList("HR"), + AccessMode.RESTRICTED, + true + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains("You cannot specify backend roles and add all backend roles at the same time.") + ); + } + }); + } + + public void test_RegisterModelGroupForUser1WithPrivate() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PRIVATE, null); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + String modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + // Admin successfully updates model group + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name1", + "description1", + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + // User1 successfully updates model group specifying access data because user1 is the owner + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, AccessMode.PRIVATE, null); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + } catch (IOException e) { + assertNull(e); } + // User2 fails to update model group because it is private to user1 + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", "description1", null, null, null); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don't have permission to update this model group.")); + } + // Admin fails to update model group when trying to set add_all_backend_roles to true + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + true + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Admin users cannot add all backend roles to a model group.")); + } + // User1 fails to update model group when trying to specify backend roles for public access mode + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, AccessMode.PUBLIC, true); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains("You can specify backend roles only for a model group with the restricted access mode.") + ); + } + }); + } + + public void test_RegisterModelGroupOnAccessControlDisabledCluster() throws IOException { + disableModelAccessControl(false); + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, null, null); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + String modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + // Admin successfully updates model group + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", "description1", null, null, null); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + // User1 successfully updates model group + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name2", "description2", null, null, null); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + // User4 successfully updates model group + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, null, null); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + } catch (IOException e) { + assertNull(e); + } + // User1 fails to update model group with access data + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name1", + "description1", + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains( + "You cannot specify model access control parameters because the Security plugin or model access control is disabled on your cluster." + ) + ); + } + // Admin fails to update model group with access data + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains( + "You cannot specify model access control parameters because the Security plugin or model access control is disabled on your cluster." + ) + ); + } + // User3 fails to update model group with model acces data + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, AccessMode.PUBLIC, null); + updateModelGroup(user3Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains( + "You cannot specify model access control parameters because the Security plugin or model access control is disabled on your cluster." + ) + ); + } + }); + } + + public void test_RegisterModelGroupForAdminWithRestricted() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput( + "modelGroupName", + ImmutableList.of("Finance"), + AccessMode.RESTRICTED, + false ); + registerModelGroup(client(), TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + String modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + // Admin successfully updates model group with access data + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name1", + "description1", + ImmutableList.of("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + // Admin successfully updates model group with access data + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + null, + null, + ImmutableList.of("Finance"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + // User3 successfully updates model group without access data because user3 has Finance backend role but is not the owner + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, null, null); + updateModelGroup(user3Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + } catch (IOException e) { + assertNull(e); + } + // User2 fails to update model group + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", "description1", null, null, null); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don't have permission to update this model group.")); + } + // User3 fails to update model group when trying to specify access control data + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + true + ); + updateModelGroup(user3Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + // Admin fails to update model group when trying to specify add all backend roles field + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + true + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Admin users cannot add all backend roles to a model group.")); + } + }); + } + + public void test_RegisterModelGroupForAdminWithPublic() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, null); + registerModelGroup(client(), TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + String modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + // User3 successfully updates model group without access data + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", null, null, null, null); + updateModelGroup(user3Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + // User3 successfully updates model group without access data + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, null, "description1", null, null, null); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + // Admin successfully updates model group with access data + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + ImmutableList.of("Finance"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + // Admin successfully updates model group with access data + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, null, null, null, AccessMode.PUBLIC, null); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + } catch (IOException e) { + assertNull(e); + } + // User1 fails to update model group when trying to specify access control data + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + // User2 fails to update model group when trying to specify access control data + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + // Admin fails to update model group when trying to specify backend roles for public access mode + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + Arrays.asList("IT"), + AccessMode.PUBLIC, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains("You can specify backend roles only for a model group with the restricted access mode.") + ); + } + }); } - public void test_updateModelGroup_userIsNonOwnerNoBackendRole_withPermissionFields() throws IOException { + public void test_RegisterModelGroupForAdminWithPrivate() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PRIVATE, false); + registerModelGroup(client(), TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + String modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", null, null, AccessMode.PUBLIC, null); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", null, null, AccessMode.PRIVATE, null); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + } catch (IOException e) { + assertNull(e); + } + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, null, null); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don't have permission to update this model group.")); + } + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, AccessMode.PUBLIC, null); + updateModelGroup(user2Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + }); + } + + public void test_RegisterModelGroupForUser4WithPublic() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, null); + registerModelGroup(user4Client, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + String modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", null, null, null, null); + updateModelGroup(user3Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name1", null, null, AccessMode.PRIVATE, null); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + ImmutableList.of("Finance"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, null, null, null, AccessMode.PUBLIC, null); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + } catch (IOException e) { + assertNull(e); + } + + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + true + ); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don’t have any backend roles.")); + } + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + Arrays.asList("IT"), + AccessMode.RESTRICTED, + null + ); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("Only owner or admin can update access control data.")); + } + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, AccessMode.PRIVATE, true); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains("You can specify backend roles only for a model group with the restricted access mode.") + ); + } + }); + } + + public void test_RegisterModelGroupForUser4WithPrivate() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PRIVATE, null); + registerModelGroup(user4Client, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { + String modelGroupId = (String) registerModelGroupResult.get("model_group_id"); + assertTrue(registerModelGroupResult.containsKey("model_group_id")); + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, null, null); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, null, null, null, AccessMode.PUBLIC, null); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, null, null, null, AccessMode.PRIVATE, null); + updateModelGroup(client(), modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), updateModelGroupResult -> { + assertTrue(updateModelGroupResult.containsKey("status")); + }); + } catch (IOException e) { + assertNull(e); + } + + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput( + modelGroupId, + "name", + "description", + null, + AccessMode.RESTRICTED, + true + ); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don’t have any backend roles.")); + } + try { + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, null, null); + updateModelGroup(user1Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue(Throwables.getStackTraceAsString(e).contains("You don't have permission to update this model group.")); + } + try { + + mlUpdateModelGroupInput = createUpdateModelGroupInput(modelGroupId, "name", "description", null, AccessMode.PRIVATE, true); + updateModelGroup(user4Client, modelGroupId, TestHelper.toJsonString(mlUpdateModelGroupInput), null); + } catch (Exception e) { + assertEquals(ResponseException.class, e.getClass()); + assertTrue( + Throwables + .getStackTraceAsString(e) + .contains("You can specify backend roles only for a model group with the restricted access mode.") + ); + } + }); + } + + public void test_AdminCreateRestrictedWithAddAllBackendRoles() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.RESTRICTED, true); exceptionRule.expect(ResponseException.class); - exceptionRule.expectMessage("Only owner or admin can update access control data."); - updateModelGroup( - mlNonAdminFullAccessWithoutBackendRoleClient, - this.modelGroupId, - TestHelper.toJsonString(mlUpdateModelGroupInput), - null - ); + exceptionRule.expectMessage("Admin users cannot add all backend roles to a model group."); + registerModelGroup(client(), TestHelper.toJsonString(mlRegisterModelGroupInput), null); } - public void test_updateModelGroup_userIsNonOwner_withoutPermissionFields() throws IOException { + public void test_User1CreateRestrictedITWithAddAllBackendRoles() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", Arrays.asList("IT"), AccessMode.RESTRICTED, true); exceptionRule.expect(ResponseException.class); - exceptionRule.expectMessage("You don't have permission to update this model group."); - MLUpdateModelGroupInput mlUpdateModelGroupInput = createUpdateModelGroupInput( - this.modelGroupId, - "new_name", - "new description", - null, - null, - null - ); - updateModelGroup( - mlNonAdminFullAccessWithoutBackendRoleClient, - this.modelGroupId, - TestHelper.toJsonString(mlUpdateModelGroupInput), - null - ); + exceptionRule.expectMessage("You cannot specify backend roles and add all backend roles at the same time."); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); } - public void test_searchModelGroup_withNoAccess() throws IOException { + public void test_User1CreatePublicWithBackendRole() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", Arrays.asList("IT"), AccessMode.PUBLIC, null); exceptionRule.expect(ResponseException.class); - exceptionRule.expectMessage("no permissions for [cluster:admin/opensearch/ml/model_groups/search]"); - searchModelGroups(mlNoAccessClient, MATCH_ALL_QUERY, null); + exceptionRule.expectMessage("You can specify backend roles only for a model group with the restricted access mode."); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); } - public void test_searchModelGroup_WithReadOnlyMLAccess() throws IOException { + public void test_User1CreatePrivateWithBackendRole() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", Arrays.asList("IT"), AccessMode.PRIVATE, null); exceptionRule.expect(ResponseException.class); - exceptionRule.expectMessage("no permissions for [cluster:admin/opensearch/ml/model_groups/search]"); - searchModelGroups(mlReadOnlyClient, MATCH_ALL_QUERY, null); + exceptionRule.expectMessage("You can specify backend roles only for a model group with the restricted access mode."); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); } - public void test_searchModelGroup_userIsOwner() throws IOException { - searchModelGroups(mlFullAccessClient, MATCH_ALL_QUERY, r -> { - assertTrue(r.containsKey("hits")); - assertTrue(((Map) r.get("hits")).containsKey("total")); - Map total = (Map) ((Map) r.get("hits")).get("total"); - assertEquals(1.0, total.get("value")); - }); + public void test_User1CreatePublicWithAddAllBackendRole() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, true); + exceptionRule.expect(ResponseException.class); + exceptionRule.expectMessage("You can specify backend roles only for a model group with the restricted access mode."); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); } - public void test_searchModelGroup_userNonOwnerHasBackendRole() throws IOException { - searchModelGroups(mlNonOwnerFullAccessWithBackendRoleClient, MATCH_ALL_QUERY, r -> { - assertTrue(r.containsKey("hits")); - assertTrue(((Map) r.get("hits")).containsKey("total")); - Map total = (Map) ((Map) r.get("hits")).get("total"); - assertEquals(1.0, total.get("value")); - }); + public void test_User1CreatePrivateWithAddAllBackendRole() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PRIVATE, true); + exceptionRule.expect(ResponseException.class); + exceptionRule.expectMessage("You can specify backend roles only for a model group with the restricted access mode."); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); } - public void test_searchModelGroup_userHasNoModelAccess() throws IOException { - searchModelGroups(mlNonAdminFullAccessWithoutBackendRoleClient, MATCH_ALL_QUERY, r -> { - assertTrue(r.containsKey("hits")); - assertTrue(((Map) r.get("hits")).containsKey("total")); - Map total = (Map) ((Map) r.get("hits")).get("total"); - assertEquals(0.0, total.get("value")); - }); + public void test_User4CreateRestrictedWithAddAllBackendRoles() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.RESTRICTED, true); + exceptionRule.expect(ResponseException.class); + exceptionRule.expectMessage("You must have at least one backend role to register a restricted model group."); + registerModelGroup(user4Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + } + + public void test_User3CreateRestrictedWithNoBackendRolesFields() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.RESTRICTED, null); + exceptionRule.expect(ResponseException.class); + exceptionRule + .expectMessage("You must specify one or more backend roles or add all backend roles to register a restricted model group."); + registerModelGroup(user3Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + } + + public void test_User2CreateRestrictedWithBackendRoleThatDoesNotBelongtoTheUser() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", Arrays.asList("Finance"), AccessMode.RESTRICTED, null); + exceptionRule.expect(ResponseException.class); + exceptionRule.expectMessage("You don't have the backend roles specified."); + registerModelGroup(user2Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + } + + public void test_User3CreateRestrictedWithBothBackendRolesFields() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", Arrays.asList("Finance"), AccessMode.RESTRICTED, true); + exceptionRule.expect(ResponseException.class); + exceptionRule.expectMessage("You cannot specify backend roles and add all backend roles at the same time."); + registerModelGroup(user3Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + } + + public void test_User1CreateModelGroupWithAccessDataOnAccessControlDisabledCluster() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", Arrays.asList("IT"), AccessMode.RESTRICTED, true); + disableModelAccessControl(false); + exceptionRule.expect(ResponseException.class); + exceptionRule + .expectMessage( + "You cannot specify model access control parameters because the Security plugin or model access control is disabled on your cluster." + ); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + } + + public void test_search_MatchAllQuery_For_ModelGroups() throws IOException { + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName1", Arrays.asList("IT"), AccessMode.RESTRICTED, null); + registerModelGroup(client(), TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName2", null, AccessMode.PUBLIC, null); + registerModelGroup(client(), TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName3", null, AccessMode.PRIVATE, null); + registerModelGroup(client(), TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName4", null, AccessMode.RESTRICTED, true); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName5", null, AccessMode.PRIVATE, null); + registerModelGroup(user1Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName6", null, AccessMode.RESTRICTED, true); + registerModelGroup(user2Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName7", null, AccessMode.RESTRICTED, true); + registerModelGroup(user3Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName8", null, AccessMode.PUBLIC, null); + registerModelGroup(user3Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName9", null, AccessMode.PRIVATE, null); + registerModelGroup(user3Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName10", null, AccessMode.PUBLIC, null); + registerModelGroup(user4Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName11", null, AccessMode.PRIVATE, null); + registerModelGroup(user4Client, TestHelper.toJsonString(mlRegisterModelGroupInput), null); + + try { + searchModelGroups(client(), MATCH_ALL_QUERY, r -> { + assertTrue(r.containsKey("hits")); + assertTrue(((Map) r.get("hits")).containsKey("total")); + Map total = (Map) ((Map) r.get("hits")).get("total"); + assertEquals(11.0, total.get("value")); + }); + } catch (IOException e) { + assertNull(e); + } + try { + searchModelGroups(user1Client, MATCH_ALL_QUERY, r -> { + assertTrue(r.containsKey("hits")); + assertTrue(((Map) r.get("hits")).containsKey("total")); + Map total = (Map) ((Map) r.get("hits")).get("total"); + assertEquals(7.0, total.get("value")); + }); + } catch (IOException e) { + assertNull(e); + } + try { + searchModelGroups(user2Client, MATCH_ALL_QUERY, r -> { + assertTrue(r.containsKey("hits")); + assertTrue(((Map) r.get("hits")).containsKey("total")); + Map total = (Map) ((Map) r.get("hits")).get("total"); + assertEquals(6.0, total.get("value")); + }); + } catch (IOException e) { + assertNull(e); + } + try { + searchModelGroups(user3Client, MATCH_ALL_QUERY, r -> { + assertTrue(r.containsKey("hits")); + assertTrue(((Map) r.get("hits")).containsKey("total")); + Map total = (Map) ((Map) r.get("hits")).get("total"); + assertEquals(5.0, total.get("value")); + }); + } catch (IOException e) { + assertNull(e); + } + try { + searchModelGroups(user4Client, MATCH_ALL_QUERY, r -> { + assertTrue(r.containsKey("hits")); + assertTrue(((Map) r.get("hits")).containsKey("total")); + Map total = (Map) ((Map) r.get("hits")).get("total"); + assertEquals(4.0, total.get("value")); + }); + } catch (IOException e) { + assertNull(e); + } } } diff --git a/plugin/src/test/java/org/opensearch/ml/rest/SecureMLRestIT.java b/plugin/src/test/java/org/opensearch/ml/rest/SecureMLRestIT.java index eb30592b23..bebdfd08bf 100644 --- a/plugin/src/test/java/org/opensearch/ml/rest/SecureMLRestIT.java +++ b/plugin/src/test/java/org/opensearch/ml/rest/SecureMLRestIT.java @@ -111,7 +111,7 @@ public void setup() throws IOException { searchSourceBuilder.fetchSource(new String[] { "petal_length_in_cm", "petal_width_in_cm" }, null); // Create public model group - mlRegisterModelGroupInput = createRegisterModelGroupInput(null, AccessMode.PUBLIC, false); + mlRegisterModelGroupInput = createRegisterModelGroupInput("modelGroupName", null, AccessMode.PUBLIC, false); registerModelGroup(mlFullAccessClient, TestHelper.toJsonString(mlRegisterModelGroupInput), registerModelGroupResult -> { this.modelGroupId = (String) registerModelGroupResult.get("model_group_id");