diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/AbstractRestApiUnitTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/AbstractRestApiUnitTest.java
index 6e775bbc62..efc39bb466 100644
--- a/src/test/java/org/opensearch/security/dlic/rest/api/AbstractRestApiUnitTest.java
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/AbstractRestApiUnitTest.java
@@ -16,10 +16,14 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.stream.Stream;
 
 import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpStatus;
 import org.junit.Assert;
@@ -90,18 +94,7 @@ protected final void setupWithRestRoles(Settings nodeOverride) throws Exception
 				.put("plugins.security.ssl.http.truststore_filepath",
 						FileHelper.getAbsoluteFilePathFromClassPath("restapi/truststore.jks"));
 
-		builder.put("plugins.security.restapi.roles_enabled.0", "opendistro_security_role_klingons");
-		builder.put("plugins.security.restapi.roles_enabled.1", "opendistro_security_role_vulcans");
-		builder.put("plugins.security.restapi.roles_enabled.2", "opendistro_security_test");
-
-		builder.put("plugins.security.restapi.endpoints_disabled.global.CACHE.0", "*");
-
-		builder.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_klingons.conFiGuration.0", "*");
-		builder.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_klingons.wRongType.0", "WRONGType");
-		builder.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_klingons.ROLESMAPPING.0", "PUT");
-		builder.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_klingons.ROLESMAPPING.1", "DELETE");
-
-		builder.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_vulcans.CONFIG.0", "*");
+		builder.put(rolesSettings());
 
 		if (null != nodeOverride) {
 			builder.put(nodeOverride);
@@ -114,6 +107,20 @@ protected final void setupWithRestRoles(Settings nodeOverride) throws Exception
 		AuditTestUtils.updateAuditConfig(rh, nodeOverride != null ? nodeOverride : Settings.EMPTY);
 	}
 
+	protected Settings rolesSettings() {
+		return Settings.builder()
+				.put("plugins.security.restapi.roles_enabled.0", "opendistro_security_role_klingons")
+				.put("plugins.security.restapi.roles_enabled.1", "opendistro_security_role_vulcans")
+				.put("plugins.security.restapi.roles_enabled.2", "opendistro_security_test")
+				.put("plugins.security.restapi.endpoints_disabled.global.CACHE.0", "*")
+				.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_klingons.conFiGuration.0", "*")
+				.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_klingons.wRongType.0", "WRONGType")
+				.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_klingons.ROLESMAPPING.0", "PUT")
+				.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_klingons.ROLESMAPPING.1", "DELETE")
+				.put("plugins.security.restapi.endpoints_disabled.opendistro_security_role_vulcans.CONFIG.0", "*")
+				.build();
+	}
+
 	protected void deleteUser(String username) throws Exception {
 		boolean sendAdminCertificate = rh.sendAdminCertificate;
 		rh.sendAdminCertificate = true;
@@ -197,30 +204,23 @@ protected void checkGeneralAccess(int status, String username, String password)
 
 	protected String checkReadAccess(int status, String username, String password, String indexName, String actionType,
 			int id) throws Exception {
-		boolean sendAdminCertificate = rh.sendAdminCertificate;
 		rh.sendAdminCertificate = false;
 		String action = indexName + "/" + actionType + "/" + id;
-		HttpResponse response = rh.executeGetRequest(action,
-				encodeBasicHeader(username, password));
+		HttpResponse response = rh.executeGetRequest(action, encodeBasicHeader(username, password));
 		int returnedStatus = response.getStatusCode();
 		Assert.assertEquals(status, returnedStatus);
-		rh.sendAdminCertificate = sendAdminCertificate;
 		return response.getBody();
 
 	}
 
 	protected String checkWriteAccess(int status, String username, String password, String indexName, String actionType,
 			int id) throws Exception {
-
-		boolean sendAdminCertificate = rh.sendAdminCertificate;
 		rh.sendAdminCertificate = false;
 		String action = indexName + "/" + actionType + "/" + id;
 		String payload = "{\"value\" : \"true\"}";
-		HttpResponse response = rh.executePutRequest(action, payload,
-				encodeBasicHeader(username, password));
+		HttpResponse response = rh.executePutRequest(action, payload, encodeBasicHeader(username, password));
 		int returnedStatus = response.getStatusCode();
 		Assert.assertEquals(status, returnedStatus);
-		rh.sendAdminCertificate = sendAdminCertificate;
 		return response.getBody();
 	}
 
@@ -260,4 +260,27 @@ protected Map<String, String> jsonStringToMap(String json) throws JsonParseExcep
 	protected static Collection<Class<? extends Plugin>> asCollection(Class<? extends Plugin>... plugins) {
 		return Arrays.asList(plugins);
 	}
+
+	String createRestAdminPermissionsPayload(String... additionPerms) throws JsonProcessingException {
+		final ObjectNode rootNode = (ObjectNode) DefaultObjectMapper.objectMapper.createObjectNode();
+		rootNode.set("cluster_permissions", clusterPermissionsForRestAdmin(additionPerms));
+		return DefaultObjectMapper.objectMapper.writeValueAsString(rootNode);
+	}
+
+	ArrayNode clusterPermissionsForRestAdmin(String... additionPerms) {
+		final ArrayNode permissionsArray = (ArrayNode) DefaultObjectMapper.objectMapper.createArrayNode();
+		for (final Endpoint endpoint : RestApiAdminPrivilegesEvaluator.ENDPOINTS_WITH_PERMISSIONS) {
+			if (endpoint == Endpoint.SSL) {
+				permissionsArray
+						.add(RestApiAdminPrivilegesEvaluator.PERMISSION_BUILDER.build(endpoint, "certs"))
+						.add(RestApiAdminPrivilegesEvaluator.PERMISSION_BUILDER.build(endpoint, "reloadcerts"));
+			} else {
+				permissionsArray.add(RestApiAdminPrivilegesEvaluator.PERMISSION_BUILDER.build(endpoint, null));
+			}
+		}
+		if (additionPerms.length != 0) {
+			Stream.of(additionPerms).forEach(permissionsArray::add);
+		}
+		return permissionsArray;
+	}
 }
diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiTest.java
index 6323746a7f..5b03e6474e 100644
--- a/src/test/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiTest.java
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiTest.java
@@ -13,6 +13,9 @@
 
 import java.util.List;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpStatus;
 import org.junit.Assert;
@@ -20,6 +23,7 @@
 
 import org.opensearch.common.settings.Settings;
 import org.opensearch.common.xcontent.XContentType;
+import org.opensearch.security.DefaultObjectMapper;
 import org.opensearch.security.dlic.rest.validation.AbstractConfigurationValidator;
 import org.opensearch.security.test.helper.file.FileHelper;
 import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse;
@@ -44,9 +48,31 @@ public void testActionGroupsApi() throws Exception {
         rh.keystore = "restapi/kirk-keystore.jks";
         rh.sendAdminCertificate = true;
 
+        // create index
+        setupStarfleetIndex();
+
+        // add user picard, role starfleet, maps to opendistro_security_role_starfleet
+        addUserWithPassword("picard", "picard", new String[] { "starfleet" }, HttpStatus.SC_CREATED);
+        checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+        // TODO: only one doctype allowed for ES6
+        // checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "public", 0);
+        checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
+        // TODO: only one doctype allowed for ES6
+        //checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "public", 0);
+        rh.sendAdminCertificate = true;
+        verifyGetForSuperAdmin(new Header[0]);
+        rh.sendAdminCertificate = true;
+        verifyDeleteForSuperAdmin(new Header[0], true);
+        rh.sendAdminCertificate = true;
+        verifyPutForSuperAdmin(new Header[0], true);
+        rh.sendAdminCertificate = true;
+        verifyPatchForSuperAdmin(new Header[0], true);
+    }
+
+    void verifyGetForSuperAdmin(final Header[] header) throws Exception {
         // --- GET_UT
         // GET_UT, actiongroup exists
-        HttpResponse response = rh.executeGetRequest(ENDPOINT+"/CRUD_UT", new Header[0]);
+        HttpResponse response = rh.executeGetRequest(ENDPOINT+"/CRUD_UT", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         List<String> permissions = settings.getAsList("CRUD_UT.allowed_actions");
@@ -56,61 +82,50 @@ public void testActionGroupsApi() throws Exception {
         Assert.assertTrue(permissions.contains("OPENDISTRO_SECURITY_WRITE"));
 
         // GET_UT, actiongroup does not exist
-        response = rh.executeGetRequest(ENDPOINT+"/nothinghthere", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT+"/nothinghthere", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // GET_UT, old endpoint
-        response = rh.executeGetRequest(ENDPOINT, new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT, header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // GET_UT, old endpoint
-        response = rh.executeGetRequest(ENDPOINT, new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT, header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // GET_UT, new endpoint which replaces configuration endpoint
-        response = rh.executeGetRequest(ENDPOINT, new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT, header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // GET_UT, old endpoint
-        response = rh.executeGetRequest(ENDPOINT, new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT, header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // GET_UT, new endpoint which replaces configuration endpoint
-        response = rh.executeGetRequest(ENDPOINT, new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT, header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // GET_UT, new endpoint which replaces configuration endpoint
-        response = rh.executeGetRequest(ENDPOINT, new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT, header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+    }
 
-        // create index
-        setupStarfleetIndex();
-
-        // add user picard, role starfleet, maps to opendistro_security_role_starfleet
-        addUserWithPassword("picard", "picard", new String[] { "starfleet" }, HttpStatus.SC_CREATED);
-        checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
-        // TODO: only one doctype allowed for ES6
-        // checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "public", 0);
-        checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
-        // TODO: only one doctype allowed for ES6
-        //checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "public", 0);
-
+    void verifyDeleteForSuperAdmin(final Header[] header, final boolean userAdminCert) throws Exception {
         // -- DELETE
         // Non-existing role
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = userAdminCert;
 
-        response = rh.executeDeleteRequest(ENDPOINT+"/idonotexist", new Header[0]);
+        HttpResponse response = rh.executeDeleteRequest(ENDPOINT+"/idonotexist", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // remove action group READ_UT, read access not possible since
         // opendistro_security_role_starfleet
         // uses this action group.
-        response = rh.executeDeleteRequest(ENDPOINT+"/READ_UT", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT+"/READ_UT", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         rh.sendAdminCertificate = false;
         checkReadAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
-
         // put picard in captains role. Role opendistro_security_role_captains uses the CRUD_UT
         // action group
         // which uses READ_UT and WRITE action groups. We removed READ_UT, so only
@@ -125,24 +140,25 @@ public void testActionGroupsApi() throws Exception {
         rh.sendAdminCertificate = false;
         checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
         checkReadAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
+    }
 
+    void verifyPutForSuperAdmin(final Header[] header, final boolean userAdminCert) throws Exception {
         // -- PUT
-
         // put with empty payload, must fail
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT+"/SOMEGROUP", "", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        HttpResponse response = rh.executePutRequest(ENDPOINT+"/SOMEGROUP", "", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
-        settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.PAYLOAD_MANDATORY.getMessage(), settings.get("reason"));
 
         // put new configuration with invalid payload, must fail
         response = rh.executePutRequest(ENDPOINT+"/SOMEGROUP", FileHelper.loadFile("restapi/actiongroup_not_parseable.json"),
-                                        new Header[0]);
+                header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.BODY_NOT_PARSEABLE.getMessage(), settings.get("reason"));
 
-        response = rh.executePutRequest(ENDPOINT+"/CRUD_UT", FileHelper.loadFile("restapi/actiongroup_crud.json"), new Header[0]);
+        response = rh.executePutRequest(ENDPOINT+"/CRUD_UT", FileHelper.loadFile("restapi/actiongroup_crud.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
 
         rh.sendAdminCertificate = false;
@@ -152,8 +168,8 @@ public void testActionGroupsApi() throws Exception {
         checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
 
         // restore READ_UT action groups
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT+"/READ_UT", FileHelper.loadFile("restapi/actiongroup_read.json"), new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePutRequest(ENDPOINT+"/READ_UT", FileHelper.loadFile("restapi/actiongroup_read.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
 
         rh.sendAdminCertificate = false;
@@ -162,99 +178,106 @@ public void testActionGroupsApi() throws Exception {
         checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
 
         // -- PUT, new JSON format including readonly flag, disallowed in REST API
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT+"/CRUD_UT", FileHelper.loadFile("restapi/actiongroup_readonly.json"), new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePutRequest(ENDPOINT+"/CRUD_UT", FileHelper.loadFile("restapi/actiongroup_readonly.json"), header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // -- DELETE read only resource, must be forbidden
         // superAdmin can delete read only resource
-        rh.sendAdminCertificate = true;
-        response = rh.executeDeleteRequest(ENDPOINT+"/GET_UT", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executeDeleteRequest(ENDPOINT+"/GET_UT", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // -- PUT read only resource, must be forbidden
         // superAdmin can add/update read only resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT+"/GET_UT", FileHelper.loadFile("restapi/actiongroup_read.json"), new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePutRequest(ENDPOINT+"/GET_UT", FileHelper.loadFile("restapi/actiongroup_read.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
         Assert.assertFalse(response.getBody().contains("Resource 'GET_UT' is read-only."));
 
         // PUT with role name
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT+"/kibana_user", FileHelper.loadFile("restapi/actiongroup_read.json"), new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePutRequest(ENDPOINT+"/kibana_user", FileHelper.loadFile("restapi/actiongroup_read.json"), header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("kibana_user is an existing role. A action group cannot be named with an existing role name."));
 
         // PUT with self-referencing action groups
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT+"/reference_itself", "{\"allowed_actions\": [\"reference_itself\"]}", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePutRequest(ENDPOINT+"/reference_itself", "{\"allowed_actions\": [\"reference_itself\"]}", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("reference_itself cannot be an allowed_action of itself"));
 
         // -- GET_UT hidden resource, must be 404 but super admin can find it
-        rh.sendAdminCertificate = true;
-        response = rh.executeGetRequest(ENDPOINT+"/INTERNAL", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executeGetRequest(ENDPOINT+"/INTERNAL", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("\"hidden\":true"));
 
         // -- DELETE hidden resource, must be 404
-        rh.sendAdminCertificate = true;
-        response = rh.executeDeleteRequest(ENDPOINT+"/INTERNAL", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executeDeleteRequest(ENDPOINT+"/INTERNAL", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("'INTERNAL' deleted."));
 
         // -- PUT hidden resource, must be forbidden
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT+"/INTERNAL", FileHelper.loadFile("restapi/actiongroup_read.json"), new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePutRequest(ENDPOINT+"/INTERNAL", FileHelper.loadFile("restapi/actiongroup_read.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
+    }
 
+    void verifyPatchForSuperAdmin(final Header[] header, final boolean userAdminCert) throws Exception {
         // -- PATCH
         // PATCH on non-existing resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT+"/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        HttpResponse response = rh.executePatchRequest(ENDPOINT+"/imnothere",
+                "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // PATCH read only resource, must be forbidden
         // SuperAdmin can patch read only resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT+"/GET_UT", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT+"/GET_UT", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // PATCH with self-referencing action groups
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT+"/GET_UT", "[{ \"op\": \"add\", \"path\": \"/allowed_actions/-\", \"value\": \"GET_UT\" }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT+"/GET_UT", "[{ \"op\": \"add\", \"path\": \"/allowed_actions/-\", \"value\": \"GET_UT\" }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("GET_UT cannot be an allowed_action of itself"));
 
         // bulk PATCH with self-referencing action groups
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/BULKNEW1\", \"value\": {\"allowed_actions\": [\"BULKNEW1\"] } }," + "{ \"op\": \"add\", \"path\": \"/BULKNEW2\", \"value\": {\"allowed_actions\": [\"READ_UT\"] } }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/BULKNEW1\", \"value\": {\"allowed_actions\": [\"BULKNEW1\"] } }," +
+                "{ \"op\": \"add\", \"path\": \"/BULKNEW2\", \"value\": {\"allowed_actions\": [\"READ_UT\"] } }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("BULKNEW1 cannot be an allowed_action of itself"));
 
         // PATCH hidden resource, must be not found, can be found by superadmin, but fails with no path exist error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT+"/INTERNAL", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT+"/INTERNAL",
+                "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH value of hidden flag, must fail with validation error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody(), response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
         // PATCH with relative JSON pointer, must fail
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"1/INTERNAL/allowed_actions/-\", \"value\": \"OPENDISTRO_SECURITY_DELETE\" }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"1/INTERNAL/allowed_actions/-\", " +
+                "\"value\": \"OPENDISTRO_SECURITY_DELETE\" }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH new format
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"/allowed_actions/-\", \"value\": \"OPENDISTRO_SECURITY_DELETE\" }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"/allowed_actions/-\", " +
+                "\"value\": \"OPENDISTRO_SECURITY_DELETE\" }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT+"/CRUD_UT", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT+"/CRUD_UT", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
-        permissions = settings.getAsList("CRUD_UT.allowed_actions");
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        List<String> permissions = settings.getAsList("CRUD_UT.allowed_actions");
         Assert.assertNotNull(permissions);
         Assert.assertEquals(3, permissions.size());
         Assert.assertTrue(permissions.contains("READ_UT"));
@@ -265,49 +288,50 @@ public void testActionGroupsApi() throws Exception {
         // -- PATCH on whole config resource
         // PATCH read only resource, must be forbidden
         // SuperAdmin can patch read only resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/GET_UT/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/GET_UT/a\", \"value\": [ \"foo\", \"bar\" ] }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/GET_UT/description\", \"value\": \"foo\" }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/GET_UT/description\", \"value\": \"foo\" }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // PATCH hidden resource, must be bad request
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/INTERNAL/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/INTERNAL/a\", \"value\": [ \"foo\", \"bar\" ] }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH delete read only resource, must be forbidden
         // SuperAdmin can delete read only resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/GET_UT\" }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/GET_UT\" }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // PATCH delete hidden resource, must be bad request
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/INTERNAL\" }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/INTERNAL\" }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("\"message\":\"Resource updated."));
 
 
         // PATCH value of hidden flag, must fail with validation error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/CRUD_UT/hidden\", \"value\": true }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/CRUD_UT/hidden\", \"value\": true }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
         // add new resource with hidden flag, must fail with validation error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/NEWNEWNEW\", \"value\": {\"allowed_actions\": [\"indices:data/write*\"], \"hidden\":true }}]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT,
+                "[{ \"op\": \"add\", \"path\": \"/NEWNEWNEW\", \"value\": {\"allowed_actions\": [\"indices:data/write*\"], \"hidden\":true }}]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
         // add new valid resources
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/BULKNEW1\", \"value\": {\"allowed_actions\": [\"indices:data/*\", \"cluster:monitor/*\"] } }," + "{ \"op\": \"add\", \"path\": \"/BULKNEW2\", \"value\": {\"allowed_actions\": [\"READ_UT\"] } }]", new Header[0]);
+        rh.sendAdminCertificate = userAdminCert;
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/BULKNEW1\", \"value\": {\"allowed_actions\": [\"indices:data/*\", \"cluster:monitor/*\"] } }," + "{ \"op\": \"add\", \"path\": \"/BULKNEW2\", \"value\": {\"allowed_actions\": [\"READ_UT\"] } }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT+"/BULKNEW1", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT+"/BULKNEW1", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         permissions = settings.getAsList("BULKNEW1.allowed_actions");
@@ -316,7 +340,7 @@ public void testActionGroupsApi() throws Exception {
         Assert.assertTrue(permissions.contains("indices:data/*"));
         Assert.assertTrue(permissions.contains("cluster:monitor/*"));
 
-        response = rh.executeGetRequest(ENDPOINT+"/BULKNEW2", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT+"/BULKNEW2", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         permissions = settings.getAsList("BULKNEW2.allowed_actions");
@@ -325,13 +349,13 @@ public void testActionGroupsApi() throws Exception {
         Assert.assertTrue(permissions.contains("READ_UT"));
 
         // delete resource
-        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/BULKNEW1\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/BULKNEW1\" }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT+"/BULKNEW1", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT+"/BULKNEW1", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // assert other resource is still there
-        response = rh.executeGetRequest(ENDPOINT+"/BULKNEW2", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT+"/BULKNEW2", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         permissions = settings.getAsList("BULKNEW2.allowed_actions");
@@ -340,6 +364,93 @@ public void testActionGroupsApi() throws Exception {
         Assert.assertTrue(permissions.contains("READ_UT"));
     }
 
+    @Test
+    public void testActionGroupsApiForRestAdmin() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+        // create index
+        setupStarfleetIndex();
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+
+        // add user picard, role starfleet, maps to opendistro_security_role_starfleet
+        addUserWithPassword("picard", "picard", new String[] { "starfleet" }, HttpStatus.SC_CREATED);
+        checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+        // TODO: only one doctype allowed for ES6
+        // checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "public", 0);
+        checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
+        // TODO: only one doctype allowed for ES6
+        //checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "public", 0);
+        verifyGetForSuperAdmin(new Header[] {restApiAdminHeader});
+        verifyDeleteForSuperAdmin(new Header[]{restApiAdminHeader}, false);
+        verifyPutForSuperAdmin(new Header[]{restApiAdminHeader}, false);
+        verifyPatchForSuperAdmin(new Header[]{restApiAdminHeader}, false);
+    }
+
+    @Test
+    public void testActionGroupsApiForActionGroupsRestApiAdmin() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+        // create index
+        setupStarfleetIndex();
+        final Header restApiAdminActionGroupsHeader = encodeBasicHeader("rest_api_admin_actiongroups", "rest_api_admin_actiongroups");
+
+        // add user picard, role starfleet, maps to opendistro_security_role_starfleet
+        addUserWithPassword("picard", "picard", new String[] { "starfleet" }, HttpStatus.SC_CREATED);
+        checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+        // TODO: only one doctype allowed for ES6
+        // checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "public", 0);
+        checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
+        // TODO: only one doctype allowed for ES6
+        //checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "public", 0);
+        verifyGetForSuperAdmin(new Header[] {restApiAdminActionGroupsHeader});
+        verifyDeleteForSuperAdmin(new Header[]{restApiAdminActionGroupsHeader}, false);
+        verifyPutForSuperAdmin(new Header[]{restApiAdminActionGroupsHeader}, false);
+        verifyPatchForSuperAdmin(new Header[]{restApiAdminActionGroupsHeader}, false);
+    }
+
+    @Test
+    public void testCreateActionGroupWithRestAdminPermissionsForbidden() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        final Header restApiAdminActionGroupsHeader = encodeBasicHeader("rest_api_admin_actiongroups", "rest_api_admin_actiongroups");
+        final Header restApiHeader = encodeBasicHeader("test", "test");
+
+        HttpResponse response = rh.executePutRequest(ENDPOINT + "/rest_api_admin_group", restAdminAllowedActions(),
+                restApiAdminHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+        response = rh.executePutRequest(ENDPOINT + "/rest_api_admin_group", restAdminAllowedActions(), restApiAdminActionGroupsHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+        response = rh.executePutRequest(ENDPOINT + "/rest_api_admin_group", restAdminAllowedActions(), restApiHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        response = rh.executePatchRequest(ENDPOINT, restAdminPatchBody(), restApiAdminHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+        response = rh.executePatchRequest(ENDPOINT, restAdminPatchBody(), restApiAdminActionGroupsHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+        response = rh.executePatchRequest(ENDPOINT, restAdminPatchBody(), restApiHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+    }
+
+    String restAdminAllowedActions() throws JsonProcessingException {
+        final ObjectNode rootNode = DefaultObjectMapper.objectMapper.createObjectNode();
+        rootNode.set("allowed_actions", clusterPermissionsForRestAdmin("cluster/*"));
+        return DefaultObjectMapper.objectMapper.writeValueAsString(rootNode);
+    }
+
+    String restAdminPatchBody() throws JsonProcessingException {
+        final ArrayNode rootNode = DefaultObjectMapper.objectMapper.createArrayNode();
+        final ObjectNode opAddRootNode = DefaultObjectMapper.objectMapper.createObjectNode();
+        final ObjectNode allowedActionsNode = DefaultObjectMapper.objectMapper.createObjectNode();
+        allowedActionsNode.set("allowed_actions", clusterPermissionsForRestAdmin("cluster/*"));
+        opAddRootNode
+                .put("op", "add")
+                .put("path", "/rest_api_admin_group")
+                .set("value", allowedActionsNode);
+        rootNode.add(opAddRootNode);
+        return DefaultObjectMapper.objectMapper.writeValueAsString(rootNode);
+    }
+
     @Test
     public void testActionGroupsApiForNonSuperAdmin() throws Exception {
 
diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/AllowlistApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/AllowlistApiTest.java
index 3d9e2dfc66..05739cc4ab 100644
--- a/src/test/java/org/opensearch/security/dlic/rest/api/AllowlistApiTest.java
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/AllowlistApiTest.java
@@ -142,7 +142,7 @@ public void testPayloadMandatory() throws Exception {
      */
     @Test
     public void testAllowlistApi() throws Exception {
-        setupWithRestRoles(null);
+        setupWithRestRoles();
         // No creds, no admin certificate - UNAUTHORIZED
         checkGetAndPutAllowlistPermissions(HttpStatus.SC_UNAUTHORIZED, false);
 
@@ -156,6 +156,29 @@ public void testAllowlistApi() throws Exception {
         checkGetAndPutAllowlistPermissions(HttpStatus.SC_OK, true, nonAdminCredsHeader);
     }
 
+    @Test
+    public void testAllowlistApiWithPermissions() throws Exception {
+        setupWithRestRoles();
+
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        final Header restApiAllowlistHeader = encodeBasicHeader("rest_api_admin_allowlist", "rest_api_admin_allowlist");
+        final Header restApiUserHeader = encodeBasicHeader("test", "test");
+
+        checkGetAndPutAllowlistPermissions(HttpStatus.SC_FORBIDDEN, false, restApiUserHeader);
+        checkGetAndPutAllowlistPermissions(HttpStatus.SC_OK, false, restApiAdminHeader);
+    }
+
+    @Test
+    public void testAllowlistApiWithAllowListPermissions() throws Exception {
+        setupWithRestRoles();
+
+        final Header restApiAllowlistHeader = encodeBasicHeader("rest_api_admin_allowlist", "rest_api_admin_allowlist");
+        final Header restApiUserHeader = encodeBasicHeader("test", "test");
+
+        checkGetAndPutAllowlistPermissions(HttpStatus.SC_FORBIDDEN, false, restApiUserHeader);
+        checkGetAndPutAllowlistPermissions(HttpStatus.SC_OK, false, restApiAllowlistHeader);
+    }
+
     @Test
     public void testAllowlistAuditComplianceLogging() throws Exception {
         Settings settings = Settings.builder()
diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/LegacySslCertsApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/LegacySslCertsApiTest.java
new file mode 100644
index 0000000000..c73a10ec5a
--- /dev/null
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/LegacySslCertsApiTest.java
@@ -0,0 +1,27 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+
+package org.opensearch.security.dlic.rest.api;
+
+import static org.opensearch.security.OpenSearchSecurityPlugin.LEGACY_OPENDISTRO_PREFIX;
+
+public class LegacySslCertsApiTest extends SslCertsApiTest {
+
+    @Override
+    String certsInfoEndpoint() {
+        return LEGACY_OPENDISTRO_PREFIX + "/api/ssl/certs";
+    }
+
+    @Override
+    String certsReloadEndpoint(String certType) {
+        return String.format("%s/api/ssl/%s/reloadcerts", LEGACY_OPENDISTRO_PREFIX, certType);
+    }
+}
diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/NodesDnApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/NodesDnApiTest.java
index ba46781e7e..03d16ee78d 100644
--- a/src/test/java/org/opensearch/security/dlic/rest/api/NodesDnApiTest.java
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/NodesDnApiTest.java
@@ -181,6 +181,85 @@ public void testNodesDnApi() throws Exception {
         }
     }
 
+
+    @Test
+    public void testNodesDnApiWithPermissions() throws Exception {
+        Settings settings = Settings.builder().put(ConfigConstants.SECURITY_NODES_DN_DYNAMIC_CONFIG_ENABLED, true)
+                .build();
+        setupWithRestRoles(settings);
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        final Header restApiNodesDnHeader = encodeBasicHeader("rest_api_admin_nodesdn", "rest_api_admin_nodesdn");
+        final Header restApiUserHeader = encodeBasicHeader("test", "test");
+        //full access admin
+        {
+            rh.sendAdminCertificate = false;
+            response = rh.executeGetRequest(
+                    ENDPOINT + "/nodesdn", restApiAdminHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+
+            response = rh.executePutRequest(
+                    ENDPOINT + "/nodesdn/c1", "{\"nodes_dn\": [\"cn=popeye\"]}",
+                    restApiAdminHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_CREATED));
+
+            response = rh.executePatchRequest(
+                    ENDPOINT + "/nodesdn/c1",
+                    "[{ \"op\": \"add\", \"path\": \"/nodes_dn/-\", \"value\": \"bluto\" }]",
+                    restApiAdminHeader
+            );
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+
+            response = rh.executeDeleteRequest(ENDPOINT + "/nodesdn/c1", restApiAdminHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+        }
+        //NodesDN only
+        {
+            rh.sendAdminCertificate = false;
+            response = rh.executeGetRequest(ENDPOINT + "/nodesdn", restApiNodesDnHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+
+            response = rh.executePutRequest(
+                    ENDPOINT + "/nodesdn/c1", "{\"nodes_dn\": [\"cn=popeye\"]}",
+                    restApiNodesDnHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_CREATED));
+
+            response = rh.executePatchRequest(
+                    ENDPOINT + "/nodesdn/c1",
+                    "[{ \"op\": \"add\", \"path\": \"/nodes_dn/-\", \"value\": \"bluto\" }]",
+                    restApiNodesDnHeader
+            );
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+
+            response = rh.executeDeleteRequest(ENDPOINT + "/nodesdn/c1", restApiNodesDnHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+
+            response = rh.executeGetRequest(ENDPOINT + "/actiongroups", restApiNodesDnHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_OK));
+        }
+        //rest api user
+        {
+            rh.sendAdminCertificate = false;
+            response = rh.executeGetRequest(ENDPOINT + "/nodesdn", restApiUserHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN));
+
+            response = rh.executePutRequest(
+                    ENDPOINT + "/nodesdn/c1", "{\"nodes_dn\": [\"cn=popeye\"]}",
+                    restApiUserHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN));
+
+            response = rh.executePatchRequest(
+                    ENDPOINT + "/nodesdn/c1",
+                    "[{ \"op\": \"add\", \"path\": \"/nodes_dn/-\", \"value\": \"bluto\" }]",
+                    restApiUserHeader
+            );
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN));
+
+            response = rh.executeDeleteRequest(ENDPOINT + "/nodesdn/c1", restApiUserHeader);
+            assertThat(response.getBody(), response.getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN));
+        }
+
+    }
+
     @Test
     public void testNodesDnApiAuditComplianceLogging() throws Exception {
         Settings settings = Settings.builder().put(ConfigConstants.SECURITY_NODES_DN_DYNAMIC_CONFIG_ENABLED, true)
diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/RolesApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/RolesApiTest.java
index 01fa5b4baf..529658940a 100644
--- a/src/test/java/org/opensearch/security/dlic/rest/api/RolesApiTest.java
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/RolesApiTest.java
@@ -13,7 +13,10 @@
 
 import java.util.List;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpStatus;
 import org.junit.Assert;
@@ -30,12 +33,13 @@
 import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX;
 
 public class RolesApiTest extends AbstractRestApiUnitTest {
-    private final String ENDPOINT; 
+    private final String ENDPOINT;
+
     protected String getEndpointPrefix() {
         return PLUGINS_PREFIX;
     }
 
-    public RolesApiTest(){
+    public RolesApiTest() {
         ENDPOINT = getEndpointPrefix() + "/api";
     }
 
@@ -67,7 +71,26 @@ public void testAllRolesForSuperAdmin() throws Exception {
 
         rh.keystore = "restapi/kirk-keystore.jks";
         rh.sendAdminCertificate = true;
-        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/roles");
+
+        checkSuperAdminRoles(new Header[0]);
+    }
+
+    @Test
+    public void testAllRolesForRestAdmin() throws Exception {
+        setupWithRestRoles();
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        checkSuperAdminRoles(new Header[]{restApiAdminHeader});
+    }
+
+    @Test
+    public void testAllRolesForRolesRestAdmin() throws Exception {
+        setupWithRestRoles();
+        final Header restApiAdminRolesHeader = encodeBasicHeader("rest_api_admin_roles", "rest_api_admin_roles");
+        checkSuperAdminRoles(new Header[]{restApiAdminRolesHeader});
+    }
+
+    void checkSuperAdminRoles(final Header[] header) {
+        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/roles", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertFalse(response.getBody().contains("_meta"));
 
@@ -121,103 +144,110 @@ public void testRolesApi() throws Exception {
         rh.keystore = "restapi/kirk-keystore.jks";
         rh.sendAdminCertificate = true;
 
+        // create index
+        setupStarfleetIndex();
+
+        // add user picard, role starfleet, maps to opendistro_security_role_starfleet
+        addUserWithPassword("picard", "picard", new String[]{"starfleet", "captains"}, HttpStatus.SC_CREATED);
+        checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+        checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+
+        rh.sendAdminCertificate = true;
+        verifyGetForSuperAdmin(new Header[0]);
+        rh.sendAdminCertificate = true;
+        verifyDeleteForSuperAdmin(new Header[0], true);
+        rh.sendAdminCertificate = true;
+        verifyPutForSuperAdmin(new Header[0], true);
+        rh.sendAdminCertificate = true;
+        verifyPatchForSuperAdmin(new Header[0], true);
+    }
+
+    void verifyGetForSuperAdmin(final Header[] header) throws Exception {
         // check roles exists
-        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/roles");
+        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/roles", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // -- GET
-
         // GET opendistro_security_role_starfleet
-        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         JsonNode settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(1, settings.size());
 
         // GET, role does not exist
-        response = rh.executeGetRequest(ENDPOINT + "/roles/nothinghthere", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/nothinghthere", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
-        response = rh.executeGetRequest(ENDPOINT + "/roles/", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
-        response = rh.executeGetRequest(ENDPOINT + "/roles", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("\"cluster_permissions\":[\"*\"]"));
         Assert.assertFalse(response.getBody().contains("\"cluster_permissions\" : ["));
 
-        response = rh.executeGetRequest(ENDPOINT + "/roles?pretty", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles?pretty", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertFalse(response.getBody().contains("\"cluster_permissions\":[\"*\"]"));
         Assert.assertTrue(response.getBody().contains("\"cluster_permissions\" : ["));
 
         // Super admin should be able to describe hidden role
-        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_hidden", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_hidden", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("\"hidden\":true"));
+    }
 
-        // create index
-        setupStarfleetIndex();
-
-        // add user picard, role starfleet, maps to opendistro_security_role_starfleet
-        addUserWithPassword("picard", "picard", new String[] { "starfleet", "captains" }, HttpStatus.SC_CREATED);
-        checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
-        checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
-
-
+    void verifyDeleteForSuperAdmin(final Header[] header, final boolean sendAdminCert) throws Exception {
         // -- DELETE
-
-        rh.sendAdminCertificate = true;
-
         // Non-existing role
-        response = rh.executeDeleteRequest(ENDPOINT + "/roles/idonotexist", new Header[0]);
+        HttpResponse response = rh.executeDeleteRequest(ENDPOINT + "/roles/idonotexist", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // read only role, SuperAdmin can delete the read-only role
-        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_transport_client", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_transport_client", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // hidden role allowed for superadmin
-        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("'opendistro_security_internal' deleted."));
 
         // remove complete role mapping for opendistro_security_role_starfleet_captains
-        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-
         rh.sendAdminCertificate = false;
-
         // user has only role starfleet left, role has READ access only
         checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 1);
-
         // ES7 only supports one doc type, but OpenSearch permission checks run first
         // So we also get a 403 FORBIDDEN when tring to add new document type
         checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
 
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
         // remove also starfleet role, nothing is allowed anymore
-        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         checkReadAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
         checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 0);
+    }
 
+    void verifyPutForSuperAdmin(final Header[] header, final boolean sendAdminCert) throws Exception {
         // -- PUT
         // put with empty roles, must fail
-        response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", "", new Header[0]);
+        HttpResponse response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", "", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
-        settings = DefaultObjectMapper.readTree(response.getBody());
+        JsonNode settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.PAYLOAD_MANDATORY.getMessage(), settings.get("reason").asText());
 
         // put new configuration with invalid payload, must fail
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet",
-                FileHelper.loadFile("restapi/roles_not_parseable.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_not_parseable.json"), header);
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.BODY_NOT_PARSEABLE.getMessage(), settings.get("reason").asText());
 
         // put new configuration with invalid keys, must fail
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet",
-                FileHelper.loadFile("restapi/roles_invalid_keys.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_invalid_keys.json"), header);
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.INVALID_CONFIGURATION.getMessage(), settings.get("reason").asText());
@@ -227,7 +257,7 @@ public void testRolesApi() throws Exception {
 
         // put new configuration with wrong datatypes, must fail
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet",
-                FileHelper.loadFile("restapi/roles_wrong_datatype.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_wrong_datatype.json"), header);
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason").asText());
@@ -236,17 +266,17 @@ public void testRolesApi() throws Exception {
         // put read only role, must be forbidden
         // But SuperAdmin can still create it
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_transport_client",
-                FileHelper.loadFile("restapi/roles_captains.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_captains.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
 
         // put hidden role, must be forbidden, but allowed for super admin
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_internal",
-                FileHelper.loadFile("restapi/roles_captains.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_captains.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
 
         // restore starfleet role
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet",
-                FileHelper.loadFile("restapi/roles_starfleet.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_starfleet.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
         rh.sendAdminCertificate = false;
         checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
@@ -258,41 +288,41 @@ public void testRolesApi() throws Exception {
         //       - 'indices:*'
         checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
 
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
 
         // restore captains role
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains",
-                FileHelper.loadFile("restapi/roles_captains.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_captains.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
         rh.sendAdminCertificate = false;
         checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
         checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
 
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains",
-                FileHelper.loadFile("restapi/roles_complete_invalid.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_complete_invalid.json"), header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
-//        rh.sendAdminCertificate = true;
+//        rh.sendAdminCertificate = sendAdminCert;
 //        response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains",
-//                FileHelper.loadFile("restapi/roles_multiple.json"), new Header[0]);
+//                FileHelper.loadFile("restapi/roles_multiple.json"), header);
 //        Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains",
-                FileHelper.loadFile("restapi/roles_multiple_2.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_multiple_2.json"), header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // check tenants
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains",
-                FileHelper.loadFile("restapi/roles_captains_tenants.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_captains_tenants.json"), header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(2, settings.size());
         Assert.assertEquals(settings.get("status").asText(), "OK");
 
 
-        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         System.out.println(response.getBody());
         settings = DefaultObjectMapper.readTree(response.getBody());
@@ -305,13 +335,13 @@ public void testRolesApi() throws Exception {
 
 
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains",
-                FileHelper.loadFile("restapi/roles_captains_tenants2.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_captains_tenants2.json"), header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(2, settings.size());
         Assert.assertEquals(settings.get("status").asText(), "OK");
 
-        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(1, settings.size());
@@ -327,13 +357,13 @@ public void testRolesApi() throws Exception {
 
         // remove tenants from role
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains",
-                FileHelper.loadFile("restapi/roles_captains_no_tenants.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_captains_no_tenants.json"), header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(2, settings.size());
         Assert.assertEquals(settings.get("status").asText(), "OK");
 
-        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(1, settings.size());
@@ -341,32 +371,46 @@ public void testRolesApi() throws Exception {
         Assert.assertTrue(new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions").get(0).isNull());
 
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains",
-                FileHelper.loadFile("restapi/roles_captains_tenants_malformed.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_captains_tenants_malformed.json"), header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         settings = DefaultObjectMapper.readTree(response.getBody());
         Assert.assertEquals(settings.get("status").asText(), "error");
         Assert.assertEquals(settings.get("reason").asText(), AbstractConfigurationValidator.ErrorType.INVALID_CONFIGURATION.getMessage());
+    }
 
+    void verifyPatchForSuperAdmin(final Header[] header, final boolean sendAdminCert) throws Exception {
         // -- PATCH
         // PATCH on non-existing resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        HttpResponse response = rh.executePatchRequest(
+                ENDPOINT + "/roles/imnothere",
+                "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // PATCH read only resource, must be forbidden
         // SuperAdmin can patch it
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_transport_client", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles/opendistro_security_transport_client",
+                "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // PATCH hidden resource, must be not found, can be found for superadmin, but will fail with no path present exception
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_internal", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles/opendistro_security_internal",
+                "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH value of hidden flag, must fail with validation error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles/opendistro_security_role_starfleet",
+                "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody(), response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
@@ -389,122 +433,328 @@ public void testRolesApi() throws Exception {
 
         // -- PATCH on whole config resource
         // PATCH on non-existing resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"add\", \"path\": \"/imnothere/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                "[{ \"op\": \"add\", \"path\": \"/imnothere/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH read only resource, must be forbidden
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/a\", \"value\": [ \"foo\", \"bar\" ] }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH hidden resource, must be bad request
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/a\", \"value\": [ \"foo\", \"bar\" ] }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH delete read only resource, must be forbidden
         // SuperAdmin can delete read only user
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_transport_client\" }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_transport_client\" }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // PATCH hidden resource, must be bad request, but allowed for superadmin
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_internal\"}]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_internal\"}]",
+                header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("\"message\":\"Resource updated."));
 
         // PATCH value of hidden flag, must fail with validation error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"add\", \"path\": \"/newnewnew\", \"value\": {  \"hidden\": true, \"index_permissions\" : [ {\"index_patterns\" : [ \"sf\" ],\"allowed_actions\" : [ \"OPENDISTRO_SECURITY_READ\" ]}] }}]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                "[{ \"op\": \"add\", \"path\": \"/newnewnew\", \"value\": {  \"hidden\": true, \"index_permissions\" : " +
+                        "[ {\"index_patterns\" : [ \"sf\" ],\"allowed_actions\" : [ \"OPENDISTRO_SECURITY_READ\" ]}] }}]",
+                header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
         // PATCH
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": {   \"index_permissions\" : [ {\"index_patterns\" : [ \"sf\" ],\"allowed_actions\" : [ \"OPENDISTRO_SECURITY_READ\" ]}] }}]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": {   \"index_permissions\" : " +
+                        "[ {\"index_patterns\" : [ \"sf\" ],\"allowed_actions\" : [ \"OPENDISTRO_SECURITY_READ\" ]}] }}]",
+                header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        settings = DefaultObjectMapper.readTree(response.getBody());
-        permissions =  new SecurityJsonNode(settings).get("bulknew1").get("index_permissions").get(0).get("allowed_actions").asList();
+        JsonNode settings = DefaultObjectMapper.readTree(response.getBody());
+        permissions = new SecurityJsonNode(settings).get("bulknew1").get("index_permissions").get(0).get("allowed_actions").asList();
         Assert.assertNotNull(permissions);
         Assert.assertEquals(1, permissions.size());
         Assert.assertTrue(permissions.contains("OPENDISTRO_SECURITY_READ"));
 
         // delete resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // put valid field masks
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_field_mask_valid",
-                FileHelper.loadFile("restapi/roles_field_masks_valid.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_field_masks_valid.json"), header);
         Assert.assertEquals(response.getBody(), HttpStatus.SC_CREATED, response.getStatusCode());
 
         // put invalid field masks
         response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_field_mask_invalid",
-                FileHelper.loadFile("restapi/roles_field_masks_invalid.json"), new Header[0]);
+                FileHelper.loadFile("restapi/roles_field_masks_invalid.json"), header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
+    }
+
+    @Test
+    public void testRolesApiWithAllRestApiPermissions() throws Exception {
+        setupWithRestRoles();
 
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+
+        rh.sendAdminCertificate = false;
+        setupStarfleetIndex();
+
+        // add user picard, role starfleet, maps to opendistro_security_role_starfleet
+        addUserWithPassword("picard", "picard", new String[]{"starfleet", "captains"}, HttpStatus.SC_CREATED);
+        checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+        checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+
+        verifyGetForSuperAdmin(new Header[]{restApiAdminHeader});
+        verifyDeleteForSuperAdmin(new Header[]{restApiAdminHeader}, false);
+        verifyPutForSuperAdmin(new Header[]{restApiAdminHeader}, false);
+        verifyPatchForSuperAdmin(new Header[]{restApiAdminHeader}, false);
     }
 
     @Test
-    public void testRolesApiForNonSuperAdmin() throws Exception {
+    public void testRolesApiWithRestApiRolePermission() throws Exception {
+        setupWithRestRoles();
+
+        final Header restApiRolesHeader = encodeBasicHeader("rest_api_admin_roles", "rest_api_admin_roles");
+
+        rh.sendAdminCertificate = false;
+        setupStarfleetIndex();
+
+        // add user picard, role starfleet, maps to opendistro_security_role_starfleet
+        addUserWithPassword("picard", "picard", new String[]{"starfleet", "captains"}, HttpStatus.SC_CREATED);
+        checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+        checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
+
 
+        verifyGetForSuperAdmin(new Header[]{restApiRolesHeader});
+        verifyDeleteForSuperAdmin(new Header[]{restApiRolesHeader}, false);
+        verifyPutForSuperAdmin(new Header[]{restApiRolesHeader}, false);
+        verifyPatchForSuperAdmin(new Header[]{restApiRolesHeader}, false);
+    }
+
+    @Test
+    public void testCreateOrUpdateRestApiAdminRoleForbiddenForNonSuperAdmin() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        final Header adminHeader = encodeBasicHeader("admin", "admin");
+        final Header restApiHeader = encodeBasicHeader("test", "test");
+
+        final String restAdminPermissionsPayload = createRestAdminPermissionsPayload("cluster/*");
+        HttpResponse response = rh.executePutRequest(
+                ENDPOINT + "/roles/new_rest_admin_role", restAdminPermissionsPayload, restApiAdminHeader);
+        Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
+        response = rh.executePutRequest(
+                ENDPOINT + "/roles/rest_admin_role_to_delete", restAdminPermissionsPayload, restApiAdminHeader);
+        Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
+
+        // attempt to create a new rest admin role by admin
+        response = rh.executePutRequest(
+                ENDPOINT + "/roles/some_rest_admin_role",
+                restAdminPermissionsPayload,
+                adminHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        // attempt to update exiting admin role
+        response = rh.executePutRequest(
+                ENDPOINT + "/roles/new_rest_admin_role",
+                restAdminPermissionsPayload,
+                adminHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        // attempt to patch exiting admin role
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles/new_rest_admin_role",
+                createPatchRestAdminPermissionsPayload("replace"),
+                adminHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        // attempt to update exiting admin role
+        response = rh.executePutRequest(
+                ENDPOINT + "/roles/new_rest_admin_role",
+                restAdminPermissionsPayload,
+                restApiHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        // attempt to create a new rest admin role by admin
+        response = rh.executePutRequest(
+                ENDPOINT + "/roles/some_rest_admin_role",
+                restAdminPermissionsPayload,
+                restApiHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        // attempt to patch exiting admin role and crate a new one
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                createPatchRestAdminPermissionsPayload("replace"),
+                restApiHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                createPatchRestAdminPermissionsPayload("add"),
+                restApiHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles",
+                createPatchRestAdminPermissionsPayload("remove"),
+                restApiHeader);
+        System.out.println("RESPONSE: " + response.getBody());
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+    }
+
+    @Test
+    public void testDeleteRestApiAdminRoleForbiddenForNonSuperAdmin() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        final Header adminHeader = encodeBasicHeader("admin", "admin");
+        final Header restApiHeader = encodeBasicHeader("test", "test");
+
+        final String allRestAdminPermissionsPayload = createRestAdminPermissionsPayload("cluster/*");
+
+        HttpResponse response = rh.executePutRequest(
+                ENDPOINT + "/roles/new_rest_admin_role", allRestAdminPermissionsPayload, restApiAdminHeader);
+        Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
+
+        // attempt to update exiting admin role
+        response = rh.executeDeleteRequest(
+                ENDPOINT + "/roles/new_rest_admin_role",
+                adminHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        //true to change
+        response = rh.executeDeleteRequest(
+                ENDPOINT + "/roles/new_rest_admin_role",
+                allRestAdminPermissionsPayload,
+                restApiHeader);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+    }
+
+
+    private String createPatchRestAdminPermissionsPayload(final String op) throws JsonProcessingException {
+        final ArrayNode rootNode = (ArrayNode) DefaultObjectMapper.objectMapper.createArrayNode();
+        final ObjectNode opAddObjectNode = DefaultObjectMapper.objectMapper.createObjectNode();
+        final ObjectNode clusterPermissionsNode = DefaultObjectMapper.objectMapper.createObjectNode();
+        clusterPermissionsNode.set("cluster_permissions", clusterPermissionsForRestAdmin("cluster/*"));
+        if ("add".equals(op)) {
+            opAddObjectNode
+                    .put("op", "add")
+                    .put("path", "/some_rest_admin_role")
+                    .set("value", clusterPermissionsNode);
+            rootNode.add(opAddObjectNode);
+        }
+
+        if ("remove".equals(op)) {
+            final ObjectNode opRemoveObjectNode = DefaultObjectMapper.objectMapper.createObjectNode();
+            opRemoveObjectNode
+                    .put("op", "remove")
+                    .put("path", "/rest_admin_role_to_delete");
+            rootNode.add(opRemoveObjectNode);
+        }
+
+        if ("replace".equals(op)) {
+            final ObjectNode replaceRemoveObjectNode = DefaultObjectMapper.objectMapper.createObjectNode();
+            replaceRemoveObjectNode
+                    .put("op", "replace")
+                    .put("path", "/new_rest_admin_role/cluster_permissions")
+                    .set("value", clusterPermissionsForRestAdmin("*"));
+
+            rootNode.add(replaceRemoveObjectNode);
+        }
+        return DefaultObjectMapper.objectMapper.writeValueAsString(rootNode);
+    }
+
+    @Test
+    public void testRolesApiForNonSuperAdmin() throws Exception {
         setupWithRestRoles();
 
         rh.keystore = "restapi/kirk-keystore.jks";
         rh.sendAdminCertificate = false;
         rh.sendHTTPClientCredentials = true;
+        checkNonSuperAdminRoles(new Header[0]);
+    }
 
+    void checkNonSuperAdminRoles(final Header[] header) throws Exception {
         HttpResponse response;
 
         // Delete read only roles
-        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_transport_client" , new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_transport_client", header);
         Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
 
         // Put read only roles
-        response = rh.executePutRequest( ENDPOINT + "/roles/opendistro_security_transport_client",
-                                        FileHelper.loadFile("restapi/roles_captains.json"), new Header[0]);
+        response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_transport_client",
+                FileHelper.loadFile("restapi/roles_captains.json"), header);
         Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
 
         // Patch single read only roles
-        response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_transport_client", "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]);
+        response = rh.executePatchRequest(
+                ENDPOINT + "/roles/opendistro_security_transport_client",
+                "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
 
         // Patch multiple read only roles
-        response = rh.executePatchRequest(ENDPOINT + "/roles/", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/description\", \"value\": \"foo\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/roles/",
+                "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/description\", \"value\": \"foo\" }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
 
         // get hidden role
-        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_internal");
+        response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_internal", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // delete hidden role
-        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal" , new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // put hidden role
         String body = FileHelper.loadFile("restapi/roles_captains.json");
-        response = rh.executePutRequest( ENDPOINT+ "/roles/opendistro_security_internal", body, new Header[0]);
+        response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_internal", body, header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // Patch single hidden roles
-        response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_internal", "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_internal",
+                "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // Patch multiple hidden roles
-        response = rh.executePatchRequest(ENDPOINT + "/roles/", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/description\", \"value\": \"foo\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/roles/",
+                "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/description\", \"value\": \"foo\" }]",
+                header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
-
     }
 
     @Test
-    public void checkNullElementsInArray() throws Exception{
+    public void checkNullElementsInArray() throws Exception {
         setup();
         rh.keystore = "restapi/kirk-keystore.jks";
         rh.sendAdminCertificate = true;
@@ -516,7 +766,7 @@ public void checkNullElementsInArray() throws Exception{
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.NULL_ARRAY_ELEMENT.getMessage(), settings.get("reason"));
 
         body = FileHelper.loadFile("restapi/roles_null_array_element_index_permissions.json");
-        response = rh.executePutRequest(ENDPOINT+ "/roles/opendistro_security_role_starfleet", body, new Header[0]);
+        response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", body, new Header[0]);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.NULL_ARRAY_ELEMENT.getMessage(), settings.get("reason"));
diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/RolesMappingApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/RolesMappingApiTest.java
index 168f15dc43..f6405e984a 100644
--- a/src/test/java/org/opensearch/security/dlic/rest/api/RolesMappingApiTest.java
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/RolesMappingApiTest.java
@@ -13,6 +13,9 @@
 
 import java.util.List;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpStatus;
 import org.junit.Assert;
@@ -20,6 +23,7 @@
 
 import org.opensearch.common.settings.Settings;
 import org.opensearch.common.xcontent.XContentType;
+import org.opensearch.security.DefaultObjectMapper;
 import org.opensearch.security.dlic.rest.validation.AbstractConfigurationValidator;
 import org.opensearch.security.test.helper.file.FileHelper;
 import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse;
@@ -44,12 +48,114 @@ public void testRolesMappingApi() throws Exception {
         rh.keystore = "restapi/kirk-keystore.jks";
         rh.sendAdminCertificate = true;
 
+        // create index
+        setupStarfleetIndex();
+        // add user picard, role captains initially maps to
+        // opendistro_security_role_starfleet_captains and opendistro_security_role_starfleet
+        addUserWithPassword("picard", "picard", new String[] { "captains" }, HttpStatus.SC_CREATED);
+        checkWriteAccess(HttpStatus.SC_CREATED, "picard", "picard", "sf", "_doc", 1);
+        // TODO: only one doctype allowed for ES6
+        //checkWriteAccess(HttpStatus.SC_CREATED, "picard", "picard", "sf", "_doc", 1);
+        rh.sendAdminCertificate = true;
+        verifyGetForSuperAdmin(new Header[0]);
+        rh.sendAdminCertificate = true;
+        verifyDeleteForSuperAdmin(new Header[0], true);
+        rh.sendAdminCertificate = true;
+        verifyPutForSuperAdmin(new Header[0]);
+        verifyPatchForSuperAdmin(new Header[0]);
+        // mapping with several backend roles, one of the is captain
+        deleteAndputNewMapping(new Header[0],"rolesmapping_backendroles_captains_list.json", true);
+        checkAllSfAllowed();
+
+        // mapping with one backend role, captain
+        deleteAndputNewMapping(new Header[0],"rolesmapping_backendroles_captains_single.json", true);
+        checkAllSfAllowed();
+
+        // mapping with several users, one is picard
+        deleteAndputNewMapping(new Header[0],"rolesmapping_users_picard_list.json", true);
+        checkAllSfAllowed();
+
+        // just user picard
+        deleteAndputNewMapping(new Header[0],"rolesmapping_users_picard_single.json", true);
+        checkAllSfAllowed();
+
+        // hosts
+        deleteAndputNewMapping(new Header[0],"rolesmapping_hosts_list.json", true);
+        checkAllSfAllowed();
+
+        // hosts
+        deleteAndputNewMapping(new Header[0],"rolesmapping_hosts_single.json", true);
+        checkAllSfAllowed();
+
+        // full settings, access
+        deleteAndputNewMapping(new Header[0],"rolesmapping_all_access.json", true);
+        checkAllSfAllowed();
+
+        // full settings, no access
+        deleteAndputNewMapping(new Header[0],"rolesmapping_all_noaccess.json", true);
+        checkAllSfForbidden();
+    }
+
+    @Test
+    public void testRolesMappingApiWithFullPermissions() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        // create index
+        setupStarfleetIndex();
+        // add user picard, role captains initially maps to
+        // opendistro_security_role_starfleet_captains and opendistro_security_role_starfleet
+        addUserWithPassword("picard", "picard", new String[] { "captains" }, HttpStatus.SC_CREATED);
+        checkWriteAccess(HttpStatus.SC_CREATED, "picard", "picard", "sf", "_doc", 1);
+        // TODO: only one doctype allowed for ES6
+        //checkWriteAccess(HttpStatus.SC_CREATED, "picard", "picard", "sf", "_doc", 1);
+
+        verifyGetForSuperAdmin(new Header[]{restApiAdminHeader});
+        verifyDeleteForSuperAdmin(new Header[]{restApiAdminHeader}, false);
+        verifyPutForSuperAdmin(new Header[]{restApiAdminHeader});
+        verifyPatchForSuperAdmin(new Header[]{restApiAdminHeader});
+        // mapping with several backend roles, one of the is captain
+        deleteAndputNewMapping(new Header[]{restApiAdminHeader}, "rolesmapping_backendroles_captains_list.json", false);
+        checkAllSfAllowed();
+
+        // mapping with one backend role, captain
+        deleteAndputNewMapping(new Header[]{restApiAdminHeader},"rolesmapping_backendroles_captains_single.json", true);
+        checkAllSfAllowed();
+
+        // mapping with several users, one is picard
+        deleteAndputNewMapping(new Header[]{restApiAdminHeader},"rolesmapping_users_picard_list.json", true);
+        checkAllSfAllowed();
+
+        // just user picard
+        deleteAndputNewMapping(new Header[]{restApiAdminHeader},"rolesmapping_users_picard_single.json", true);
+        checkAllSfAllowed();
+
+        // hosts
+        deleteAndputNewMapping(new Header[]{restApiAdminHeader},"rolesmapping_hosts_list.json", true);
+        checkAllSfAllowed();
+
+        // hosts
+        deleteAndputNewMapping(new Header[]{restApiAdminHeader},"rolesmapping_hosts_single.json", true);
+        checkAllSfAllowed();
+
+        // full settings, access
+        deleteAndputNewMapping(new Header[]{restApiAdminHeader},"rolesmapping_all_access.json", true);
+        checkAllSfAllowed();
+
+        // full settings, no access
+        deleteAndputNewMapping(new Header[]{restApiAdminHeader},"rolesmapping_all_noaccess.json", true);
+        checkAllSfForbidden();
+
+    }
+
+    void verifyGetForSuperAdmin(final Header[] header) throws Exception {
         // check rolesmapping exists, old config api
-        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/rolesmapping");
+        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/rolesmapping", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // check rolesmapping exists, new API
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping");
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getContentType(), response.isJsonContentType());
 
@@ -61,9 +167,8 @@ public void testRolesMappingApi() throws Exception {
 
 
         // -- GET
-
         // GET opendistro_security_role_starfleet, exists
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getContentType(), response.isJsonContentType());
         Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
@@ -73,55 +178,42 @@ public void testRolesMappingApi() throws Exception {
         Assert.assertEquals("nagilum", settings.getAsList("opendistro_security_role_starfleet.users").get(0));
 
         // GET, rolesmapping does not exist
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/nothinghthere", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/nothinghthere", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // GET, new URL endpoint in security
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getContentType(), response.isJsonContentType());
 
         // GET, new URL endpoint in security
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getContentType(), response.isJsonContentType());
 
         // Super admin should be able to describe particular hidden rolemapping
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("\"hidden\":true"));
+    }
 
-        // create index
-        setupStarfleetIndex();
-
-        // add user picard, role captains initially maps to
-        // opendistro_security_role_starfleet_captains and opendistro_security_role_starfleet
-        addUserWithPassword("picard", "picard", new String[] { "captains" }, HttpStatus.SC_CREATED);
-        checkWriteAccess(HttpStatus.SC_CREATED, "picard", "picard", "sf", "_doc", 1);
-
-        // TODO: only one doctype allowed for ES6
-        //checkWriteAccess(HttpStatus.SC_CREATED, "picard", "picard", "sf", "_doc", 1);
-
-        // --- DELETE
-
-        rh.sendAdminCertificate = true;
-
+    void verifyDeleteForSuperAdmin(final Header[] header, final boolean useAdminCert) throws Exception {
         // Non-existing role
-        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/idonotexist", new Header[0]);
+        HttpResponse response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/idonotexist", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // read only role
         // SuperAdmin can delete read only role
-        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // hidden role
-        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("'opendistro_security_internal' deleted."));
 
         // remove complete role mapping for opendistro_security_role_starfleet_captains
-        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         response = rh.executeGetRequest(ENDPOINT + "/configuration/rolesmapping");
         rh.sendAdminCertificate = false;
@@ -134,32 +226,30 @@ public void testRolesMappingApi() throws Exception {
         // checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 1);
 
         // remove also opendistro_security_role_starfleet, poor picard has no mapping left
-        rh.sendAdminCertificate = true;
-        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet", new Header[0]);
+        rh.sendAdminCertificate = useAdminCert;
+        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         rh.sendAdminCertificate = false;
         checkAllSfForbidden();
+    }
 
-        rh.sendAdminCertificate = true;
-
-        // --- PUT
-
+    void verifyPutForSuperAdmin(final Header[] header) throws Exception {
         // put with empty mapping, must fail
-        response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", "", new Header[0]);
+        HttpResponse response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", "", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
-        settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.PAYLOAD_MANDATORY.getMessage(), settings.get("reason"));
 
         // put new configuration with invalid payload, must fail
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains",
-                        FileHelper.loadFile("restapi/rolesmapping_not_parseable.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_not_parseable.json"), header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.BODY_NOT_PARSEABLE.getMessage(), settings.get("reason"));
 
         // put new configuration with invalid keys, must fail
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains",
-                        FileHelper.loadFile("restapi/rolesmapping_invalid_keys.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_invalid_keys.json"), header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.INVALID_CONFIGURATION.getMessage(), settings.get("reason"));
@@ -170,7 +260,7 @@ public void testRolesMappingApi() throws Exception {
 
         // wrong datatypes
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains",
-                        FileHelper.loadFile("restapi/rolesmapping_backendroles_captains_single_wrong_datatype.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_backendroles_captains_single_wrong_datatype.json"), header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason"));
@@ -179,7 +269,7 @@ public void testRolesMappingApi() throws Exception {
         Assert.assertTrue(settings.get("users") == null);
 
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains",
-                        FileHelper.loadFile("restapi/rolesmapping_hosts_single_wrong_datatype.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_hosts_single_wrong_datatype.json"), header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason"));
@@ -188,7 +278,7 @@ public void testRolesMappingApi() throws Exception {
         Assert.assertTrue(settings.get("users") == null);
 
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains",
-                        FileHelper.loadFile("restapi/rolesmapping_users_picard_single_wrong_datatype.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_users_picard_single_wrong_datatype.json"), header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason"));
@@ -199,81 +289,83 @@ public void testRolesMappingApi() throws Exception {
         // Read only role mapping
         // SuperAdmin can add read only roles - mappings
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library",
-                FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_all_access.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
 
         // hidden role, allowed for super admin
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal",
-                FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_all_access.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
 
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains",
-                FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_all_access.json"), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
+    }
 
-        // -- PATCH
+    void verifyPatchForSuperAdmin(final Header[] header) throws Exception {
         // PATCH on non-existing resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        HttpResponse response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // PATCH read only resource, must be forbidden
         // SuperAdmin can patch read-only resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\"] }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library",
+                "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\"] }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH hidden resource, must be not found, can be found by super admin
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ " +
-                "\"foo\", \"bar\" ] }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal",
+                "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ " +
+                "\"foo\", \"bar\" ] }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH value of hidden flag, must fail with validation error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans",
+                "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
         // PATCH
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", "[{ \"op\": \"add\", \"path\": \"/backend_roles/-\", \"value\": \"spring\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans",
+                "[{ \"op\": \"add\", \"path\": \"/backend_roles/-\", \"value\": \"spring\" }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         List<String> permissions = settings.getAsList("opendistro_security_role_vulcans.backend_roles");
         Assert.assertNotNull(permissions);
         Assert.assertTrue(permissions.contains("spring"));
 
         // -- PATCH on whole config resource
         // PATCH on non-existing resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/imnothere/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping",
+                "[{ \"op\": \"add\", \"path\": \"/imnothere/a\", \"value\": [ \"foo\", \"bar\" ] }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH read only resource, must be forbidden
         // SuperAdmin can patch read only resource
         rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_starfleet_library/description\", \"value\": \"foo\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping",
+                "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_starfleet_library/description\", \"value\": \"foo\" }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // PATCH hidden resource, must be bad request
         rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/a\", \"value\": [ \"foo\", \"bar\" ] }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH value of hidden flag, must fail with validation error
         rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_vulcans/hidden\", \"value\": true }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping",
+                "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_vulcans/hidden\", \"value\": true }]", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
         // PATCH
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": {  \"backend_roles\":[\"vulcanadmin\"]} }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping",
+                "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": {  \"backend_roles\":[\"vulcanadmin\"]} }]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/bulknew1", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/bulknew1", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         permissions = settings.getAsList("bulknew1.backend_roles");
@@ -281,45 +373,10 @@ public void testRolesMappingApi() throws Exception {
         Assert.assertTrue(permissions.contains("vulcanadmin"));
 
         // PATCH delete
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/bulknew1", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/bulknew1", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
-
-
-        // mapping with several backend roles, one of the is captain
-        deleteAndputNewMapping("rolesmapping_backendroles_captains_list.json");
-        checkAllSfAllowed();
-
-        // mapping with one backend role, captain
-        deleteAndputNewMapping("rolesmapping_backendroles_captains_single.json");
-        checkAllSfAllowed();
-
-        // mapping with several users, one is picard
-        deleteAndputNewMapping("rolesmapping_users_picard_list.json");
-        checkAllSfAllowed();
-
-        // just user picard
-        deleteAndputNewMapping("rolesmapping_users_picard_single.json");
-        checkAllSfAllowed();
-
-        // hosts
-        deleteAndputNewMapping("rolesmapping_hosts_list.json");
-        checkAllSfAllowed();
-
-        // hosts
-        deleteAndputNewMapping("rolesmapping_hosts_single.json");
-        checkAllSfAllowed();
-
-        // full settings, access
-        deleteAndputNewMapping("rolesmapping_all_access.json");
-        checkAllSfAllowed();
-
-        // full settings, no access
-        deleteAndputNewMapping("rolesmapping_all_noaccess.json");
-        checkAllSfForbidden();
-
     }
 
     private void checkAllSfAllowed() throws Exception {
@@ -334,13 +391,13 @@ private void checkAllSfForbidden() throws Exception {
         checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 1);
     }
 
-    private HttpResponse deleteAndputNewMapping(String fileName) throws Exception {
-        rh.sendAdminCertificate = true;
+    private HttpResponse deleteAndputNewMapping(final Header[] header, final String fileName, final boolean useAdminCert) throws Exception {
+        rh.sendAdminCertificate = useAdminCert;
         HttpResponse response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains",
-                        new Header[0]);
+                        header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains",
-                        FileHelper.loadFile("restapi/"+fileName), new Header[0]);
+                        FileHelper.loadFile("restapi/"+fileName), header);
         Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
         rh.sendAdminCertificate = false;
         return response;
@@ -355,46 +412,142 @@ public void testRolesMappingApiForNonSuperAdmin() throws Exception {
         rh.sendAdminCertificate = false;
         rh.sendHTTPClientCredentials = true;
 
+        verifyNonSuperAdminUser(new Header[0]);
+    }
+
+    @Test
+    public void testRolesMappingApiForNonSuperAdminRestApiUser() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+        final Header restApiHeader = encodeBasicHeader("test", "test");
+        verifyNonSuperAdminUser(new Header[] {restApiHeader});
+    }
+
+    void verifyNonSuperAdminUser(final Header[] header) throws Exception {
         HttpResponse response;
 
         // Delete read only roles mapping
-        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library" , new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library" , header);
         Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
 
         // Put read only roles mapping
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library",
-                        FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_all_access.json"), header);
         Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
 
         // Patch single read only roles mapping
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", header);
         Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
 
         // Patch multiple read only roles mapping
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_starfleet_library/description\", \"value\": \"foo\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_starfleet_library/description\", \"value\": \"foo\" }]", header);
         Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
 
         // GET, rolesmapping is hidden, allowed for super admin
-        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // Delete hidden roles mapping
-        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal" , new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal" , header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // Put hidden roles mapping
         response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal",
-                        FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]);
+                FileHelper.loadFile("restapi/rolesmapping_all_access.json"), header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // Patch hidden roles mapping
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // Patch multiple hidden roles mapping
-        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/description\", \"value\": \"foo\" }]", new Header[0]);
-        Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
+        response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/description\", \"value\": \"foo\" }]", header);
+        Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());    }
+
+    @Test
+    public void testChangeRestApiAdminRoleMappingForbiddenForNonSuperAdmin() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        final Header adminHeader = encodeBasicHeader("admin", "admin");
+        final Header restApiHeader = encodeBasicHeader("test", "test");
+
+        HttpResponse response = rh.executePutRequest(
+                ENDPOINT + "/roles/new_rest_api_role",
+                createRestAdminPermissionsPayload(), restApiAdminHeader);
+        Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
+        response = rh.executePutRequest(
+                ENDPOINT + "/roles/new_rest_api_role_without_mapping",
+                createRestAdminPermissionsPayload(), restApiAdminHeader);
+        Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
+        response = rh.executePutRequest(
+                ENDPOINT + "/rolesmapping/new_rest_api_role",
+                createUsersPayload("a", "b", "c"), restApiAdminHeader);
+        Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode());
+
+        verifyRestApiPutAndDeleteForNonRestApiAdmin(adminHeader);
+        verifyRestApiPutAndDeleteForNonRestApiAdmin(restApiHeader);
+        verifyRestApiPatchForNonRestApiAdmin(adminHeader, false);
+        verifyRestApiPatchForNonRestApiAdmin(restApiHeader, false);
+        verifyRestApiPatchForNonRestApiAdmin(adminHeader, true);
+        verifyRestApiPatchForNonRestApiAdmin(restApiHeader, true);
+    }
+
+    private void verifyRestApiPutAndDeleteForNonRestApiAdmin(final Header header) throws Exception {
+        HttpResponse response = rh.executePutRequest(
+                ENDPOINT + "/rolesmapping/new_rest_api_role", createUsersPayload("a", "b", "c"), header);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        response = rh.executeDeleteRequest(
+                ENDPOINT + "/rolesmapping/new_rest_api_role", "", header);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+    }
+
+    private void verifyRestApiPatchForNonRestApiAdmin(final Header header, boolean bulk) throws Exception {
+        String path = ENDPOINT + "/rolesmapping";
+        if (!bulk) {
+            path += "/new_rest_api_role";
+        }
+        HttpResponse response = rh.executePatchRequest(path, createPathPayload("add"), header);
+        System.err.println(response.getBody());
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        response = rh.executePatchRequest(path, createPathPayload("replace"), header);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        response = rh.executePatchRequest(path, createPathPayload("remove"), header);
+        Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+    }
+
+    private ObjectNode createUsersObjectNode(final String... users) {
+        final ArrayNode usersArray = DefaultObjectMapper.objectMapper.createArrayNode();
+        for (final String user : users) {
+            usersArray.add(user);
+        }
+        return DefaultObjectMapper.objectMapper.createObjectNode().set("users", usersArray);
+    }
+
+    private String createUsersPayload(final String... users) throws JsonProcessingException {
+        return DefaultObjectMapper.objectMapper.writeValueAsString(createUsersObjectNode(users));
+    }
 
+    private String createPathPayload(final String op) throws JsonProcessingException {
+        final ArrayNode arrayNode = DefaultObjectMapper.objectMapper.createArrayNode();
+        final ObjectNode opNode = DefaultObjectMapper.objectMapper.createObjectNode();
+        opNode.put("op", op);
+        if ("add".equals(op)) {
+            opNode.put("path", "/new_rest_api_role_without_mapping");
+            opNode.set("value", createUsersObjectNode("d", "e", "f"));
+        }
+        if ("replace".equals(op)) {
+            opNode.put("path", "/new_rest_api_role");
+            opNode.set("value", createUsersObjectNode("g", "h", "i"));
+        }
+        if ("remove".equals(op)) {
+            opNode.put("path", "/new_rest_api_role");
+        }
+        return DefaultObjectMapper.objectMapper.writeValueAsString(arrayNode.add(opNode));
     }
 
     @Test
diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/SslCertsApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/SslCertsApiTest.java
new file mode 100644
index 0000000000..1054b51aea
--- /dev/null
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/SslCertsApiTest.java
@@ -0,0 +1,174 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ *
+ * Modifications Copyright OpenSearch Contributors. See
+ * GitHub history for details.
+ */
+
+package org.opensearch.security.dlic.rest.api;
+
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpStatus;
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.opensearch.common.settings.Settings;
+import org.opensearch.security.DefaultObjectMapper;
+import org.opensearch.security.support.ConfigConstants;
+import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse;
+
+import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX;
+
+public class SslCertsApiTest extends AbstractRestApiUnitTest {
+
+    static final String HTTP_CERTS = "http";
+
+    static final String TRANSPORT_CERTS = "transport";
+
+    private final static List<Map<String, String>> EXPECTED_CERTIFICATES =
+            ImmutableList.of(
+                    ImmutableMap.of(
+                            "issuer_dn", "CN=Example Com Inc. Signing CA,OU=Example Com Inc. Signing CA,O=Example Com Inc.,DC=example,DC=com",
+                            "subject_dn", "CN=node-0.example.com,OU=SSL,O=Test,L=Test,C=DE",
+                            "san", "[[2, node-0.example.com], [2, localhost], [7, 127.0.0.1], [8, 1.2.3.4.5.5]]",
+                            "not_before", "2018-05-05T14:37:09Z",
+                            "not_after", "2028-05-02T14:37:09Z"
+                    ),
+                    ImmutableMap.of(
+                            "issuer_dn", "CN=Example Com Inc. Root CA,OU=Example Com Inc. Root CA,O=Example Com Inc.,DC=example,DC=com",
+                            "subject_dn", "CN=Example Com Inc. Signing CA,OU=Example Com Inc. Signing CA,O=Example Com Inc.,DC=example,DC=com",
+                            "san", "",
+                            "not_before", "2018-05-05T14:37:08Z",
+                            "not_after", "2028-05-04T14:37:08Z"
+                    )
+            );
+
+    private final static String EXPECTED_CERTIFICATES_BY_TYPE;
+    static {
+        try {
+            EXPECTED_CERTIFICATES_BY_TYPE = DefaultObjectMapper.objectMapper.writeValueAsString(
+                    ImmutableMap.of(
+                            "http_certificates_list", EXPECTED_CERTIFICATES,
+                            "transport_certificates_list", EXPECTED_CERTIFICATES
+                    )
+            );
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+    private final Header restApiCertsInfoAdminHeader = encodeBasicHeader("rest_api_admin_ssl_info", "rest_api_admin_ssl_info");
+
+    private final Header restApiReloadCertsAdminHeader = encodeBasicHeader("rest_api_admin_ssl_reloadcerts", "rest_api_admin_ssl_reloadcerts");
+
+    private final Header restApiHeader = encodeBasicHeader("test", "test");
+
+
+    String certsInfoEndpoint() {
+        return PLUGINS_PREFIX + "/api/ssl/certs";
+    }
+
+    String certsReloadEndpoint(final String certType) {
+        return String.format("%s/api/ssl/%s/reloadcerts", PLUGINS_PREFIX, certType);
+    }
+
+    @Test
+    public void testCertsInfo() throws Exception {
+        setupWithRestRoles();
+        final Header adminCredsHeader = encodeBasicHeader("admin", "admin");
+        // No creds, no admin certificate - UNAUTHORIZED
+        rh.sendAdminCertificate = false;
+        HttpResponse response = rh.executeGetRequest(certsInfoEndpoint());
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_UNAUTHORIZED, response.getStatusCode());
+
+        rh.sendAdminCertificate = false;
+        response = rh.executeGetRequest(certsInfoEndpoint(), adminCredsHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        sendAdminCert();
+        response = rh.executeGetRequest(certsInfoEndpoint());
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode());
+        Assert.assertEquals(EXPECTED_CERTIFICATES_BY_TYPE, response.getBody());
+
+        rh.sendAdminCertificate = false;
+        Assert.assertEquals(EXPECTED_CERTIFICATES_BY_TYPE, loadCerts(restApiAdminHeader));
+        Assert.assertEquals(EXPECTED_CERTIFICATES_BY_TYPE, loadCerts(restApiCertsInfoAdminHeader));
+
+        response = rh.executeGetRequest(certsInfoEndpoint(), restApiHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+    }
+
+    private String loadCerts(final Header... header) throws Exception {
+        HttpResponse response = rh.executeGetRequest(certsInfoEndpoint(), restApiAdminHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode());
+        return response.getBody();
+    }
+
+    @Test
+    public void testReloadCertsNotAvailableByDefault() throws Exception {
+        setupWithRestRoles();
+
+        sendAdminCert();
+        verifyReloadCertsNotAvailable();
+
+        rh.sendAdminCertificate = false;
+        verifyReloadCertsNotAvailable(restApiAdminHeader);
+        verifyReloadCertsNotAvailable(restApiReloadCertsAdminHeader);
+
+        HttpResponse response = rh.executePutRequest(certsReloadEndpoint(HTTP_CERTS), "{}", restApiHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+        response = rh.executePutRequest(certsReloadEndpoint(TRANSPORT_CERTS), "{}", restApiHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+    }
+
+    private void verifyReloadCertsNotAvailable(final Header... header) {
+        HttpResponse response = rh.executePutRequest(certsReloadEndpoint(HTTP_CERTS), "{}", header);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
+        response = rh.executePutRequest(certsReloadEndpoint(TRANSPORT_CERTS), "{}", header);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
+    }
+
+    @Test
+    public void testReloadCertsWrongCertsType() throws Exception {
+        setupWithRestRoles(reloadEnabled());
+        sendAdminCert();
+        HttpResponse response = rh.executePutRequest(certsReloadEndpoint("aaaaa"), "{}");
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+        rh.sendAdminCertificate = false;
+        response = rh.executePutRequest(certsReloadEndpoint("bbbb"), "{}", restApiAdminHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+        response = rh.executePutRequest(certsReloadEndpoint("cccc"), "{}", restApiReloadCertsAdminHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode());
+
+    }
+
+    @Test
+    public void testReloadCerts() throws Exception {
+        setupWithRestRoles(reloadEnabled());
+    }
+
+
+    private void sendAdminCert() {
+        rh.keystore = "restapi/kirk-keystore.jks";
+        rh.sendAdminCertificate = true;
+    }
+
+    Settings reloadEnabled() {
+        return Settings.builder()
+                .put(ConfigConstants.SECURITY_SSL_CERT_RELOAD_ENABLED, true)
+                .build();
+    }
+
+}
diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java
index f100b4ba7f..88eab72766 100644
--- a/src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java
+++ b/src/test/java/org/opensearch/security/dlic/rest/api/UserApiTest.java
@@ -58,7 +58,7 @@ public void testSecurityRoles() throws Exception {
                 .executeGetRequest(ENDPOINT + "/" + CType.INTERNALUSERS.toLCString());
         Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode());
         Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
-        Assert.assertEquals(56, settings.size());
+        Assert.assertEquals(133, settings.size());
         response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/newuser\", \"value\": {\"password\": \"newuser\", \"opendistro_security_roles\": [\"opendistro_security_all_access\"] } }]", new Header[0]);
         Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode());
 
@@ -104,50 +104,57 @@ public void testUserApi() throws Exception {
         rh.keystore = "restapi/kirk-keystore.jks";
         rh.sendAdminCertificate = true;
 
-        // initial configuration, 6 users
-        HttpResponse response = rh
-                .executeGetRequest(ENDPOINT + "/" + CType.INTERNALUSERS.toLCString());
+        // initial configuration
+        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/" + CType.INTERNALUSERS.toLCString());
         Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode());
         Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
-        Assert.assertEquals(56, settings.size());
-        // --- GET
+        Assert.assertEquals(133, settings.size());
+        verifyGet();
+        verifyPut();
+        verifyPatch(true);
+        // create index first
+        setupStarfleetIndex();
+        verifyRoles(true);
+    }
 
+    private void verifyGet(final Header... header) throws Exception {
+        // --- GET
         // GET, user admin, exists
-        response = rh.executeGetRequest(ENDPOINT + "/internalusers/admin", new Header[0]);
+        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/internalusers/admin", header);
         Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode());
-        settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(7, settings.size());
         // hash must be filtered
         Assert.assertEquals("", settings.get("admin.hash"));
 
         // GET, user does not exist
-        response = rh.executeGetRequest(ENDPOINT + "/internalusers/nothinghthere", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/internalusers/nothinghthere", header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // GET, new URL endpoint in security
-        response = rh.executeGetRequest(ENDPOINT + "/user/", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/user/", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // GET, new URL endpoint in security
-        response = rh.executeGetRequest(ENDPOINT + "/user", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/user", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
+    }
 
+    private void verifyPut(final Header... header) throws Exception {
         // -- PUT
-
         // no username given
-        response = rh.executePutRequest(ENDPOINT + "/internalusers/", "{\"hash\": \"123\"}", new Header[0]);
+        HttpResponse response = rh.executePutRequest(ENDPOINT + "/internalusers/", "{\"hash\": \"123\"}", header);
         Assert.assertEquals(HttpStatus.SC_METHOD_NOT_ALLOWED, response.getStatusCode());
 
         // Faulty JSON payload
         response = rh.executePutRequest(ENDPOINT + "/internalusers/nagilum", "{some: \"thing\" asd  other: \"thing\"}",
-                new Header[0]);
+                header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
-        settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(settings.get("reason"), AbstractConfigurationValidator.ErrorType.BODY_NOT_PARSEABLE.getMessage());
 
         // Missing quotes in JSON - parseable in 6.x, but wrong config keys
-        response = rh.executePutRequest(ENDPOINT + "/internalusers/nagilum", "{some: \"thing\", other: \"thing\"}",
-                new Header[0]);
+        response = rh.executePutRequest(ENDPOINT + "/internalusers/nagilum", "{some: \"thing\", other: \"thing\"}", header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         //JK: this should be "Could not parse content of request." because JSON is truly invalid
@@ -156,102 +163,105 @@ public void testUserApi() throws Exception {
         //Assert.assertTrue(settings.get(AbstractConfigurationValidator.INVALID_KEYS_KEY + ".keys").contains("other"));
 
         // Get hidden role
-        response = rh.executeGetRequest(ENDPOINT + "/internalusers/hide" , new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/internalusers/hide" , header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("\"hidden\":true"));
 
         // Associating with hidden role is allowed (for superadmin)
         response = rh.executePutRequest(ENDPOINT + "/internalusers/test", "{ \"opendistro_security_roles\": " +
-                "[\"opendistro_security_hidden\"]}", new Header[0]);
+                "[\"opendistro_security_hidden\"]}", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // Associating with reserved role is allowed (for superadmin)
         response = rh.executePutRequest(ENDPOINT + "/internalusers/test", "{ \"opendistro_security_roles\": [\"opendistro_security_reserved\"], " +
-                "\"hash\": \"123\"}",
-            new Header[0]);
+                        "\"hash\": \"123\"}",
+                header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // Associating with non-existent role is not allowed
         response = rh.executePutRequest(ENDPOINT + "/internalusers/nagilum", "{ \"opendistro_security_roles\": [\"non_existent\"]}",
-            new Header[0]);
+                header);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(settings.get("message"), "Role 'non_existent' is not available for role-mapping.");
 
         // Wrong config keys
         response = rh.executePutRequest(ENDPOINT + "/internalusers/nagilum", "{\"some\": \"thing\", \"other\": \"thing\"}",
-                new Header[0]);
+                header);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(settings.get("reason"), AbstractConfigurationValidator.ErrorType.INVALID_CONFIGURATION.getMessage());
         Assert.assertTrue(settings.get(AbstractConfigurationValidator.INVALID_KEYS_KEY + ".keys").contains("some"));
         Assert.assertTrue(settings.get(AbstractConfigurationValidator.INVALID_KEYS_KEY + ".keys").contains("other"));
+    }
 
+    private void verifyPatch(final boolean sendAdminCert, Header... restAdminHeader) throws Exception {
         // -- PATCH
         // PATCH on non-existing resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        HttpResponse response = rh.executePatchRequest(ENDPOINT + "/internalusers/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // PATCH read only resource, must be forbidden,
         // but SuperAdmin can PATCH read-only resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers/sarek", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers/sarek", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // PATCH hidden resource, must be not found, can be found for super admin
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers/q", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers/q", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH value of hidden flag, must fail with validation error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers/test", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers/test", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
         // PATCH password
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers/test", "[{ \"op\": \"add\", \"path\": \"/password\", \"value\": \"neu\" }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers/test", "[{ \"op\": \"add\", \"path\": \"/password\", \"value\": \"neu\" }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT + "/internalusers/test", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/internalusers/test", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertFalse(settings.hasValue("test.password"));
         Assert.assertTrue(settings.hasValue("test.hash"));
 
         // -- PATCH on whole config resource
         // PATCH on non-existing resource
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/imnothere/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/imnothere/a\", \"value\": [ \"foo\", \"bar\" ] }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH read only resource, must be forbidden,
         // but SuperAdmin can PATCH read only resouce
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/sarek/description\", \"value\": \"foo\" }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/sarek/description\", \"value\": \"foo\" }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         rh.sendAdminCertificate = false;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/sarek/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/sarek/a\", \"value\": [ \"foo\", \"bar\" ] }]");
         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusCode());
 
         // PATCH hidden resource, must be bad request
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/q/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/q/a\", \"value\": [ \"foo\", \"bar\" ] }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
 
         // PATCH value of hidden flag, must fail with validation error
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/test/hidden\", \"value\": true }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/test/hidden\", \"value\": true }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*"));
 
         // PATCH
-        rh.sendAdminCertificate = true;
-        response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": {\"password\": \"bla\", \"backend_roles\": [\"vulcan\"] } }]", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePatchRequest(ENDPOINT + "/internalusers",
+                "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": {\"password\": \"bla\", \"backend_roles\": [\"vulcan\"] } }]", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
-        response = rh.executeGetRequest(ENDPOINT + "/internalusers/bulknew1", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/internalusers/bulknew1", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertFalse(settings.hasValue("bulknew1.password"));
@@ -267,17 +277,17 @@ public void testUserApi() throws Exception {
 
         // add/update user, user is read only, forbidden
         // SuperAdmin can add read only users
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
         addUserWithHash("sarek", "$2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m",
                 HttpStatus.SC_OK);
 
         // add/update user, user is hidden, forbidden, allowed for super admin
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
         addUserWithHash("q", "$2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m",
                 HttpStatus.SC_OK);
 
         // add users
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
         addUserWithHash("nagilum", "$2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m",
                 HttpStatus.SC_CREATED);
 
@@ -285,20 +295,20 @@ public void testUserApi() throws Exception {
         checkGeneralAccess(HttpStatus.SC_OK, "nagilum", "nagilum");
 
         // try remove user, no username
-        rh.sendAdminCertificate = true;
-        response = rh.executeDeleteRequest(ENDPOINT + "/internalusers", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executeDeleteRequest(ENDPOINT + "/internalusers", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_METHOD_NOT_ALLOWED, response.getStatusCode());
 
         // try remove user, nonexisting user
-        response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/picard", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/picard", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode());
 
         // try remove readonly user
-        response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/sarek", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/sarek", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
 
         // try remove hidden user, allowed for super admin
-        response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/q", new Header[0]);
+        response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/q", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Assert.assertTrue(response.getBody().contains("'q' deleted."));
         // now really remove user
@@ -309,7 +319,7 @@ public void testUserApi() throws Exception {
         checkGeneralAccess(HttpStatus.SC_UNAUTHORIZED, "nagilum", "nagilum");
 
         // use password instead of hash
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
         addUserWithPassword("nagilum", "correctpassword", HttpStatus.SC_CREATED);
 
         rh.sendAdminCertificate = false;
@@ -319,7 +329,7 @@ public void testUserApi() throws Exception {
         deleteUser("nagilum");
 
         // Check unchanged password functionality
-        rh.sendAdminCertificate = true;
+        rh.sendAdminCertificate = sendAdminCert;
 
         // new user, password or hash is mandatory
         addUserWithoutPasswordOrHash("nagilum", new String[]{"starfleet"}, HttpStatus.SC_BAD_REQUEST);
@@ -329,35 +339,32 @@ public void testUserApi() throws Exception {
         // update user, do not specify hash or password, hash must remain the same
         addUserWithoutPasswordOrHash("nagilum", new String[]{"starfleet"}, HttpStatus.SC_OK);
         // get user, check hash, must be untouched
-        response = rh.executeGetRequest(ENDPOINT + "/internalusers/nagilum", new Header[0]);
+        response = rh.executeGetRequest(ENDPOINT + "/internalusers/nagilum", restAdminHeader);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertTrue(settings.get("nagilum.hash").equals(""));
+    }
 
-
-        // ROLES
-        // create index first
-        setupStarfleetIndex();
-
+    private void verifyRoles(final boolean sendAdminCert, Header... header) throws Exception {
         // wrong datatypes in roles file
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes.json"), new Header[0]);
-        settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        rh.sendAdminCertificate = sendAdminCert;
+        HttpResponse response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes.json"), header);
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason"));
         Assert.assertTrue(settings.get("backend_roles").equals("Array expected"));
         rh.sendAdminCertificate = false;
 
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes.json"), new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes.json"), header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason"));
         Assert.assertTrue(settings.get("backend_roles").equals("Array expected"));
         rh.sendAdminCertificate = false;
 
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes2.json"), new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes2.json"), header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason"));
@@ -365,8 +372,8 @@ public void testUserApi() throws Exception {
         Assert.assertTrue(settings.get("backend_roles") == null);
         rh.sendAdminCertificate = false;
 
-        rh.sendAdminCertificate = true;
-        response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes3.json"), new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes3.json"), header);
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
         Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason"));
@@ -395,12 +402,12 @@ public void testUserApi() throws Exception {
         checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0);
         checkWriteAccess(HttpStatus.SC_CREATED, "picard", "picard", "sf", "_doc", 1);
 
-        rh.sendAdminCertificate = true;
-        response = rh.executeGetRequest(ENDPOINT + "/internalusers/picard", new Header[0]);
+        rh.sendAdminCertificate = sendAdminCert;
+        response = rh.executeGetRequest(ENDPOINT + "/internalusers/picard", header);
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
         Assert.assertEquals("", settings.get("picard.hash"));
-        roles = settings.getAsList("picard.backend_roles");
+        List<String> roles = settings.getAsList("picard.backend_roles");
         Assert.assertNotNull(roles);
         Assert.assertEquals(2, roles.size());
         Assert.assertTrue(roles.contains("starfleet"));
@@ -411,10 +418,46 @@ public void testUserApi() throws Exception {
 
 
         // check tabs in json
-        response = rh.executePutRequest(ENDPOINT + "/internalusers/userwithtabs", "\t{\"hash\": \t \"123\"\t}  ", new Header[0]);
+        response = rh.executePutRequest(ENDPOINT + "/internalusers/userwithtabs", "\t{\"hash\": \t \"123\"\t}  ", header);
         Assert.assertEquals(response.getBody(), HttpStatus.SC_CREATED, response.getStatusCode());
     }
 
+    @Test
+    public void testUserApiWithRestAdminPermissions() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+        final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user");
+        // initial configuration
+        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/" + CType.INTERNALUSERS.toLCString(), restApiAdminHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode());
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        Assert.assertEquals(133, settings.size());
+        verifyGet(restApiAdminHeader);
+        verifyPut(restApiAdminHeader);
+        verifyPatch(false, restApiAdminHeader);
+        // create index first
+        setupStarfleetIndex();
+        verifyRoles(false, restApiAdminHeader);
+    }
+
+    @Test
+    public void testUserApiWithRestInternalUsersAdminPermissions() throws Exception {
+        setupWithRestRoles();
+        rh.sendAdminCertificate = false;
+        final Header restApiInternalUsersAdminHeader = encodeBasicHeader("rest_api_admin_internalusers", "rest_api_admin_internalusers");
+        // initial configuration
+        HttpResponse response = rh.executeGetRequest(ENDPOINT + "/" + CType.INTERNALUSERS.toLCString(), restApiInternalUsersAdminHeader);
+        Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode());
+        Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
+        Assert.assertEquals(133, settings.size());
+        verifyGet(restApiInternalUsersAdminHeader);
+        verifyPut(restApiInternalUsersAdminHeader);
+        verifyPatch(false, restApiInternalUsersAdminHeader);
+        // create index first
+        setupStarfleetIndex();
+        verifyRoles(false, restApiInternalUsersAdminHeader);
+    }
+
     @Test
     public void testPasswordRules() throws Exception {
 
@@ -436,7 +479,7 @@ public void testPasswordRules() throws Exception {
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         System.out.println(response.getBody());
         Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
-        Assert.assertEquals(56, settings.size());
+        Assert.assertEquals(133, settings.size());
 
         addUserWithPassword("tooshoort", "", HttpStatus.SC_BAD_REQUEST);
         addUserWithPassword("tooshoort", "123", HttpStatus.SC_BAD_REQUEST);
@@ -516,7 +559,7 @@ public void testUserApiWithDots() throws Exception {
                 .executeGetRequest(ENDPOINT + "/" + CType.INTERNALUSERS.toLCString());
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode());
         Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build();
-        Assert.assertEquals(56, settings.size());
+        Assert.assertEquals(133, settings.size());
 
         addUserWithPassword(".my.dotuser0", "$2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m",
                 HttpStatus.SC_CREATED);
diff --git a/src/test/java/org/opensearch/security/ssl/SecuritySSLReloadCertsActionTests.java b/src/test/java/org/opensearch/security/ssl/SecuritySSLReloadCertsActionTests.java
index ea78ee043c..f7684a9c51 100644
--- a/src/test/java/org/opensearch/security/ssl/SecuritySSLReloadCertsActionTests.java
+++ b/src/test/java/org/opensearch/security/ssl/SecuritySSLReloadCertsActionTests.java
@@ -127,32 +127,6 @@ public void testReloadHttpSSLCertsPass() throws Exception {
         assertReloadCertificateSuccess(rh, "http", getUpdatedCertDetailsExpectedResponse("http"));
     }
 
-    @Test
-    public void testReloadHttpSSLCerts_FailWrongUri() throws Exception {
-        initClusterWithTestCerts();
-        RestHelper rh = getRestHelperAdminUser();
-
-        RestHelper.HttpResponse reloadCertsResponse = rh.executePutRequest("_opendistro/_security/api/ssl/wrong/reloadcerts", null);
-        JSONObject expectedResponse = new JSONObject();
-        // Note: toString and toJSONString replace / with \/. This helps get rid of the additional \ character.
-        expectedResponse.put("message", "invalid uri path, please use /_opendistro/_security/api/ssl/http/reload or /_opendistro/_security/api/ssl/transport/reload");
-        final String expectedResponseString = expectedResponse.toString().replace("\\", "");
-        Assert.assertEquals(expectedResponseString, reloadCertsResponse.getBody());
-    }
-
-
-    @Test
-    public void testSSLReloadFail_UnAuthorizedUser() throws Exception {
-        initClusterWithTestCerts();
-        // Test endpoint for non-admin user
-        RestHelper rh = getRestHelperNonAdminUser();
-
-        final RestHelper.HttpResponse reloadCertsResponse = rh.executePutRequest(RELOAD_TRANSPORT_CERTS_ENDPOINT, null);
-        Assert.assertEquals(401, reloadCertsResponse.getStatusCode());
-        Assert.assertEquals("Unauthorized", reloadCertsResponse.getStatusReason());
-    }
-
-
     @Test
     public void testSSLReloadFail_InvalidDNAndDate() throws Exception {
         initClusterWithTestCerts();
@@ -169,24 +143,6 @@ public void testSSLReloadFail_InvalidDNAndDate() throws Exception {
         Assert.assertEquals(expectedResponse.toString(), reloadCertsResponse.getBody());
     }
 
-    @Test
-    public void testSSLReloadFail_NoReloadSet() throws Exception {
-        updateFiles(defaultCertFilePath, pemCertFilePath);
-        updateFiles(defaultKeyFilePath, pemKeyFilePath);
-        // This is when SSLCertReload property is set to false
-        initTestCluster(pemCertFilePath, pemKeyFilePath, pemCertFilePath, pemKeyFilePath, false);
-
-        RestHelper rh = getRestHelperAdminUser();
-
-        final RestHelper.HttpResponse reloadCertsResponse = rh.executePutRequest(RELOAD_TRANSPORT_CERTS_ENDPOINT, null);
-        Assert.assertEquals(400, reloadCertsResponse.getStatusCode());
-        JSONObject expectedResponse = new JSONObject();
-        expectedResponse.appendField("error", "no handler found for uri [/_opendistro/_security/api/ssl/transport/reloadcerts] and method [PUT]");
-        // Note: toString and toJSONString replace / with \/. This helps get rid of the additional \ character.
-        final String expectedResponseString = expectedResponse.toString().replace("\\", "");
-        Assert.assertEquals(expectedResponseString, reloadCertsResponse.getBody());
-    }
-
     @Test
     public void testReloadTransportSSLSameCertsPass() throws Exception {
         initClusterWithTestCerts();
diff --git a/src/test/resources/restapi/internal_users.yml b/src/test/resources/restapi/internal_users.yml
index 0049ab8c86..658d3f3aa1 100644
--- a/src/test/resources/restapi/internal_users.yml
+++ b/src/test/resources/restapi/internal_users.yml
@@ -61,3 +61,69 @@ admin_all_access:
     - "vulcan"
   attributes: {}
   description: "sample user with all_access, used to test whitelisting"
+rest_api_admin_user:
+  hash: "$2y$12$X5ZamIheHYc2bihGTbK66Oe1.1vJ19akH0OFGF7TvI2BhbbED.KcO"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API Full Access admin user"
+rest_api_admin_actiongroups:
+  hash: "$2y$12$XE9zXOgyYBBxnzoWMowIsO1w6o6oIc5w3vgwYjdEE44m9MxFZEuR."
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API Action groups admin user"
+rest_api_admin_allowlist:
+  hash: "$2y$12$W5AdCO/j08KiDu7EF/1Zf.nkcQM/7s.TtAdN2pRpbDM31xXcIIJUq"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API Allow list admin user"
+rest_api_admin_audit:
+  hash: "$2y$12$UEbBqz9S6xuEefbK3LDvge5MwX4V1GvJYzUP8M24ItkfXMXg/NSh6"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API Audit admin user"
+rest_api_admin_internalusers:
+  hash: "$2y$12$pUn1a6jdIeR.stkvEqNe5uK3rOY7Dj3uQfE8Cvd2bjNjTQ2HbsBMK"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API Internal users admin user"
+rest_api_admin_nodesdn:
+  hash: "$2y$12$xFUIepz0vILRMzMkZMGY1Ow1P1eJo8TJ2oGiaFXaenGrOMsmDnKZS"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API NodesDN admin user"
+rest_api_admin_roles:
+  hash: "$2y$12$BR.CBsElNLj8v2dzpHJ7bOKVLwWKWjKDhlEvBIvAe9b6/m0xWy2Bq"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API Roles admin user"
+rest_api_admin_rolesmapping:
+  hash: "$2y$12$WQb7PsnRRr04zxjuZsDwU.F7QEr7W0f/rJLjUNLf50hpoJuTqqnaS"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API Roles Mapping admin user"
+rest_api_admin_ssl_info:
+  hash: "$2y$12$irI4k0eKE8z9OXEd1jO4eeQfPV8WRMfttzutAhEeRBWy5XNXOlpr."
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API SSL Certs admin user"
+rest_api_admin_ssl_reloadcerts:
+  hash: "$2y$12$DxNdaBBMvTq5wO5XlnwlTeGSaC7yNoFoJt2N5TVtraopxPnGjMol2"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API SSL Reload Certs admin user"
+rest_api_admin_tenants:
+  hash: "$2y$12$q05T7m7DFtkLLj.MVJ6jjuZkAywG4ZwaNi9fiYn6XCJelN2TUXCy2"
+  reserved: false
+  hidden: false
+  backend_roles: []
+  description: "REST API Tenats admin user"
diff --git a/src/test/resources/restapi/roles.yml b/src/test/resources/restapi/roles.yml
index d82382e4f6..6deb194e9b 100644
--- a/src/test/resources/restapi/roles.yml
+++ b/src/test/resources/restapi/roles.yml
@@ -393,3 +393,51 @@ opendistro_security_role_starfleet_captains:
     allowed_actions:
     - "CRUD_UT"
   tenant_permissions: []
+rest_api_admin_full_access:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/actiongroups'
+    - 'restapi:admin/allowlist'
+    - 'restapi:admin/internalusers'
+    - 'restapi:admin/nodesdn'
+    - 'restapi:admin/roles'
+    - 'restapi:admin/rolesmapping'
+    - 'restapi:admin/ssl/certs/info'
+    - 'restapi:admin/ssl/certs/reload'
+    - 'restapi:admin/tenants'
+rest_api_admin_actiongroups_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/actiongroups'
+rest_api_admin_allowlist_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/allowlist'
+rest_api_admin_internalusers_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/internalusers'
+rest_api_admin_nodesdn_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/nodesdn'
+rest_api_admin_roles_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/roles'
+rest_api_admin_rolesmapping_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/rolesmapping'
+rest_api_admin_ssl_info_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/ssl/certs/info'
+rest_api_admin_ssl_reloadcerts_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/ssl/certs/reload'
+rest_api_admin_tenants_only:
+  reserved: true
+  cluster_permissions:
+    - 'restapi:admin/tenants'
diff --git a/src/test/resources/restapi/roles_mapping.yml b/src/test/resources/restapi/roles_mapping.yml
index 8c46942854..a87287d5ff 100644
--- a/src/test/resources/restapi/roles_mapping.yml
+++ b/src/test/resources/restapi/roles_mapping.yml
@@ -185,6 +185,16 @@ opendistro_security_test:
   hosts: []
   users:
   - "test"
+  - "rest_api_admin_user"
+  - "rest_api_admin_nodesdn"
+  - "rest_api_admin_allowlist"
+  - "rest_api_admin_roles"
+  - "rest_api_admin_rolesmapping"
+  - "rest_api_admin_actiongroups"
+  - "rest_api_admin_internalusers"
+  - "rest_api_admin_tenants"
+  - "rest_api_admin_ssl_info"
+  - "rest_api_admin_ssl_reloadcerts"
   and_backend_roles: []
   description: "Migrated from v6"
 opendistro_security_role_starfleet_captains:
@@ -206,3 +216,47 @@ opendistro_security_role_host2:
   - "opendistro_security_host_localhost"
   and_backend_roles: []
   description: "Migrated from v6"
+rest_api_admin_full_access:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_user]
+rest_api_admin_actiongroups_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_actiongroups]
+rest_api_admin_allowlist_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_allowlist]
+rest_api_admin_audit_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_audit]
+rest_api_admin_internalusers_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_internalusers]
+rest_api_admin_nodesdn_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_nodesdn]
+rest_api_admin_roles_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_roles]
+rest_api_admin_rolesmapping_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_rolesmapping]
+rest_api_admin_ssl_info_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_ssl_info]
+rest_api_admin_ssl_reloadcerts_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_ssl_reloadcerts]
+rest_api_admin_tenants_only:
+  reserved: false
+  hidden: true
+  users: [rest_api_admin_tenants]
diff --git a/src/test/resources/restapi/roles_tenants.yml b/src/test/resources/restapi/roles_tenants.yml
index 93b510dd16..e9b724e342 100644
--- a/src/test/resources/restapi/roles_tenants.yml
+++ b/src/test/resources/restapi/roles_tenants.yml
@@ -2,3 +2,13 @@
 _meta:
   type: "tenants"
   config_version: 2
+some_admin_tenant:
+  reserved: false
+  description: "Demo tenant for admin user"
+hidden_tenant:
+  reserved: true
+  hidden: true
+  description: "Hidden tenant"
+reserved_tenant:
+  reserved: true
+  description: "Reserved tenant"