From 6d001db87cf630388bdbc3c47ff914959f675a8e Mon Sep 17 00:00:00 2001 From: Andrey Pleskach Date: Mon, 3 Apr 2023 18:39:08 +0200 Subject: [PATCH] Revert "Rest admin permissions (#2411) (#2466)" This reverts commit 076715d7c7684e3cd0624a45f4a3bd233315dce6. --- config/roles.yml | 15 +- .../security/OpenSearchSecurityPlugin.java | 29 +- .../dlic/rest/api/AbstractApiAction.java | 22 +- .../dlic/rest/api/AccountApiAction.java | 7 - .../dlic/rest/api/ActionGroupsApiAction.java | 27 +- .../dlic/rest/api/AllowlistApiAction.java | 7 - .../dlic/rest/api/AuditApiAction.java | 7 - .../rest/api/AuthTokenProcessorAction.java | 8 - .../security/dlic/rest/api/Endpoint.java | 3 +- .../dlic/rest/api/FlushCacheApiAction.java | 8 - .../dlic/rest/api/InternalUsersApiAction.java | 7 - .../dlic/rest/api/MigrateApiAction.java | 7 - .../dlic/rest/api/NodesDnApiAction.java | 7 - .../rest/api/PatchableResourceApiAction.java | 21 +- .../api/RestApiAdminPrivilegesEvaluator.java | 164 ------- .../rest/api/RestApiPrivilegesEvaluator.java | 16 +- .../dlic/rest/api/RolesApiAction.java | 20 - .../dlic/rest/api/RolesMappingApiAction.java | 27 -- .../dlic/rest/api/SecurityConfigAction.java | 7 - .../dlic/rest/api/SecurityRestApiActions.java | 20 +- .../dlic/rest/api/TenantsApiAction.java | 8 - .../dlic/rest/api/ValidateApiAction.java | 7 - .../security/dlic/rest/support/Utils.java | 10 - .../privileges/PrivilegesEvaluator.java | 12 - .../security/securityconf/ConfigModelV6.java | 10 - .../security/securityconf/ConfigModelV7.java | 8 - .../security/securityconf/SecurityRoles.java | 2 - .../ssl/rest/SecuritySSLCertsInfoAction.java | 190 ++++++++ .../rest/SecuritySSLReloadCertsAction.java | 155 ++++++ .../rest/api/AbstractRestApiUnitTest.java | 65 +-- .../dlic/rest/api/ActionGroupsApiTest.java | 273 ++++------- .../dlic/rest/api/AllowlistApiTest.java | 25 +- .../dlic/rest/api/NodesDnApiTest.java | 79 --- .../security/dlic/rest/api/RolesApiTest.java | 450 ++++-------------- .../dlic/rest/api/RolesMappingApiTest.java | 385 +++++---------- .../dlic/rest/api/SslCertsApiTest.java | 174 ------- .../security/dlic/rest/api/UserApiTest.java | 199 +++----- .../api/legacy/LegacySslCertsApiTest.java | 29 -- .../SecurityRolesPermissionsTest.java | 274 ----------- .../ssl/SecuritySSLCertsInfoActionTests.java | 110 +++++ .../SecuritySSLReloadCertsActionTests.java | 44 ++ src/test/resources/restapi/internal_users.yml | 66 --- src/test/resources/restapi/roles.yml | 48 -- src/test/resources/restapi/roles_mapping.yml | 54 --- src/test/resources/restapi/roles_tenants.yml | 10 - 45 files changed, 942 insertions(+), 2174 deletions(-) delete mode 100644 src/main/java/org/opensearch/security/dlic/rest/api/RestApiAdminPrivilegesEvaluator.java create mode 100644 src/main/java/org/opensearch/security/ssl/rest/SecuritySSLCertsInfoAction.java create mode 100644 src/main/java/org/opensearch/security/ssl/rest/SecuritySSLReloadCertsAction.java delete mode 100644 src/test/java/org/opensearch/security/dlic/rest/api/SslCertsApiTest.java delete mode 100644 src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacySslCertsApiTest.java delete mode 100644 src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java create mode 100644 src/test/java/org/opensearch/security/ssl/SecuritySSLCertsInfoActionTests.java diff --git a/config/roles.yml b/config/roles.yml index fd485423fe..29f6fcbe5d 100644 --- a/config/roles.yml +++ b/config/roles.yml @@ -9,20 +9,7 @@ kibana_read_only: # The security REST API access role is used to assign specific users access to change the security settings through the REST API. security_rest_api_access: reserved: true - -security_rest_api_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' - + # Allows users to view monitors, destinations and alerts alerting_read_access: reserved: true diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 6a58862028..ce7e880620 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -156,6 +156,8 @@ import org.opensearch.security.ssl.OpenSearchSecuritySSLPlugin; import org.opensearch.security.ssl.SslExceptionHandler; import org.opensearch.security.ssl.http.netty.ValidatingDispatcher; +import org.opensearch.security.ssl.rest.SecuritySSLCertsInfoAction; +import org.opensearch.security.ssl.rest.SecuritySSLReloadCertsAction; import org.opensearch.security.ssl.transport.DefaultPrincipalExtractor; import org.opensearch.security.ssl.transport.SecuritySSLNettyTransport; import org.opensearch.security.ssl.util.SSLConfigConstants; @@ -464,25 +466,18 @@ public List getRestHandlers(Settings settings, RestController restC if(!SSLConfig.isSslOnlyMode()) { handlers.add(new SecurityInfoAction(settings, restController, Objects.requireNonNull(evaluator), Objects.requireNonNull(threadPool))); handlers.add(new SecurityHealthAction(settings, restController, Objects.requireNonNull(backendRegistry))); + handlers.add(new SecuritySSLCertsInfoAction(settings, restController, sks, Objects.requireNonNull(threadPool), Objects.requireNonNull(adminDns))); handlers.add(new DashboardsInfoAction(settings, restController, Objects.requireNonNull(evaluator), Objects.requireNonNull(threadPool))); handlers.add(new TenantInfoAction(settings, restController, Objects.requireNonNull(evaluator), Objects.requireNonNull(threadPool), - Objects.requireNonNull(cs), Objects.requireNonNull(adminDns), Objects.requireNonNull(cr))); - handlers.add(new SecurityConfigUpdateAction(settings, restController, Objects.requireNonNull(threadPool), adminDns, configPath, principalExtractor)); - handlers.add(new SecurityWhoAmIAction(settings, restController, Objects.requireNonNull(threadPool), adminDns, configPath, principalExtractor)); - handlers.addAll( - SecurityRestApiActions.getHandler( - settings, - configPath, - restController, - localClient, - adminDns, - cr, cs, principalExtractor, - evaluator, - threadPool, - Objects.requireNonNull(auditLog), sks, - sslCertReloadEnabled) - ); - log.debug("Added {} rest handler(s)", handlers.size()); + Objects.requireNonNull(cs), Objects.requireNonNull(adminDns), Objects.requireNonNull(cr))); + handlers.add(new SecurityConfigUpdateAction(settings, restController,Objects.requireNonNull(threadPool), adminDns, configPath, principalExtractor)); + handlers.add(new SecurityWhoAmIAction(settings ,restController,Objects.requireNonNull(threadPool), adminDns, configPath, principalExtractor)); + if (sslCertReloadEnabled) { + handlers.add(new SecuritySSLReloadCertsAction(settings, restController, sks, Objects.requireNonNull(threadPool), Objects.requireNonNull(adminDns))); + } + final Collection apiHandlers = SecurityRestApiActions.getHandler(settings, configPath, restController, localClient, adminDns, cr, cs, principalExtractor, evaluator, threadPool, Objects.requireNonNull(auditLog)); + handlers.addAll(apiHandlers); + log.debug("Added {} management rest handler(s)", apiHandlers.size()); } } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java index 0e98124b6f..f79aa3aaa8 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AbstractApiAction.java @@ -75,9 +75,9 @@ public abstract class AbstractApiAction extends BaseRestHandler { final ThreadPool threadPool; protected String opendistroIndex; private final RestApiPrivilegesEvaluator restApiPrivilegesEvaluator; - protected final RestApiAdminPrivilegesEvaluator restApiAdminPrivilegesEvaluator; protected final AuditLog auditLog; protected final Settings settings; + private AdminDNs adminDNs; protected AbstractApiAction(final Settings settings, final Path configPath, final RestController controller, final Client client, final AdminDNs adminDNs, final ConfigurationRepository cl, @@ -88,13 +88,12 @@ protected AbstractApiAction(final Settings settings, final Path configPath, fina this.opendistroIndex = settings.get(ConfigConstants.SECURITY_CONFIG_INDEX_NAME, ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); + this.adminDNs = adminDNs; this.cl = cl; this.cs = cs; this.threadPool = threadPool; this.restApiPrivilegesEvaluator = new RestApiPrivilegesEvaluator(settings, adminDNs, evaluator, principalExtractor, configPath, threadPool); - this.restApiAdminPrivilegesEvaluator = - new RestApiAdminPrivilegesEvaluator(threadPool.getThreadContext(), evaluator, adminDNs); this.auditLog = auditLog; } @@ -196,12 +195,7 @@ protected void handlePut(final RestChannel channel, final RestRequest request, f } boolean existed = existingConfiguration.exists(name); - final Object newContent = DefaultObjectMapper.readTree(content, existingConfiguration.getImplementingClass()); - if (!hasPermissionsToCreate(existingConfiguration, newContent, getResourceName())) { - forbidden(channel, "No permissions"); - return; - } - existingConfiguration.putCObject(name, newContent); + existingConfiguration.putCObject(name, DefaultObjectMapper.readTree(content, existingConfiguration.getImplementingClass())); saveAnUpdateConfigs(client, request, getConfigName(), existingConfiguration, new OnSucessActionListener(channel) { @@ -222,12 +216,6 @@ protected void handlePost(final RestChannel channel, final RestRequest request, notImplemented(channel, Method.POST); } - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) throws IOException { - return false; - } - protected void handleGet(final RestChannel channel, RestRequest request, Client client, final JsonNode content) throws IOException{ @@ -460,6 +448,7 @@ protected static XContentBuilder convertToJson(RestChannel channel, ToXContent t } protected void response(RestChannel channel, RestStatus status, String message) { + try { final XContentBuilder builder = channel.newBuilder(); builder.startObject(); @@ -569,7 +558,8 @@ public String getName() { protected abstract Endpoint getEndpoint(); protected boolean isSuperAdmin() { - return restApiAdminPrivilegesEvaluator.isCurrentUserRestApiAdminFor(getEndpoint()); + User user = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + return adminDNs.isAdmin(user); } /** diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java index 885a5476af..831e390949 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AccountApiAction.java @@ -83,13 +83,6 @@ public AccountApiAction(Settings settings, this.threadContext = threadPool.getThreadContext(); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override public List routes() { return routes; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java index 23a3a451b9..83d1a993ff 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/ActionGroupsApiAction.java @@ -118,35 +118,12 @@ protected void handlePut(RestChannel channel, RestRequest request, Client client // Prevent the case where action group references to itself in the allowed_actions. final SecurityDynamicConfiguration existingActionGroupsConfig = load(getConfigName(), false); - final Object actionGroup = DefaultObjectMapper.readTree(content, existingActionGroupsConfig.getImplementingClass()); - existingActionGroupsConfig.putCObject(name, actionGroup); + existingActionGroupsConfig.putCObject(name, DefaultObjectMapper.readTree(content, existingActionGroupsConfig.getImplementingClass())); if (hasActionGroupSelfReference(existingActionGroupsConfig, name)) { badRequestResponse(channel, name + " cannot be an allowed_action of itself"); return; } - // prevent creation of groups for REST admin api - if (restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(actionGroup)) { - forbidden(channel, "Not allowed"); - return; - } - super.handlePut(channel, request, client, content); - } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfiguration, - final Object content, - final String resourceName) throws IOException { - if (restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(content)) { - return false; - } - return true; - } - - @Override - protected boolean isReadOnly(SecurityDynamicConfiguration existingConfiguration, String name) { - if (restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(existingConfiguration.getCEntry(name))) { - return true; - } - return super.isReadOnly(existingConfiguration, name); + super.handlePut(channel, request, client, content); } } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java index b37375a461..afe08bc486 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AllowlistApiAction.java @@ -98,13 +98,6 @@ public AllowlistApiAction(final Settings settings, final Path configPath, final super(settings, configPath, controller, client, adminDNs, cl, cs, principalExtractor, evaluator, threadPool, auditLog); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override protected void handleApiRequest(final RestChannel channel, final RestRequest request, final Client client) throws IOException { if (!isSuperAdmin()) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java index e19f04d437..ce11b74509 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java @@ -165,13 +165,6 @@ public AuditApiAction(final Settings settings, } } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override public List routes() { return routes; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java index 497efcdf76..2338fe15e9 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AuthTokenProcessorAction.java @@ -34,7 +34,6 @@ import org.opensearch.security.dlic.rest.validation.NoOpValidator; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.ssl.transport.PrincipalExtractor; import org.opensearch.threadpool.ThreadPool; @@ -54,13 +53,6 @@ public AuthTokenProcessorAction(final Settings settings, final Path configPath, auditLog); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override public List routes() { return routes; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/Endpoint.java b/src/main/java/org/opensearch/security/dlic/rest/api/Endpoint.java index 84a447bcac..ce57070825 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/Endpoint.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/Endpoint.java @@ -28,6 +28,5 @@ public enum Endpoint { VALIDATE, WHITELIST, ALLOWLIST, - NODESDN, - SSL; + NODESDN; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java index 406b81679c..7e6d7989a9 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/FlushCacheApiAction.java @@ -38,7 +38,6 @@ import org.opensearch.security.dlic.rest.validation.NoOpValidator; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.ssl.transport.PrincipalExtractor; import org.opensearch.threadpool.ThreadPool; @@ -59,13 +58,6 @@ public FlushCacheApiAction(final Settings settings, final Path configPath, final super(settings, configPath, controller, client, adminDNs, cl, cs, principalExtractor, evaluator, threadPool, auditLog); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override public List routes() { return routes; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java index 417465e353..e902178b7f 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/InternalUsersApiAction.java @@ -78,13 +78,6 @@ public InternalUsersApiAction(final Settings settings, final Path configPath, fi auditLog); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override public List routes() { return routes; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java index a58b6329b7..3403dc1ee8 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/MigrateApiAction.java @@ -94,13 +94,6 @@ protected Endpoint getEndpoint() { return Endpoint.MIGRATE; } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @SuppressWarnings("unchecked") @Override protected void handlePost(RestChannel channel, RestRequest request, Client client, final JsonNode content) throws IOException { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java index 22897b8305..5498c2a50b 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/NodesDnApiAction.java @@ -77,13 +77,6 @@ public NodesDnApiAction(final Settings settings, final Path configPath, final Re this.staticNodesDnFromEsYml = settings.getAsList(ConfigConstants.SECURITY_NODES_DN, Collections.emptyList()); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override public List routes() { if (settings.getAsBoolean(ConfigConstants.SECURITY_NODES_DN_DYNAMIC_CONFIG_ENABLED, false)) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java index 6d644c1eae..74abb1d10a 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/PatchableResourceApiAction.java @@ -55,9 +55,9 @@ public abstract class PatchableResourceApiAction extends AbstractApiAction { protected final Logger log = LogManager.getLogger(this.getClass()); public PatchableResourceApiAction(Settings settings, Path configPath, RestController controller, Client client, - AdminDNs adminDNs, ConfigurationRepository cl, ClusterService cs, - PrincipalExtractor principalExtractor, PrivilegesEvaluator evaluator, ThreadPool threadPool, - AuditLog auditLog) { + AdminDNs adminDNs, ConfigurationRepository cl, ClusterService cs, + PrincipalExtractor principalExtractor, PrivilegesEvaluator evaluator, ThreadPool threadPool, + AuditLog auditLog) { super(settings, configPath, controller, client, adminDNs, cl, cs, principalExtractor, evaluator, threadPool, auditLog); } @@ -146,7 +146,7 @@ private void handleSinglePatch(RestChannel channel, RestRequest request, Client if (!validator.validate()) { request.params().clear(); - badRequestResponse(channel, validator); + badRequestResponse(channel, validator); return; } @@ -156,7 +156,7 @@ private void handleSinglePatch(RestChannel channel, RestRequest request, Client , existingConfiguration.getVersion(), existingConfiguration.getSeqNo(), existingConfiguration.getPrimaryTerm()); if (existingConfiguration.getCType().equals(CType.ACTIONGROUPS)) { - if (hasActionGroupSelfReference(mdc, name)) { + if(hasActionGroupSelfReference(mdc, name)) { badRequestResponse(channel, name + " cannot be an allowed_action of itself"); return; } @@ -188,6 +188,7 @@ private void handleBulkPatch(RestChannel channel, RestRequest request, Client cl for (String resourceName : existingConfiguration.getCEntries().keySet()) { JsonNode oldResource = existingAsObjectNode.get(resourceName); JsonNode patchedResource = patchedAsJsonNode.get(resourceName); + if (oldResource != null && !oldResource.equals(patchedResource) && !isWriteable(channel, existingConfiguration, resourceName)) { return; } @@ -205,7 +206,7 @@ private void handleBulkPatch(RestChannel channel, RestRequest request, Client cl if(originalValidator != null) { if (!originalValidator.validate()) { request.params().clear(); - badRequestResponse(channel, originalValidator); + badRequestResponse(channel, originalValidator); return; } } @@ -221,13 +222,7 @@ private void handleBulkPatch(RestChannel channel, RestRequest request, Client cl if (!validator.validate()) { request.params().clear(); - badRequestResponse(channel, validator); - return; - } - final Object newContent = DefaultObjectMapper.readTree(patchedResource, existingConfiguration.getImplementingClass()); - if (!hasPermissionsToCreate(existingConfiguration, newContent, resourceName)) { - request.params().clear(); - forbidden(channel, "No permissions"); + badRequestResponse(channel, validator); return; } } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RestApiAdminPrivilegesEvaluator.java b/src/main/java/org/opensearch/security/dlic/rest/api/RestApiAdminPrivilegesEvaluator.java deleted file mode 100644 index c3449e99bb..0000000000 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RestApiAdminPrivilegesEvaluator.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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.Locale; -import java.util.Map; - -import com.google.common.collect.ImmutableMap; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.common.util.concurrent.ThreadContext; -import org.opensearch.security.configuration.AdminDNs; -import org.opensearch.security.dlic.rest.support.Utils; -import org.opensearch.security.privileges.PrivilegesEvaluator; -import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; -import org.opensearch.security.securityconf.impl.v7.RoleV7; -import org.opensearch.security.support.WildcardMatcher; -import org.opensearch.security.user.User; - -public class RestApiAdminPrivilegesEvaluator { - - protected final Logger logger = LogManager.getLogger(RestApiAdminPrivilegesEvaluator.class); - - public final static String CERTS_INFO_ACTION = "certs"; - - public final static String RELOAD_CERTS_ACTION = "reloadcerts"; - - private final static String REST_API_PERMISSION_PREFIX = "restapi:admin"; - - private final static String REST_ENDPOINT_PERMISSION_PATTERN = - REST_API_PERMISSION_PREFIX + "/%s"; - - private final static String REST_ENDPOINT_ACTION_PERMISSION_PATTERN = - REST_API_PERMISSION_PREFIX + "/%s/%s"; - - private final static WildcardMatcher REST_API_PERMISSION_PREFIX_MATCHER = - WildcardMatcher.from(REST_API_PERMISSION_PREFIX + "/*"); - - @FunctionalInterface - public interface PermissionBuilder { - - default String build() { - return build(null); - } - - String build(final String action); - - } - - public final static Map ENDPOINTS_WITH_PERMISSIONS = - ImmutableMap.builder() - .put(Endpoint.ACTIONGROUPS, action -> buildEndpointPermission(Endpoint.ACTIONGROUPS)) - .put(Endpoint.ALLOWLIST, action -> buildEndpointPermission(Endpoint.ALLOWLIST)) - .put(Endpoint.INTERNALUSERS, action -> buildEndpointPermission(Endpoint.INTERNALUSERS)) - .put(Endpoint.NODESDN, action -> buildEndpointPermission(Endpoint.NODESDN)) - .put(Endpoint.ROLES, action -> buildEndpointPermission(Endpoint.ROLES)) - .put(Endpoint.ROLESMAPPING, action -> buildEndpointPermission(Endpoint.ROLESMAPPING)) - .put(Endpoint.TENANTS, action -> buildEndpointPermission(Endpoint.TENANTS)) - .put(Endpoint.SSL, action -> { - switch (action) { - case CERTS_INFO_ACTION: - return buildEndpointActionPermission(Endpoint.SSL, "certs/info"); - case RELOAD_CERTS_ACTION: - return buildEndpointActionPermission(Endpoint.SSL, "certs/reload"); - default: - return null; - } - }).build(); - - private final ThreadContext threadContext; - - private final PrivilegesEvaluator privilegesEvaluator; - - private final AdminDNs adminDNs; - - public RestApiAdminPrivilegesEvaluator( - final ThreadContext threadContext, - final PrivilegesEvaluator privilegesEvaluator, - final AdminDNs adminDNs) { - this.threadContext = threadContext; - this.privilegesEvaluator = privilegesEvaluator; - this.adminDNs = adminDNs; - } - - public boolean isCurrentUserRestApiAdminFor(final Endpoint endpoint, final String action) { - final Pair userAndRemoteAddress = Utils.userAndRemoteAddressFrom(threadContext); - if (userAndRemoteAddress.getLeft() == null) { - return false; - } - if (adminDNs.isAdmin(userAndRemoteAddress.getLeft())) { - if (logger.isDebugEnabled()) { - logger.debug( - "Security admin permissions required for endpoint {} but {} is not an admin", - endpoint, userAndRemoteAddress.getLeft().getName()); - } - return true; - } - if (!ENDPOINTS_WITH_PERMISSIONS.containsKey(endpoint)) { - if (logger.isDebugEnabled()) { - logger.debug("No permission found for {} endpoint", endpoint); - } - return false; - } - final String permission = ENDPOINTS_WITH_PERMISSIONS.get(endpoint).build(action); - if (logger.isDebugEnabled()) { - logger.debug("Checking permission {} for endpoint {}", permission, endpoint); - } - return privilegesEvaluator.hasRestAdminPermissions( - userAndRemoteAddress.getLeft(), - userAndRemoteAddress.getRight(), - permission - ); - } - - public boolean containsRestApiAdminPermissions(final Object configObject) { - if (configObject == null) { - return false; - } - if (configObject instanceof RoleV7) { - return ((RoleV7) configObject) - .getCluster_permissions() - .stream() - .anyMatch(REST_API_PERMISSION_PREFIX_MATCHER); - } else if (configObject instanceof ActionGroupsV7) { - return ((ActionGroupsV7) configObject) - .getAllowed_actions() - .stream() - .anyMatch(REST_API_PERMISSION_PREFIX_MATCHER); - } else { - return false; - } - } - - public boolean isCurrentUserRestApiAdminFor(final Endpoint endpoint) { - return isCurrentUserRestApiAdminFor(endpoint, null); - } - - private static String buildEndpointActionPermission(final Endpoint endpoint, final String action) { - return String.format( - REST_ENDPOINT_ACTION_PERMISSION_PATTERN, - endpoint.name().toLowerCase(Locale.ROOT), - action); - } - - private static String buildEndpointPermission(final Endpoint endpoint) { - return String.format( - REST_ENDPOINT_PERMISSION_PATTERN, - endpoint.name().toLowerCase(Locale.ROOT) - ); - } - -} diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java b/src/main/java/org/opensearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java index 04b4b31c77..21316876cb 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java @@ -25,7 +25,6 @@ import java.util.Map.Entry; import java.util.Set; -import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -72,11 +71,7 @@ public class RestApiPrivilegesEvaluator { private final Boolean roleBasedAccessEnabled; - public RestApiPrivilegesEvaluator(final Settings settings, - final AdminDNs adminDNs, - final PrivilegesEvaluator privilegesEvaluator, - final PrincipalExtractor principalExtractor, - final Path configPath, + public RestApiPrivilegesEvaluator(Settings settings, AdminDNs adminDNs, PrivilegesEvaluator privilegesEvaluator, PrincipalExtractor principalExtractor, Path configPath, ThreadPool threadPool) { this.adminDNs = adminDNs; @@ -85,7 +80,9 @@ public RestApiPrivilegesEvaluator(final Settings settings, this.configPath = configPath; this.threadPool = threadPool; this.settings = settings; + // set up + // all endpoints and methods Map> allEndpoints = new HashMap<>(); for(Endpoint endpoint : Endpoint.values()) { @@ -347,10 +344,8 @@ private String checkRoleBasedAccessPermissions(RestRequest request, Endpoint end if (this.roleBasedAccessEnabled) { // get current user and roles - final Pair userAndRemoteAddress = - Utils.userAndRemoteAddressFrom(threadPool.getThreadContext()); - final User user = userAndRemoteAddress.getLeft(); - final TransportAddress remoteAddress = userAndRemoteAddress.getRight(); + final User user = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + final TransportAddress remoteAddress = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); // map the users Security roles Set userRoles = privilegesEvaluator.mapRoles(user, remoteAddress); @@ -360,6 +355,7 @@ private String checkRoleBasedAccessPermissions(RestRequest request, Endpoint end // yes, calculate disabled end points. Since a user can have // multiple roles, the endpoint // needs to be disabled in all roles. + Map> disabledEndpointsForUser = getDisabledEndpointsForCurrentUser(user.getName(), userRoles); if (isDebugEnabled) { diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java index 7b2676c246..7fb6dfa915 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java @@ -11,7 +11,6 @@ package org.opensearch.security.dlic.rest.api; -import java.io.IOException; import java.nio.file.Path; import java.util.List; @@ -32,7 +31,6 @@ import org.opensearch.security.dlic.rest.validation.RolesValidator; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.ssl.transport.PrincipalExtractor; import org.opensearch.threadpool.ThreadPool; @@ -79,22 +77,4 @@ protected CType getConfigName() { return CType.ROLES; } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfiguration, final Object content, final String resourceName) throws IOException { - if (restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(content)) { - return isSuperAdmin(); - } else { - return true; - } - } - - @Override - protected boolean isReadOnly(SecurityDynamicConfiguration existingConfiguration, String name) { - if (restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(existingConfiguration.getCEntry(name))) { - return !isSuperAdmin(); - } else { - return super.isReadOnly(existingConfiguration, name); - } - } - } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java index b056a25ad2..c7e7f3d7ec 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesMappingApiAction.java @@ -68,18 +68,11 @@ protected void handlePut(RestChannel channel, final RestRequest request, final C return; } - final SecurityDynamicConfiguration rolesConfiguration = load(CType.ROLES, false); final SecurityDynamicConfiguration rolesMappingConfiguration = load(getConfigName(), false); final boolean rolesMappingExists = rolesMappingConfiguration.exists(name); if (!isValidRolesMapping(channel, name)) return; - if (restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(rolesConfiguration.getCEntry(name))) { - if (!isSuperAdmin()) { - forbidden(channel, "No permissions"); - return; - } - } rolesMappingConfiguration.putCObject(name, DefaultObjectMapper.readTree(content, rolesMappingConfiguration.getImplementingClass())); saveAnUpdateConfigs(client, request, getConfigName(), rolesMappingConfiguration, new OnSucessActionListener(channel) { @@ -96,26 +89,6 @@ public void onResponse(IndexResponse response) { }); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, final Object content, final String resourceName) throws IOException { - final SecurityDynamicConfiguration rolesConfiguration = load(CType.ROLES, false); - if (restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(rolesConfiguration.getCEntry(resourceName))) { - return isSuperAdmin(); - } else { - return true; - } - } - - @Override - protected boolean isReadOnly(SecurityDynamicConfiguration existingConfiguration, String name) { - final SecurityDynamicConfiguration rolesConfiguration = load(CType.ROLES, false); - if (restApiAdminPrivilegesEvaluator.containsRestApiAdminPermissions(rolesConfiguration.getCEntry(name))) { - return !isSuperAdmin(); - } else { - return super.isReadOnly(existingConfiguration, name); - } - } - @Override public List routes() { return routes; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigAction.java index 66888bc126..7545f47113 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityConfigAction.java @@ -74,13 +74,6 @@ public List routes() { return allowPutOrPatch ? allRoutes : getRoutes; } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override protected void handleGet(RestChannel channel, RestRequest request, Client client, final JsonNode content) throws IOException{ final SecurityDynamicConfiguration configuration = load(getConfigName(), true); diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityRestApiActions.java b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityRestApiActions.java index 9655ba67ea..54ea46efd1 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/SecurityRestApiActions.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/SecurityRestApiActions.java @@ -26,26 +26,15 @@ import org.opensearch.security.configuration.AdminDNs; import org.opensearch.security.configuration.ConfigurationRepository; import org.opensearch.security.privileges.PrivilegesEvaluator; -import org.opensearch.security.ssl.SecurityKeyStore; import org.opensearch.security.ssl.transport.PrincipalExtractor; import org.opensearch.threadpool.ThreadPool; public class SecurityRestApiActions { - public static Collection getHandler(final Settings settings, - final Path configPath, - final RestController controller, - final Client client, - final AdminDNs adminDns, - final ConfigurationRepository cr, - final ClusterService cs, - final PrincipalExtractor principalExtractor, - final PrivilegesEvaluator evaluator, - final ThreadPool threadPool, - final AuditLog auditLog, - final SecurityKeyStore securityKeyStore, - final boolean certificatesReloadEnabled) { - final List handlers = new ArrayList(16); + public static Collection getHandler(Settings settings, Path configPath, RestController controller, Client client, + AdminDNs adminDns, ConfigurationRepository cr, ClusterService cs, PrincipalExtractor principalExtractor, + final PrivilegesEvaluator evaluator, ThreadPool threadPool, AuditLog auditLog) { + final List handlers = new ArrayList(15); handlers.add(new InternalUsersApiAction(settings, configPath, controller, client, adminDns, cr, cs, principalExtractor, evaluator, threadPool, auditLog)); handlers.add(new RolesMappingApiAction(settings, configPath, controller, client, adminDns, cr, cs, principalExtractor, evaluator, threadPool, auditLog)); handlers.add(new RolesApiAction(settings, configPath, controller, client, adminDns, cr, cs, principalExtractor, evaluator, threadPool, auditLog)); @@ -62,7 +51,6 @@ public static Collection getHandler(final Settings settings, handlers.add(new WhitelistApiAction(settings, configPath, controller, client, adminDns, cr, cs, principalExtractor, evaluator, threadPool, auditLog)); handlers.add(new AllowlistApiAction(settings, configPath, controller, client, adminDns, cr, cs, principalExtractor, evaluator, threadPool, auditLog)); handlers.add(new AuditApiAction(settings, configPath, controller, client, adminDns, cr, cs, principalExtractor, evaluator, threadPool, auditLog)); - handlers.add(new SecuritySSLCertsAction(settings, configPath, controller, client, adminDns, cr, cs, principalExtractor, evaluator, threadPool, auditLog, securityKeyStore, certificatesReloadEnabled)); return Collections.unmodifiableCollection(handlers); } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java index ef8d1d3b9f..26d39767d5 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java @@ -47,7 +47,6 @@ import org.opensearch.security.dlic.rest.validation.TenantValidator; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.ssl.transport.PrincipalExtractor; import org.opensearch.threadpool.ThreadPool; @@ -70,13 +69,6 @@ public TenantsApiAction(final Settings settings, final Path configPath, final Re super(settings, configPath, controller, client, adminDNs, cl, cs, principalExtractor, evaluator, threadPool, auditLog); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override public List routes() { return routes; diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java index 8e2222cab0..a3f2b202ee 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/ValidateApiAction.java @@ -67,13 +67,6 @@ public ValidateApiAction(final Settings settings, final Path configPath, final R super(settings, configPath, controller, client, adminDNs, cl, cs, principalExtractor, evaluator, threadPool, auditLog); } - @Override - protected boolean hasPermissionsToCreate(final SecurityDynamicConfiguration dynamicConfigFactory, - final Object content, - final String resourceName) { - return true; - } - @Override public List routes() { return routes; diff --git a/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java b/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java index aba2807846..24588c98eb 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java +++ b/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java @@ -29,7 +29,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import org.apache.commons.lang3.tuple.Pair; import org.bouncycastle.crypto.generators.OpenBSDBCrypt; import org.opensearch.ExceptionsHelper; @@ -47,8 +46,6 @@ import org.opensearch.rest.RestHandler.DeprecatedRoute; import org.opensearch.rest.RestHandler.Route; import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.support.ConfigConstants; -import org.opensearch.security.user.User; import static org.opensearch.core.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION; @@ -270,11 +267,4 @@ public static List addDeprecatedRoutesPrefix(List new DeprecatedRoute(r.getMethod(), p + r.getPath(), r.getDeprecationMessage()))) .collect(ImmutableList.toImmutableList()); } - - public static Pair userAndRemoteAddressFrom(final ThreadContext threadContext) { - final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - final TransportAddress remoteAddress = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); - return Pair.of(user, remoteAddress); - } - } diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index cceaeb4cb0..43722dad46 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -179,18 +179,6 @@ private SecurityRoles getSecurityRoles(Set roles) { return configModel.getSecurityRoles().filter(roles); } - public boolean hasRestAdminPermissions(final User user, - final TransportAddress remoteAddress, - final String permissions) { - final Set userRoles = mapRoles(user, remoteAddress); - return hasRestAdminPermissions(userRoles, permissions); - } - - private boolean hasRestAdminPermissions(final Set roles, String permission) { - final SecurityRoles securityRoles = getSecurityRoles(roles); - return securityRoles.hasExplicitClusterPermissionPermission(permission); - } - public boolean isInitialized() { return configModel !=null && configModel.getSecurityRoles() != null && dcm != null; } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index 987b8fac64..3638960c50 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -482,20 +482,10 @@ public boolean get(Resolved resolved, User user, String[] actions, IndexNameExpr return false; } - @Override public boolean impliesClusterPermissionPermission(String action) { return roles.stream().filter(r -> r.impliesClusterPermission(action)).count() > 0; } - @Override - public boolean hasExplicitClusterPermissionPermission(String action) { - return roles.stream() - .map(r -> { - final WildcardMatcher m = WildcardMatcher.from(r.clusterPerms); - return m == WildcardMatcher.ANY ? WildcardMatcher.NONE : m; - }).filter(m -> m.test(action)).count() > 0; - } - //rolespan public boolean impliesTypePermGlobal(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 8b8dd9f3e3..ec75bbd08f 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -478,18 +478,10 @@ public boolean get(Resolved resolved, User user, String[] actions, IndexNameExpr return false; } - @Override public boolean impliesClusterPermissionPermission(String action) { return roles.stream().filter(r -> r.impliesClusterPermission(action)).count() > 0; } - @Override - public boolean hasExplicitClusterPermissionPermission(String action) { - return roles.stream() - .map(r -> r.clusterPerms == WildcardMatcher.ANY ? WildcardMatcher.NONE : r.clusterPerms) - .filter(m -> m.test(action)).count() > 0; - } - //rolespan public boolean impliesTypePermGlobal(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) { diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index de7afbc27b..ecd29cb20c 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -39,8 +39,6 @@ public interface SecurityRoles { boolean impliesClusterPermissionPermission(String action0); - boolean hasExplicitClusterPermissionPermission(String action); - Set getRoleNames(); Set reduce(Resolved requestedResolved, User user, String[] strings, IndexNameExpressionResolver resolver, ClusterService clusterService); diff --git a/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLCertsInfoAction.java b/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLCertsInfoAction.java new file mode 100644 index 0000000000..6c1ec5296c --- /dev/null +++ b/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLCertsInfoAction.java @@ -0,0 +1,190 @@ +/* + * 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.ssl.rest; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.BytesRestResponse; +import org.opensearch.rest.RestChannel; +import org.opensearch.rest.RestController; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.RestRequest.Method; +import org.opensearch.rest.RestStatus; +import org.opensearch.security.configuration.AdminDNs; +import org.opensearch.security.ssl.SecurityKeyStore; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; +import org.opensearch.threadpool.ThreadPool; + +import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; + + +/** + * Rest API action to get SSL certificate information related to http and transport encryption. + * Only super admin users are allowed to access this API. + * Currently this action serves GET request for _plugins/_security/api/ssl/certs endpoint + */ +public class SecuritySSLCertsInfoAction extends BaseRestHandler { + private static final List routes = addRoutesPrefix(ImmutableList.of( + new Route(Method.GET, "/ssl/certs") + )); + + private final Logger log = LogManager.getLogger(this.getClass()); + private Settings settings; + private SecurityKeyStore odsks; + private AdminDNs adminDns; + private ThreadContext threadContext; + + public SecuritySSLCertsInfoAction(final Settings settings, + final RestController restController, + final SecurityKeyStore odsks, + final ThreadPool threadPool, + final AdminDNs adminDns) { + super(); + this.settings = settings; + this.odsks = odsks; + this.adminDns = adminDns; + this.threadContext = threadPool.getThreadContext(); + } + + @Override + public List routes() { + return routes; + } + + /** + * GET request to fetch transport certificate details + * + * Sample request: + * GET _plugins/_security/api/ssl/certs + * + * Sample response: + * { + * "http_certificates_list" : [ + * { + * "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=transport-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:09.000Z", + * "not_after" : "2028-05-02T14:37:09.000Z" + * } + * "transport_certificates_list" : [ + * { + * "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=transport-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:09.000Z", + * "not_after" : "2028-05-02T14:37:09.000Z" + * } + * ] + * } + * + * @param request request to be served + * @param client client + * @throws IOException + */ + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + + return new RestChannelConsumer() { + + @Override + public void accept(RestChannel channel) throws Exception { + XContentBuilder builder = channel.newBuilder(); + BytesRestResponse response = null; + + // Check for Super admin user + final User user = (User)threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + if(user == null || !adminDns.isAdmin(user)) { + response = new BytesRestResponse(RestStatus.FORBIDDEN, builder); + } else { + try { + // Check if keystore initialised + if (odsks != null) { + builder.startObject(); + builder.field("http_certificates_list", generateCertDetailList(odsks.getHttpCerts())); + builder.field("transport_certificates_list", generateCertDetailList(odsks.getTransportCerts())); + builder.endObject(); + response = new BytesRestResponse(RestStatus.OK, builder); + } else { + builder.startObject(); + builder.field("message", "keystore is not initialized"); + builder.endObject(); + response = new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, builder); + } + } catch (final Exception e1) { + log.error("Error handle request ", e1); + builder = channel.newBuilder(); + builder.startObject(); + builder.field("error", e1.toString()); + builder.endObject(); + response = new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, builder); + } finally { + if (builder != null) { + builder.close(); + } + } + } + channel.sendResponse(response); + } + + /** + * Helper that construct list of certificate details. + * @param certs list of certificates. + * @return Array containing certificate details. + */ + private List> generateCertDetailList(final X509Certificate[] certs) { + if (certs == null) { + return null; + } + return Arrays.stream(certs) + .map(cert -> { + final String issuerDn = cert != null && cert.getIssuerX500Principal() != null ? cert.getIssuerX500Principal().getName(): ""; + final String subjectDn = cert != null && cert.getSubjectX500Principal() != null ? cert.getSubjectX500Principal().getName(): ""; + + final String san = odsks.getSubjectAlternativeNames(cert); + + final String notBefore = cert != null && cert.getNotBefore() != null ? cert.getNotBefore().toInstant().toString(): ""; + final String notAfter = cert != null && cert.getNotAfter() != null ? cert.getNotAfter().toInstant().toString(): ""; + return ImmutableMap.builder() + .put("issuer_dn", issuerDn) + .put("subject_dn", subjectDn) + .put("san", san) + .put("not_before", notBefore) + .put("not_after", notAfter) + .build(); + }) + .collect(Collectors.toList()); + } + }; + } + + @Override + public String getName() { + return "SSL Certificate Information Action"; + } +} diff --git a/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLReloadCertsAction.java b/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLReloadCertsAction.java new file mode 100644 index 0000000000..ef4c89c625 --- /dev/null +++ b/src/main/java/org/opensearch/security/ssl/rest/SecuritySSLReloadCertsAction.java @@ -0,0 +1,155 @@ +/* + * 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.ssl.rest; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.BytesRestResponse; +import org.opensearch.rest.RestChannel; +import org.opensearch.rest.RestController; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.RestStatus; +import org.opensearch.security.configuration.AdminDNs; +import org.opensearch.security.ssl.SecurityKeyStore; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; +import org.opensearch.threadpool.ThreadPool; + +import static org.opensearch.rest.RestRequest.Method.PUT; + + +/** + * Rest API action to reload SSL certificates. + * Can be used to reload SSL certificates that are about to expire without restarting OpenSearch node. + * This API assumes that new certificates are in the same location specified by the security configurations in opensearch.yml + * (https://docs-beta.opensearch.org/docs/security-configuration/tls/) + * To keep sensitive certificate reload secure, this API will only allow hot reload + * with certificates issued by the same Issuer and Subject DN and SAN with expiry dates after the current one. + * Currently this action serves PUT request for /_opendistro/_security/ssl/http/reloadcerts or /_opendistro/_security/ssl/transport/reloadcerts endpoint + */ +public class SecuritySSLReloadCertsAction extends BaseRestHandler { + private static final List routes = Collections.singletonList( + new Route(PUT, "_opendistro/_security/api/ssl/{certType}/reloadcerts/") + ); + + private final Settings settings; + private final SecurityKeyStore sks; + private final ThreadContext threadContext; + private final AdminDNs adminDns; + + public SecuritySSLReloadCertsAction(final Settings settings, + final RestController restController, + final SecurityKeyStore sks, + final ThreadPool threadPool, + final AdminDNs adminDns) { + super(); + this.settings = settings; + this.sks = sks; + this.adminDns = adminDns; + this.threadContext = threadPool.getThreadContext(); + } + + @Override + public List routes() { + return routes; + } + + /** + * PUT request to reload SSL Certificates. + * + * Sample request: + * PUT _opendistro/_security/api/ssl/transport/reloadcerts + * PUT _opendistro/_security/api/ssl/http/reloadcerts + * + * NOTE: No request body is required. We will assume new certificates are loaded in the paths specified in your opensearch.yml file + * (https://docs-beta.opensearch.org/docs/security/configuration/tls/) + * + * Sample response: + * { "message": "updated http certs" } + * + * @param request request to be served + * @param client client + * @throws IOException + */ + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + return new RestChannelConsumer() { + + final String certType = request.param("certType").toLowerCase().trim(); + + @Override + public void accept(RestChannel channel) throws Exception { + XContentBuilder builder = channel.newBuilder(); + BytesRestResponse response = null; + + // Check for Super admin user + final User user = (User) threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + if(user ==null||!adminDns.isAdmin(user)) { + response = new BytesRestResponse(RestStatus.FORBIDDEN, ""); + } else { + try { + builder.startObject(); + if (sks != null) { + switch (certType) { + case "http": + sks.initHttpSSLConfig(); + builder.field("message", "updated http certs"); + builder.endObject(); + response = new BytesRestResponse(RestStatus.OK, builder); + break; + case "transport": + sks.initTransportSSLConfig(); + builder.field("message", "updated transport certs"); + builder.endObject(); + response = new BytesRestResponse(RestStatus.OK, builder); + break; + default: + builder.field("message", "invalid uri path, please use /_opendistro/_security/api/ssl/http/reload or " + + "/_opendistro/_security/api/ssl/transport/reload"); + builder.endObject(); + response = new BytesRestResponse(RestStatus.FORBIDDEN, builder); + break; + } + } else { + builder.field("message", "keystore is not initialized"); + builder.endObject(); + response = new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, builder); + } + } catch (final Exception e1) { + builder = channel.newBuilder(); + builder.startObject(); + builder.field("error", e1.toString()); + builder.endObject(); + response = new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, builder); + } finally { + if (builder != null) { + builder.close(); + } + } + } + channel.sendResponse(response); + } + }; + } + + @Override + public String getName() { + return "SSL Cert Reload Action"; + } +} 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 98730a81cb..59e8feb198 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,14 +16,10 @@ 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.http.Header; import org.apache.http.HttpStatus; import org.junit.Assert; @@ -94,7 +90,18 @@ protected final void setupWithRestRoles(Settings nodeOverride) throws Exception .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("restapi/truststore.jks")); - builder.put(rolesSettings()); + 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", "*"); if (null != nodeOverride) { builder.put(nodeOverride); @@ -107,20 +114,6 @@ 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; @@ -204,23 +197,30 @@ 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,27 +260,4 @@ protected Map jsonStringToMap(String json) throws JsonParseExcep protected static Collection> asCollection(Class... 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 Map.Entry entry : RestApiAdminPrivilegesEvaluator.ENDPOINTS_WITH_PERMISSIONS.entrySet()) { - if (entry.getKey() == Endpoint.SSL) { - permissionsArray - .add(entry.getValue().build("certs")) - .add(entry.getValue().build("reloadcerts")); - } else { - permissionsArray.add(entry.getValue().build()); - } - } - 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 bf5acdf0c3..09efae9fbe 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,9 +13,6 @@ 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.http.Header; import org.apache.http.HttpStatus; import org.junit.Assert; @@ -23,7 +20,6 @@ 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; @@ -48,27 +44,9 @@ 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); - checkWriteAccess(HttpStatus.SC_FORBIDDEN, "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 { // --- GET_UT // GET_UT, actiongroup exists - HttpResponse response = rh.executeGetRequest(ENDPOINT+"/CRUD_UT", header); + HttpResponse response = rh.executeGetRequest(ENDPOINT+"/CRUD_UT", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); List permissions = settings.getAsList("CRUD_UT.allowed_actions"); @@ -78,50 +56,61 @@ void verifyGetForSuperAdmin(final Header[] header) throws Exception { Assert.assertTrue(permissions.contains("OPENDISTRO_SECURITY_WRITE")); // GET_UT, actiongroup does not exist - response = rh.executeGetRequest(ENDPOINT+"/nothinghthere", header); + response = rh.executeGetRequest(ENDPOINT+"/nothinghthere", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // GET_UT, old endpoint - response = rh.executeGetRequest(ENDPOINT, header); + response = rh.executeGetRequest(ENDPOINT, new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // GET_UT, old endpoint - response = rh.executeGetRequest(ENDPOINT, header); + response = rh.executeGetRequest(ENDPOINT, new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // GET_UT, new endpoint which replaces configuration endpoint - response = rh.executeGetRequest(ENDPOINT, header); + response = rh.executeGetRequest(ENDPOINT, new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // GET_UT, old endpoint - response = rh.executeGetRequest(ENDPOINT, header); + response = rh.executeGetRequest(ENDPOINT, new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // GET_UT, new endpoint which replaces configuration endpoint - response = rh.executeGetRequest(ENDPOINT, header); + response = rh.executeGetRequest(ENDPOINT, new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // GET_UT, new endpoint which replaces configuration endpoint - response = rh.executeGetRequest(ENDPOINT, header); + response = rh.executeGetRequest(ENDPOINT, new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - } - void verifyDeleteForSuperAdmin(final Header[] header, final boolean userAdminCert) throws Exception { + // 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); + // -- DELETE // Non-existing role - rh.sendAdminCertificate = userAdminCert; + rh.sendAdminCertificate = true; - HttpResponse response = rh.executeDeleteRequest(ENDPOINT+"/idonotexist", header); + response = rh.executeDeleteRequest(ENDPOINT+"/idonotexist", new Header[0]); 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", header); + response = rh.executeDeleteRequest(ENDPOINT+"/READ_UT", new Header[0]); 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 @@ -136,25 +125,24 @@ void verifyDeleteForSuperAdmin(final Header[] header, final boolean userAdminCer 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 = userAdminCert; - HttpResponse response = rh.executePutRequest(ENDPOINT+"/SOMEGROUP", "", header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT+"/SOMEGROUP", "", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); + 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"), - header); + new Header[0]); 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"), header); + response = rh.executePutRequest(ENDPOINT+"/CRUD_UT", FileHelper.loadFile("restapi/actiongroup_crud.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); rh.sendAdminCertificate = false; @@ -164,8 +152,8 @@ void verifyPutForSuperAdmin(final Header[] header, final boolean userAdminCert) checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0); // restore READ_UT action groups - rh.sendAdminCertificate = userAdminCert; - response = rh.executePutRequest(ENDPOINT+"/READ_UT", FileHelper.loadFile("restapi/actiongroup_read.json"), header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT+"/READ_UT", FileHelper.loadFile("restapi/actiongroup_read.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); rh.sendAdminCertificate = false; @@ -174,106 +162,99 @@ void verifyPutForSuperAdmin(final Header[] header, final boolean userAdminCert) checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0); // -- PUT, new JSON format including readonly flag, disallowed in REST API - rh.sendAdminCertificate = userAdminCert; - response = rh.executePutRequest(ENDPOINT+"/CRUD_UT", FileHelper.loadFile("restapi/actiongroup_readonly.json"), header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT+"/CRUD_UT", FileHelper.loadFile("restapi/actiongroup_readonly.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // -- DELETE read only resource, must be forbidden // superAdmin can delete read only resource - rh.sendAdminCertificate = userAdminCert; - response = rh.executeDeleteRequest(ENDPOINT+"/GET_UT", header); + rh.sendAdminCertificate = true; + response = rh.executeDeleteRequest(ENDPOINT+"/GET_UT", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // -- PUT read only resource, must be forbidden // superAdmin can add/update read only resource - rh.sendAdminCertificate = userAdminCert; - response = rh.executePutRequest(ENDPOINT+"/GET_UT", FileHelper.loadFile("restapi/actiongroup_read.json"), header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT+"/GET_UT", FileHelper.loadFile("restapi/actiongroup_read.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); Assert.assertFalse(response.getBody().contains("Resource 'GET_UT' is read-only.")); // PUT with role name - rh.sendAdminCertificate = userAdminCert; - response = rh.executePutRequest(ENDPOINT+"/kibana_user", FileHelper.loadFile("restapi/actiongroup_read.json"), header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT+"/kibana_user", FileHelper.loadFile("restapi/actiongroup_read.json"), new Header[0]); 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 = userAdminCert; - response = rh.executePutRequest(ENDPOINT+"/reference_itself", "{\"allowed_actions\": [\"reference_itself\"]}", header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT+"/reference_itself", "{\"allowed_actions\": [\"reference_itself\"]}", new Header[0]); 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 = userAdminCert; - response = rh.executeGetRequest(ENDPOINT+"/INTERNAL", header); + rh.sendAdminCertificate = true; + response = rh.executeGetRequest(ENDPOINT+"/INTERNAL", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(response.getBody().contains("\"hidden\":true")); // -- DELETE hidden resource, must be 404 - rh.sendAdminCertificate = userAdminCert; - response = rh.executeDeleteRequest(ENDPOINT+"/INTERNAL", header); + rh.sendAdminCertificate = true; + response = rh.executeDeleteRequest(ENDPOINT+"/INTERNAL", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(response.getBody().contains("'INTERNAL' deleted.")); // -- PUT hidden resource, must be forbidden - rh.sendAdminCertificate = userAdminCert; - response = rh.executePutRequest(ENDPOINT+"/INTERNAL", FileHelper.loadFile("restapi/actiongroup_read.json"), header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT+"/INTERNAL", FileHelper.loadFile("restapi/actiongroup_read.json"), new Header[0]); 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 = userAdminCert; - HttpResponse response = rh.executePatchRequest(ENDPOINT+"/imnothere", - "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT+"/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // PATCH read only resource, must be forbidden // SuperAdmin can patch read only resource - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT+"/GET_UT", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT+"/GET_UT", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // PATCH with self-referencing action groups - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT+"/GET_UT", "[{ \"op\": \"add\", \"path\": \"/allowed_actions/-\", \"value\": \"GET_UT\" }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT+"/GET_UT", "[{ \"op\": \"add\", \"path\": \"/allowed_actions/-\", \"value\": \"GET_UT\" }]", new Header[0]); 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\"] } }]", header); + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/BULKNEW1\", \"value\": {\"allowed_actions\": [\"BULKNEW1\"] } }," + "{ \"op\": \"add\", \"path\": \"/BULKNEW2\", \"value\": {\"allowed_actions\": [\"READ_UT\"] } }]", new Header[0]); 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 = userAdminCert; - response = rh.executePatchRequest(ENDPOINT+"/INTERNAL", - "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT+"/INTERNAL", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH value of hidden flag, must fail with validation error - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", new Header[0]); 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 = userAdminCert; - response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"1/INTERNAL/allowed_actions/-\", " + - "\"value\": \"OPENDISTRO_SECURITY_DELETE\" }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"1/INTERNAL/allowed_actions/-\", \"value\": \"OPENDISTRO_SECURITY_DELETE\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH new format - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"/allowed_actions/-\", " + - "\"value\": \"OPENDISTRO_SECURITY_DELETE\" }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT+"/CRUD_UT", "[{ \"op\": \"add\", \"path\": \"/allowed_actions/-\", \"value\": \"OPENDISTRO_SECURITY_DELETE\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT+"/CRUD_UT", header); + response = rh.executeGetRequest(ENDPOINT+"/CRUD_UT", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); - List permissions = settings.getAsList("CRUD_UT.allowed_actions"); + settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); + permissions = settings.getAsList("CRUD_UT.allowed_actions"); Assert.assertNotNull(permissions); Assert.assertEquals(3, permissions.size()); Assert.assertTrue(permissions.contains("READ_UT")); @@ -284,50 +265,49 @@ void verifyPatchForSuperAdmin(final Header[] header, final boolean userAdminCert // -- PATCH on whole config resource // PATCH read only resource, must be forbidden // SuperAdmin can patch read only resource - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/GET_UT/a\", \"value\": [ \"foo\", \"bar\" ] }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/GET_UT/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/GET_UT/description\", \"value\": \"foo\" }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/GET_UT/description\", \"value\": \"foo\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // PATCH hidden resource, must be bad request - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/INTERNAL/a\", \"value\": [ \"foo\", \"bar\" ] }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/INTERNAL/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH delete read only resource, must be forbidden // SuperAdmin can delete read only resource - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/GET_UT\" }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/GET_UT\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // PATCH delete hidden resource, must be bad request - rh.sendAdminCertificate = userAdminCert; - response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/INTERNAL\" }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/INTERNAL\" }]", new Header[0]); 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 = userAdminCert; - response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/CRUD_UT/hidden\", \"value\": true }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/CRUD_UT/hidden\", \"value\": true }]", new Header[0]); 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 = userAdminCert; - response = rh.executePatchRequest(ENDPOINT, - "[{ \"op\": \"add\", \"path\": \"/NEWNEWNEW\", \"value\": {\"allowed_actions\": [\"indices:data/write*\"], \"hidden\":true }}]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"add\", \"path\": \"/NEWNEWNEW\", \"value\": {\"allowed_actions\": [\"indices:data/write*\"], \"hidden\":true }}]", new Header[0]); 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 = 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); + 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]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT+"/BULKNEW1", header); + response = rh.executeGetRequest(ENDPOINT+"/BULKNEW1", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); permissions = settings.getAsList("BULKNEW1.allowed_actions"); @@ -336,7 +316,7 @@ void verifyPatchForSuperAdmin(final Header[] header, final boolean userAdminCert Assert.assertTrue(permissions.contains("indices:data/*")); Assert.assertTrue(permissions.contains("cluster:monitor/*")); - response = rh.executeGetRequest(ENDPOINT+"/BULKNEW2", header); + response = rh.executeGetRequest(ENDPOINT+"/BULKNEW2", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); permissions = settings.getAsList("BULKNEW2.allowed_actions"); @@ -345,13 +325,13 @@ void verifyPatchForSuperAdmin(final Header[] header, final boolean userAdminCert Assert.assertTrue(permissions.contains("READ_UT")); // delete resource - response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/BULKNEW1\" }]", header); + response = rh.executePatchRequest(ENDPOINT, "[{ \"op\": \"remove\", \"path\": \"/BULKNEW1\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT+"/BULKNEW1", header); + response = rh.executeGetRequest(ENDPOINT+"/BULKNEW1", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // assert other resource is still there - response = rh.executeGetRequest(ENDPOINT+"/BULKNEW2", header); + response = rh.executeGetRequest(ENDPOINT+"/BULKNEW2", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); permissions = settings.getAsList("BULKNEW2.allowed_actions"); @@ -360,85 +340,6 @@ void verifyPatchForSuperAdmin(final Header[] header, final boolean userAdminCert 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); - checkWriteAccess(HttpStatus.SC_FORBIDDEN, "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 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); - checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 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 a4a121bc06..1c97d138da 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(); + setupWithRestRoles(null); // No creds, no admin certificate - UNAUTHORIZED checkGetAndPutAllowlistPermissions(HttpStatus.SC_UNAUTHORIZED, false); @@ -156,29 +156,6 @@ 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/NodesDnApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/NodesDnApiTest.java index ca73e7b527..f72375600c 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,85 +181,6 @@ 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 cfb6b38145..8dc18f5043 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,10 +13,7 @@ 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.http.Header; import org.apache.http.HttpStatus; import org.junit.Assert; @@ -33,13 +30,12 @@ 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"; } @@ -71,26 +67,7 @@ public void testAllRolesForSuperAdmin() throws Exception { rh.keystore = "restapi/kirk-keystore.jks"; rh.sendAdminCertificate = true; - - 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); + HttpResponse response = rh.executeGetRequest(ENDPOINT + "/roles"); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertFalse(response.getBody().contains("_meta")); @@ -144,110 +121,103 @@ 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", header); + HttpResponse response = rh.executeGetRequest(ENDPOINT + "/roles"); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // -- GET + // GET opendistro_security_role_starfleet - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", new Header[0]); 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", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/nothinghthere", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/roles/", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/roles", header); + response = rh.executeGetRequest(ENDPOINT + "/roles", new Header[0]); 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", header); + response = rh.executeGetRequest(ENDPOINT + "/roles?pretty", new Header[0]); 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", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_hidden", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(response.getBody().contains("\"hidden\":true")); - } - void verifyDeleteForSuperAdmin(final Header[] header, final boolean sendAdminCert) throws Exception { + // 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); + + // -- DELETE + + rh.sendAdminCertificate = true; + // Non-existing role - HttpResponse response = rh.executeDeleteRequest(ENDPOINT + "/roles/idonotexist", header); + response = rh.executeDeleteRequest(ENDPOINT + "/roles/idonotexist", new Header[0]); 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", header); + response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_transport_client", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // hidden role allowed for superadmin - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal", header); + response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal", new Header[0]); 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", header); + response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", new Header[0]); 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 = sendAdminCert; + rh.sendAdminCertificate = true; // remove also starfleet role, nothing is allowed anymore - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", header); + response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", new Header[0]); 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 - HttpResponse response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", "", header); + response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", "", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - JsonNode settings = DefaultObjectMapper.readTree(response.getBody()); + 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"), header); + FileHelper.loadFile("restapi/roles_not_parseable.json"), new Header[0]); 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"), header); + FileHelper.loadFile("restapi/roles_invalid_keys.json"), new Header[0]); settings = DefaultObjectMapper.readTree(response.getBody()); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); Assert.assertEquals(AbstractConfigurationValidator.ErrorType.INVALID_CONFIGURATION.getMessage(), settings.get("reason").asText()); @@ -257,7 +227,7 @@ void verifyPutForSuperAdmin(final Header[] header, final boolean sendAdminCert) // put new configuration with wrong datatypes, must fail response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", - FileHelper.loadFile("restapi/roles_wrong_datatype.json"), header); + FileHelper.loadFile("restapi/roles_wrong_datatype.json"), new Header[0]); settings = DefaultObjectMapper.readTree(response.getBody()); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); Assert.assertEquals(AbstractConfigurationValidator.ErrorType.WRONG_DATATYPE.getMessage(), settings.get("reason").asText()); @@ -266,17 +236,17 @@ void verifyPutForSuperAdmin(final Header[] header, final boolean sendAdminCert) // 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"), header); + FileHelper.loadFile("restapi/roles_captains.json"), new Header[0]); 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"), header); + FileHelper.loadFile("restapi/roles_captains.json"), new Header[0]); 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"), header); + FileHelper.loadFile("restapi/roles_starfleet.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); rh.sendAdminCertificate = false; checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0); @@ -288,41 +258,41 @@ void verifyPutForSuperAdmin(final Header[] header, final boolean sendAdminCert) // - 'indices:*' checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0); - rh.sendAdminCertificate = sendAdminCert; + rh.sendAdminCertificate = true; // restore captains role response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains.json"), header); + FileHelper.loadFile("restapi/roles_captains.json"), new Header[0]); 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 = sendAdminCert; + rh.sendAdminCertificate = true; response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_complete_invalid.json"), header); + FileHelper.loadFile("restapi/roles_complete_invalid.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); -// rh.sendAdminCertificate = sendAdminCert; +// rh.sendAdminCertificate = true; // response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", -// FileHelper.loadFile("restapi/roles_multiple.json"), header); +// FileHelper.loadFile("restapi/roles_multiple.json"), new Header[0]); // 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"), header); + FileHelper.loadFile("restapi/roles_multiple_2.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // check tenants - rh.sendAdminCertificate = sendAdminCert; + rh.sendAdminCertificate = true; response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains_tenants.json"), header); + FileHelper.loadFile("restapi/roles_captains_tenants.json"), new Header[0]); 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", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); System.out.println(response.getBody()); settings = DefaultObjectMapper.readTree(response.getBody()); @@ -335,13 +305,13 @@ void verifyPutForSuperAdmin(final Header[] header, final boolean sendAdminCert) response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains_tenants2.json"), header); + FileHelper.loadFile("restapi/roles_captains_tenants2.json"), new Header[0]); 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", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = DefaultObjectMapper.readTree(response.getBody()); Assert.assertEquals(1, settings.size()); @@ -357,13 +327,13 @@ void verifyPutForSuperAdmin(final Header[] header, final boolean sendAdminCert) // remove tenants from role response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains_no_tenants.json"), header); + FileHelper.loadFile("restapi/roles_captains_no_tenants.json"), new Header[0]); 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", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = DefaultObjectMapper.readTree(response.getBody()); Assert.assertEquals(1, settings.size()); @@ -371,46 +341,32 @@ void verifyPutForSuperAdmin(final Header[] header, final boolean sendAdminCert) 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"), header); + FileHelper.loadFile("restapi/roles_captains_tenants_malformed.json"), new Header[0]); 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 = sendAdminCert; - HttpResponse response = rh.executePatchRequest( - ENDPOINT + "/roles/imnothere", - "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // PATCH read only resource, must be forbidden // SuperAdmin can patch it - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles/opendistro_security_transport_client", - "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_transport_client", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]); 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 = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles/opendistro_security_internal", - "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_internal", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH value of hidden flag, must fail with validation error - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet", - "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", new Header[0]); 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*\\}.*")); @@ -433,328 +389,122 @@ void verifyPatchForSuperAdmin(final Header[] header, final boolean sendAdminCert // -- PATCH on whole config resource // PATCH on non-existing resource - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/imnothere/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"add\", \"path\": \"/imnothere/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH read only resource, must be forbidden - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/a\", \"value\": [ \"foo\", \"bar\" ] }]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH hidden resource, must be bad request - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/a\", \"value\": [ \"foo\", \"bar\" ] }]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH delete read only resource, must be forbidden // SuperAdmin can delete read only user - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_transport_client\" }]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_transport_client\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // PATCH hidden resource, must be bad request, but allowed for superadmin - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_internal\"}]", - header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_internal\"}]", new Header[0]); 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 = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/newnewnew\", \"value\": { \"hidden\": true, \"index_permissions\" : " + - "[ {\"index_patterns\" : [ \"sf\" ],\"allowed_actions\" : [ \"OPENDISTRO_SECURITY_READ\" ]}] }}]", - header); + 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]); 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 = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": { \"index_permissions\" : " + - "[ {\"index_patterns\" : [ \"sf\" ],\"allowed_actions\" : [ \"OPENDISTRO_SECURITY_READ\" ]}] }}]", - header); + 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]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - JsonNode settings = DefaultObjectMapper.readTree(response.getBody()); - permissions = new SecurityJsonNode(settings).get("bulknew1").get("index_permissions").get(0).get("allowed_actions").asList(); + 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 = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", new Header[0]); 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"), header); + FileHelper.loadFile("restapi/roles_field_masks_valid.json"), new Header[0]); 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"), header); + FileHelper.loadFile("restapi/roles_field_masks_invalid.json"), new Header[0]); 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 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", header); + response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_transport_client" , new Header[0]); 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"), header); + response = rh.executePutRequest( ENDPOINT + "/roles/opendistro_security_transport_client", + FileHelper.loadFile("restapi/roles_captains.json"), new Header[0]); 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\" }]", - header); + response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_transport_client", "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]); 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\" }]", - header); + response = rh.executePatchRequest(ENDPOINT + "/roles/", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/description\", \"value\": \"foo\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); // get hidden role - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_internal", header); + response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_internal"); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // delete hidden role - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal", header); + response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal" , new Header[0]); 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, header); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); + response = rh.executePutRequest( ENDPOINT+ "/roles/opendistro_security_internal", body, new Header[0]); + Assert.assertEquals(org.apache.http.HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // Patch single hidden roles - response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_internal", - "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]", header); + response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_internal", "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]); 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\" }]", - header); + response = rh.executePatchRequest(ENDPOINT + "/roles/", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/description\", \"value\": \"foo\" }]", new Header[0]); 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; @@ -766,7 +516,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 19174a115b..2d1f10736d 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,9 +13,6 @@ 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.http.Header; import org.apache.http.HttpStatus; import org.junit.Assert; @@ -23,7 +20,6 @@ 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; @@ -48,114 +44,12 @@ 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", header); + HttpResponse response = rh.executeGetRequest(ENDPOINT + "/rolesmapping"); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // check rolesmapping exists, new API - response = rh.executeGetRequest(ENDPOINT + "/rolesmapping", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping"); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(response.getContentType(), response.isJsonContentType()); @@ -167,8 +61,9 @@ void verifyGetForSuperAdmin(final Header[] header) throws Exception { // -- GET + // GET opendistro_security_role_starfleet, exists - response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(response.getContentType(), response.isJsonContentType()); Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); @@ -178,42 +73,55 @@ void verifyGetForSuperAdmin(final Header[] header) 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", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/nothinghthere", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // GET, new URL endpoint in security - response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(response.getContentType(), response.isJsonContentType()); // GET, new URL endpoint in security - response = rh.executeGetRequest(ENDPOINT + "/rolesmapping", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping", new Header[0]); 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", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(response.getBody().contains("\"hidden\":true")); - } - void verifyDeleteForSuperAdmin(final Header[] header, final boolean useAdminCert) throws Exception { + // 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; + // Non-existing role - HttpResponse response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/idonotexist", header); + response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/idonotexist", new Header[0]); 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", header); + response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // hidden role - response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", header); + response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", new Header[0]); 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", header); + response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); response = rh.executeGetRequest(ENDPOINT + "/configuration/rolesmapping"); rh.sendAdminCertificate = false; @@ -226,30 +134,32 @@ void verifyDeleteForSuperAdmin(final Header[] header, final boolean useAdminCert // checkWriteAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 1); // remove also opendistro_security_role_starfleet, poor picard has no mapping left - rh.sendAdminCertificate = useAdminCert; - response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet", header); + rh.sendAdminCertificate = true; + response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); rh.sendAdminCertificate = false; checkAllSfForbidden(); - } - void verifyPutForSuperAdmin(final Header[] header) throws Exception { + rh.sendAdminCertificate = true; + + // --- PUT + // put with empty mapping, must fail - HttpResponse response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", "", header); + response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", "", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); + 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"), header); + FileHelper.loadFile("restapi/rolesmapping_not_parseable.json"), new Header[0]); 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"), header); + FileHelper.loadFile("restapi/rolesmapping_invalid_keys.json"), new Header[0]); 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")); @@ -260,7 +170,7 @@ void verifyPutForSuperAdmin(final Header[] header) throws Exception { // wrong datatypes response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/rolesmapping_backendroles_captains_single_wrong_datatype.json"), header); + FileHelper.loadFile("restapi/rolesmapping_backendroles_captains_single_wrong_datatype.json"), new Header[0]); 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")); @@ -269,7 +179,7 @@ void verifyPutForSuperAdmin(final Header[] header) 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"), header); + FileHelper.loadFile("restapi/rolesmapping_hosts_single_wrong_datatype.json"), new Header[0]); 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")); @@ -278,7 +188,7 @@ void verifyPutForSuperAdmin(final Header[] header) 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"), header); + FileHelper.loadFile("restapi/rolesmapping_users_picard_single_wrong_datatype.json"), new Header[0]); 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")); @@ -289,83 +199,81 @@ void verifyPutForSuperAdmin(final Header[] header) 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"), header); + FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]); 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"), header); + FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/rolesmapping_all_access.json"), header); + FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]); Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - } - void verifyPatchForSuperAdmin(final Header[] header) throws Exception { + // -- PATCH // PATCH on non-existing resource - HttpResponse response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // PATCH read only resource, must be forbidden // SuperAdmin can patch read-only resource - response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", - "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\"] }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\"] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH hidden resource, must be not found, can be found by super admin - response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", - "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ " + - "\"foo\", \"bar\" ] }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ " + + "\"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH value of hidden flag, must fail with validation error - response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", - "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*")); // PATCH - response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", - "[{ \"op\": \"add\", \"path\": \"/backend_roles/-\", \"value\": \"spring\" }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", "[{ \"op\": \"add\", \"path\": \"/backend_roles/-\", \"value\": \"spring\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_vulcans", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); + settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); List 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 - response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", - "[{ \"op\": \"add\", \"path\": \"/imnothere/a\", \"value\": [ \"foo\", \"bar\" ] }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/imnothere/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); 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\" }]", header); + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_starfleet_library/description\", \"value\": \"foo\" }]", new Header[0]); 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\" ] }]", header); + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); 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 }]", header); + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_vulcans/hidden\", \"value\": true }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*")); // PATCH - response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", - "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": { \"backend_roles\":[\"vulcanadmin\"]} }]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": { \"backend_roles\":[\"vulcanadmin\"]} }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/bulknew1", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/bulknew1", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); permissions = settings.getAsList("bulknew1.backend_roles"); @@ -373,10 +281,45 @@ void verifyPatchForSuperAdmin(final Header[] header) throws Exception { Assert.assertTrue(permissions.contains("vulcanadmin")); // PATCH delete - response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", header); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/bulknew1", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/bulknew1", new Header[0]); 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 { @@ -391,13 +334,13 @@ private void checkAllSfForbidden() throws Exception { checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picard", "sf", "_doc", 1); } - private HttpResponse deleteAndputNewMapping(final Header[] header, final String fileName, final boolean useAdminCert) throws Exception { - rh.sendAdminCertificate = useAdminCert; + private HttpResponse deleteAndputNewMapping(String fileName) throws Exception { + rh.sendAdminCertificate = true; HttpResponse response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", - header); + new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); response = rh.executePutRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/"+fileName), header); + FileHelper.loadFile("restapi/"+fileName), new Header[0]); Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); rh.sendAdminCertificate = false; return response; @@ -412,142 +355,46 @@ 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" , header); + response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library" , new Header[0]); 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"), header); + FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]); 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\" }]", header); + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_role_starfleet_library", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]); 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\" }]", header); + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping", "[{ \"op\": \"add\", \"path\": \"/opendistro_security_role_starfleet_library/description\", \"value\": \"foo\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); // GET, rolesmapping is hidden, allowed for super admin - response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", header); + response = rh.executeGetRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // Delete hidden roles mapping - response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal" , header); + response = rh.executeDeleteRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal" , new Header[0]); 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"), header); + FileHelper.loadFile("restapi/rolesmapping_all_access.json"), new Header[0]); 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\" }]", header); + response = rh.executePatchRequest(ENDPOINT + "/rolesmapping/opendistro_security_internal", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]); 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\" }]", 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)); - } + 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()); - 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 deleted file mode 100644 index e2b649770a..0000000000 --- a/src/test/java/org/opensearch/security/dlic/rest/api/SslCertsApiTest.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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.http.Header; -import org.apache.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> 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"); - - - public String certsInfoEndpoint() { - return PLUGINS_PREFIX + "/api/ssl/certs"; - } - - public 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 c69cd845b0..cb844c7b62 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(133, settings.size()); + Assert.assertEquals(56, 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,57 +104,50 @@ public void testUserApi() throws Exception { rh.keystore = "restapi/kirk-keystore.jks"; rh.sendAdminCertificate = true; - // initial configuration - HttpResponse response = rh.executeGetRequest(ENDPOINT + "/" + CType.INTERNALUSERS.toLCString()); + // initial configuration, 6 users + 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(133, settings.size()); - verifyGet(); - verifyPut(); - verifyPatch(true); - // create index first - setupStarfleetIndex(); - verifyRoles(true); - } - - private void verifyGet(final Header... header) throws Exception { + Assert.assertEquals(56, settings.size()); // --- GET + // GET, user admin, exists - HttpResponse response = rh.executeGetRequest(ENDPOINT + "/internalusers/admin", header); + response = rh.executeGetRequest(ENDPOINT + "/internalusers/admin", new Header[0]); Assert.assertEquals(response.getBody(), HttpStatus.SC_OK, response.getStatusCode()); - Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); + 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", header); + response = rh.executeGetRequest(ENDPOINT + "/internalusers/nothinghthere", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // GET, new URL endpoint in security - response = rh.executeGetRequest(ENDPOINT + "/user/", header); + response = rh.executeGetRequest(ENDPOINT + "/user/", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // GET, new URL endpoint in security - response = rh.executeGetRequest(ENDPOINT + "/user", header); + response = rh.executeGetRequest(ENDPOINT + "/user", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - } - private void verifyPut(final Header... header) throws Exception { // -- PUT + // no username given - HttpResponse response = rh.executePutRequest(ENDPOINT + "/internalusers/", "{\"hash\": \"123\"}", header); + response = rh.executePutRequest(ENDPOINT + "/internalusers/", "{\"hash\": \"123\"}", new Header[0]); Assert.assertEquals(HttpStatus.SC_METHOD_NOT_ALLOWED, response.getStatusCode()); // Faulty JSON payload response = rh.executePutRequest(ENDPOINT + "/internalusers/nagilum", "{some: \"thing\" asd other: \"thing\"}", - header); + new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); + 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\"}", header); + response = rh.executePutRequest(ENDPOINT + "/internalusers/nagilum", "{some: \"thing\", other: \"thing\"}", + new Header[0]); 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 @@ -163,105 +156,102 @@ private void verifyPut(final Header... header) throws Exception { //Assert.assertTrue(settings.get(AbstractConfigurationValidator.INVALID_KEYS_KEY + ".keys").contains("other")); // Get hidden role - response = rh.executeGetRequest(ENDPOINT + "/internalusers/hide" , header); + response = rh.executeGetRequest(ENDPOINT + "/internalusers/hide" , new Header[0]); 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\"]}", header); + "[\"opendistro_security_hidden\"]}", new Header[0]); 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\"}", - header); + "\"hash\": \"123\"}", + new Header[0]); 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\"]}", - header); + new Header[0]); 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\"}", - header); + new Header[0]); 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 = sendAdminCert; - HttpResponse response = rh.executePatchRequest(ENDPOINT + "/internalusers/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers/imnothere", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // PATCH read only resource, must be forbidden, // but SuperAdmin can PATCH read-only resource - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers/sarek", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers/sarek", "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // PATCH hidden resource, must be not found, can be found for super admin - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers/q", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers/q", "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH value of hidden flag, must fail with validation error - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers/test", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers/test", "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", new Header[0]); 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 = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers/test", "[{ \"op\": \"add\", \"path\": \"/password\", \"value\": \"neu\" }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers/test", "[{ \"op\": \"add\", \"path\": \"/password\", \"value\": \"neu\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/internalusers/test", restAdminHeader); + response = rh.executeGetRequest(ENDPOINT + "/internalusers/test", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); + 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 = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/imnothere/a\", \"value\": [ \"foo\", \"bar\" ] }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/imnothere/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH read only resource, must be forbidden, // but SuperAdmin can PATCH read only resouce - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/sarek/description\", \"value\": \"foo\" }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/sarek/description\", \"value\": \"foo\" }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); rh.sendAdminCertificate = false; - response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/sarek/a\", \"value\": [ \"foo\", \"bar\" ] }]"); + response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/sarek/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusCode()); // PATCH hidden resource, must be bad request - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/q/a\", \"value\": [ \"foo\", \"bar\" ] }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/q/a\", \"value\": [ \"foo\", \"bar\" ] }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); // PATCH value of hidden flag, must fail with validation error - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/test/hidden\", \"value\": true }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/test/hidden\", \"value\": true }]", new Header[0]); 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 = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/internalusers", - "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": {\"password\": \"bla\", \"backend_roles\": [\"vulcan\"] } }]", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executePatchRequest(ENDPOINT + "/internalusers", "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": {\"password\": \"bla\", \"backend_roles\": [\"vulcan\"] } }]", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/internalusers/bulknew1", restAdminHeader); + response = rh.executeGetRequest(ENDPOINT + "/internalusers/bulknew1", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); Assert.assertFalse(settings.hasValue("bulknew1.password")); @@ -277,17 +267,17 @@ private void verifyPatch(final boolean sendAdminCert, Header... restAdminHeader) // add/update user, user is read only, forbidden // SuperAdmin can add read only users - rh.sendAdminCertificate = sendAdminCert; + rh.sendAdminCertificate = true; addUserWithHash("sarek", "$2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m", HttpStatus.SC_OK); // add/update user, user is hidden, forbidden, allowed for super admin - rh.sendAdminCertificate = sendAdminCert; + rh.sendAdminCertificate = true; addUserWithHash("q", "$2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m", HttpStatus.SC_OK); // add users - rh.sendAdminCertificate = sendAdminCert; + rh.sendAdminCertificate = true; addUserWithHash("nagilum", "$2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m", HttpStatus.SC_CREATED); @@ -295,20 +285,20 @@ private void verifyPatch(final boolean sendAdminCert, Header... restAdminHeader) checkGeneralAccess(HttpStatus.SC_OK, "nagilum", "nagilum"); // try remove user, no username - rh.sendAdminCertificate = sendAdminCert; - response = rh.executeDeleteRequest(ENDPOINT + "/internalusers", restAdminHeader); + rh.sendAdminCertificate = true; + response = rh.executeDeleteRequest(ENDPOINT + "/internalusers", new Header[0]); Assert.assertEquals(HttpStatus.SC_METHOD_NOT_ALLOWED, response.getStatusCode()); // try remove user, nonexisting user - response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/picard", restAdminHeader); + response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/picard", new Header[0]); Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); // try remove readonly user - response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/sarek", restAdminHeader); + response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/sarek", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); // try remove hidden user, allowed for super admin - response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/q", restAdminHeader); + response = rh.executeDeleteRequest(ENDPOINT + "/internalusers/q", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(response.getBody().contains("'q' deleted.")); // now really remove user @@ -319,7 +309,7 @@ private void verifyPatch(final boolean sendAdminCert, Header... restAdminHeader) checkGeneralAccess(HttpStatus.SC_UNAUTHORIZED, "nagilum", "nagilum"); // use password instead of hash - rh.sendAdminCertificate = sendAdminCert; + rh.sendAdminCertificate = true; addUserWithPassword("nagilum", "correctpassword", HttpStatus.SC_CREATED); rh.sendAdminCertificate = false; @@ -329,7 +319,7 @@ private void verifyPatch(final boolean sendAdminCert, Header... restAdminHeader) deleteUser("nagilum"); // Check unchanged password functionality - rh.sendAdminCertificate = sendAdminCert; + rh.sendAdminCertificate = true; // new user, password or hash is mandatory addUserWithoutPasswordOrHash("nagilum", new String[]{"starfleet"}, HttpStatus.SC_BAD_REQUEST); @@ -339,32 +329,35 @@ private void verifyPatch(final boolean sendAdminCert, Header... restAdminHeader) // 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", restAdminHeader); + response = rh.executeGetRequest(ENDPOINT + "/internalusers/nagilum", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); Assert.assertTrue(settings.get("nagilum.hash").equals("")); - } - private void verifyRoles(final boolean sendAdminCert, Header... header) throws Exception { + + // ROLES + // create index first + setupStarfleetIndex(); + // wrong datatypes in roles file - 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(); + 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(); 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 = sendAdminCert; - response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes.json"), header); + 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(); 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 = sendAdminCert; - response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes2.json"), header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes2.json"), new Header[0]); 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")); @@ -372,8 +365,8 @@ private void verifyRoles(final boolean sendAdminCert, Header... header) throws E Assert.assertTrue(settings.get("backend_roles") == null); rh.sendAdminCertificate = false; - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes3.json"), header); + rh.sendAdminCertificate = true; + response = rh.executePutRequest(ENDPOINT + "/internalusers/picard", FileHelper.loadFile("restapi/users_wrong_datatypes3.json"), new Header[0]); 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")); @@ -402,12 +395,12 @@ private void verifyRoles(final boolean sendAdminCert, Header... header) throws E checkReadAccess(HttpStatus.SC_OK, "picard", "picard", "sf", "_doc", 0); checkWriteAccess(HttpStatus.SC_CREATED, "picard", "picard", "sf", "_doc", 1); - rh.sendAdminCertificate = sendAdminCert; - response = rh.executeGetRequest(ENDPOINT + "/internalusers/picard", header); + rh.sendAdminCertificate = true; + response = rh.executeGetRequest(ENDPOINT + "/internalusers/picard", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); Assert.assertEquals("", settings.get("picard.hash")); - List roles = settings.getAsList("picard.backend_roles"); + roles = settings.getAsList("picard.backend_roles"); Assert.assertNotNull(roles); Assert.assertEquals(2, roles.size()); Assert.assertTrue(roles.contains("starfleet")); @@ -418,46 +411,10 @@ private void verifyRoles(final boolean sendAdminCert, Header... header) throws E // check tabs in json - response = rh.executePutRequest(ENDPOINT + "/internalusers/userwithtabs", "\t{\"hash\": \t \"123\"\t} ", header); + response = rh.executePutRequest(ENDPOINT + "/internalusers/userwithtabs", "\t{\"hash\": \t \"123\"\t} ", new Header[0]); 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 { @@ -479,7 +436,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(133, settings.size()); + Assert.assertEquals(56, settings.size()); addUserWithPassword("tooshoort", "", HttpStatus.SC_BAD_REQUEST); addUserWithPassword("tooshoort", "123", HttpStatus.SC_BAD_REQUEST); @@ -559,7 +516,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(133, settings.size()); + Assert.assertEquals(56, settings.size()); addUserWithPassword(".my.dotuser0", "$2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m", HttpStatus.SC_CREATED); diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacySslCertsApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacySslCertsApiTest.java deleted file mode 100644 index 5d1c3ae538..0000000000 --- a/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacySslCertsApiTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.legacy; - -import org.opensearch.security.dlic.rest.api.SslCertsApiTest; - -import static org.opensearch.security.OpenSearchSecurityPlugin.LEGACY_OPENDISTRO_PREFIX; - -public class LegacySslCertsApiTest extends SslCertsApiTest { - - @Override - public String certsInfoEndpoint() { - return LEGACY_OPENDISTRO_PREFIX + "/api/ssl/certs"; - } - - @Override - public 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/securityconf/SecurityRolesPermissionsTest.java b/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java deleted file mode 100644 index 010b453b85..0000000000 --- a/src/test/java/org/opensearch/security/securityconf/SecurityRolesPermissionsTest.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright 2015-2018 _floragunn_ GmbH - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * 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.securityconf; - -import java.io.IOException; -import java.util.AbstractMap.SimpleEntry; -import java.util.Arrays; -import java.util.Collection; -import java.util.Locale; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import org.opensearch.common.settings.Settings; -import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.dlic.rest.api.Endpoint; -import org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.PermissionBuilder; -import org.opensearch.security.securityconf.impl.CType; -import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; - -import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.CERTS_INFO_ACTION; -import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.ENDPOINTS_WITH_PERMISSIONS; -import static org.opensearch.security.dlic.rest.api.RestApiAdminPrivilegesEvaluator.RELOAD_CERTS_ACTION; - -public class SecurityRolesPermissionsTest { - - static final Map NO_REST_ADMIN_PERMISSIONS_ROLES = - ImmutableMap.builder() - .put( - "all_access", - role("*")) - .put( - "all_cluster_and_indices", - role("custer:*", "indices:*") - ).build(); - - static final Map REST_ADMIN_PERMISSIONS_FULL_ACCESS_ROLES = - ImmutableMap.builder() - .put( - "security_rest_api_full_access", - role(allRestApiPermissions())) - .put( - "security_rest_api_full_access_with_star", - role("restapi:admin/*")) - .build(); - - - static String restAdminApiRoleName(final String endpoint) { - return String.format("security_rest_api_%s_only", endpoint); - } - - static final Map REST_ADMIN_PERMISSIONS_ROLES = - ENDPOINTS_WITH_PERMISSIONS - .entrySet() - .stream() - .flatMap(e -> { - final String endpoint = e.getKey().name().toLowerCase(Locale.ROOT); - final PermissionBuilder pb = e.getValue(); - if (e.getKey() == Endpoint.SSL) { - return Stream.of( - new SimpleEntry<>( - restAdminApiRoleName(CERTS_INFO_ACTION), - role(pb.build(CERTS_INFO_ACTION)) - ), - new SimpleEntry<>( - restAdminApiRoleName(RELOAD_CERTS_ACTION), - role(pb.build(RELOAD_CERTS_ACTION)) - ) - ); - } else { - return Stream.of( - new SimpleEntry<>(restAdminApiRoleName(endpoint), role(pb.build())) - ); - } - }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - - static ObjectNode role(final String... clusterPermissions) { - final ArrayNode clusterPermissionsArrayNode = DefaultObjectMapper.objectMapper.createArrayNode(); - Arrays.stream(clusterPermissions).forEach(clusterPermissionsArrayNode::add); - return DefaultObjectMapper.objectMapper - .createObjectNode() - .put("reserved", true) - .set("cluster_permissions", clusterPermissionsArrayNode); - } - - static String[] allRestApiPermissions() { - return ENDPOINTS_WITH_PERMISSIONS - .entrySet() - .stream() - .flatMap(entry -> { - if (entry.getKey() == Endpoint.SSL) { - return Stream.of(entry.getValue().build(CERTS_INFO_ACTION), entry.getValue().build(RELOAD_CERTS_ACTION)); - } else { - return Stream.of(entry.getValue().build()); - } - }).toArray(String[]::new); - } - - final ConfigModel configModel; - - public SecurityRolesPermissionsTest() throws IOException { - this.configModel = - new ConfigModelV7( - createRolesConfig(), - createRoleMappingsConfig(), - createActionGroupsConfig(), - createTenantsConfig(), - Mockito.mock(DynamicConfigModel.class), - Settings.EMPTY - ); - } - - @Test - public void hasNoExplicitClusterPermissionPermissionForRestAdmin() { - for (final String role : NO_REST_ADMIN_PERMISSIONS_ROLES.keySet()) { - final SecurityRoles securityRolesForRole = configModel.getSecurityRoles().filter(ImmutableSet.of(role)); - for (final Map.Entry entry : ENDPOINTS_WITH_PERMISSIONS.entrySet()) { - final Endpoint endpoint = entry.getKey(); - final PermissionBuilder permissionBuilder = entry.getValue(); - if (endpoint == Endpoint.SSL) { - Assert.assertFalse( - endpoint.name(), - securityRolesForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build(CERTS_INFO_ACTION)) - ); - Assert.assertFalse( - endpoint.name(), - securityRolesForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build(RELOAD_CERTS_ACTION)) - ); - } else { - Assert.assertFalse( - endpoint.name(), - securityRolesForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build()) - ); - } - } - } - } - - @Test - public void hasExplicitClusterPermissionPermissionForRestAdminWitFullAccess() { - for (final String role : REST_ADMIN_PERMISSIONS_FULL_ACCESS_ROLES.keySet()) { - final SecurityRoles securityRolesForRole = configModel.getSecurityRoles().filter(ImmutableSet.of(role)); - for (final Map.Entry entry : ENDPOINTS_WITH_PERMISSIONS.entrySet()) { - final Endpoint endpoint = entry.getKey(); - final PermissionBuilder permissionBuilder = entry.getValue(); - if (endpoint == Endpoint.SSL) { - Assert.assertTrue(endpoint.name() + "/" + CERTS_INFO_ACTION, securityRolesForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build(CERTS_INFO_ACTION))); - Assert.assertTrue(endpoint.name() + "/" + CERTS_INFO_ACTION, securityRolesForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build(RELOAD_CERTS_ACTION))); - } else { - Assert.assertTrue(endpoint.name(), securityRolesForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build())); - } - } - } - } - - @Test - public void hasExplicitClusterPermissionPermissionForRestAdmin() { - // verify all endpoint except SSL - final Collection noSslEndpoints = - ENDPOINTS_WITH_PERMISSIONS.keySet().stream() - .filter(e -> e != Endpoint.SSL).collect(Collectors.toList()); - for (final Endpoint endpoint : noSslEndpoints) { - final String permission = ENDPOINTS_WITH_PERMISSIONS.get(endpoint).build(); - final SecurityRoles allowOnePermissionRole = - configModel.getSecurityRoles().filter( - ImmutableSet.of(restAdminApiRoleName(endpoint.name().toLowerCase(Locale.ROOT)))); - Assert.assertTrue(endpoint.name(), allowOnePermissionRole.hasExplicitClusterPermissionPermission(permission)); - assertHasNoPermissionsForRestApiAdminOnePermissionRole( - endpoint, - allowOnePermissionRole - ); - } - // verify SSL endpoint with 2 actions - for (final String sslAction : ImmutableSet.of(CERTS_INFO_ACTION, RELOAD_CERTS_ACTION)) { - final SecurityRoles sslAllowRole = - configModel.getSecurityRoles().filter(ImmutableSet.of(restAdminApiRoleName(sslAction))); - final PermissionBuilder permissionBuilder = ENDPOINTS_WITH_PERMISSIONS.get(Endpoint.SSL); - Assert.assertTrue( - Endpoint.SSL + "/" + sslAction, - sslAllowRole.hasExplicitClusterPermissionPermission(permissionBuilder.build(sslAction)) - ); - assertHasNoPermissionsForRestApiAdminOnePermissionRole(Endpoint.SSL, sslAllowRole); - } - } - - void assertHasNoPermissionsForRestApiAdminOnePermissionRole(final Endpoint allowEndpoint, final SecurityRoles allowOnlyRoleForRole) { - final Collection noPermissionEndpoints = - ENDPOINTS_WITH_PERMISSIONS.keySet().stream() - .filter(e -> e != allowEndpoint) - .collect(Collectors.toList()); - for (final Endpoint endpoint : noPermissionEndpoints) { - final PermissionBuilder permissionBuilder = ENDPOINTS_WITH_PERMISSIONS.get(endpoint); - if (endpoint == Endpoint.SSL) { - Assert.assertFalse( - endpoint.name(), - allowOnlyRoleForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build(CERTS_INFO_ACTION))); - Assert.assertFalse( - endpoint.name(), - allowOnlyRoleForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build(RELOAD_CERTS_ACTION))); - } else { - Assert.assertFalse( - endpoint.name(), - allowOnlyRoleForRole.hasExplicitClusterPermissionPermission(permissionBuilder.build())); - } - } - } - - static ObjectNode meta(final String type) { - return DefaultObjectMapper.objectMapper - .createObjectNode() - .put("type", type) - .put("config_version", 2); - } - - static SecurityDynamicConfiguration createRolesConfig() throws IOException { - final ObjectNode rolesNode = DefaultObjectMapper.objectMapper.createObjectNode(); - rolesNode.set("_meta", meta("roles")); - NO_REST_ADMIN_PERMISSIONS_ROLES.forEach(rolesNode::set); - REST_ADMIN_PERMISSIONS_FULL_ACCESS_ROLES.forEach(rolesNode::set); - REST_ADMIN_PERMISSIONS_ROLES.forEach(rolesNode::set); - return SecurityDynamicConfiguration.fromNode(rolesNode, CType.ROLES, 2, 0, 0); - } - - static SecurityDynamicConfiguration createRoleMappingsConfig() throws IOException { - final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); - metaNode.set("_meta", meta("rolesmapping")); - return SecurityDynamicConfiguration.fromNode(metaNode, CType.ROLESMAPPING, 2, 0, 0); - } - - static SecurityDynamicConfiguration createActionGroupsConfig() throws IOException { - final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); - metaNode.set("_meta", meta("actiongroups")); - return SecurityDynamicConfiguration.fromNode(metaNode, CType.ACTIONGROUPS, 2, 0, 0); - } - - static SecurityDynamicConfiguration createTenantsConfig() throws IOException { - final ObjectNode metaNode = DefaultObjectMapper.objectMapper.createObjectNode(); - metaNode.set("_meta", meta("tenants")); - return SecurityDynamicConfiguration.fromNode(metaNode, CType.TENANTS, 2, 0, 0); - } - -} diff --git a/src/test/java/org/opensearch/security/ssl/SecuritySSLCertsInfoActionTests.java b/src/test/java/org/opensearch/security/ssl/SecuritySSLCertsInfoActionTests.java new file mode 100644 index 0000000000..c9618e6463 --- /dev/null +++ b/src/test/java/org/opensearch/security/ssl/SecuritySSLCertsInfoActionTests.java @@ -0,0 +1,110 @@ +/* + * 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.ssl; + +import java.util.List; +import java.util.Map; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import net.minidev.json.JSONObject; +import org.junit.Assert; +import org.junit.Test; + +import org.opensearch.common.settings.Settings; +import org.opensearch.security.ssl.util.SSLConfigConstants; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.test.SingleClusterTest; +import org.opensearch.security.test.helper.file.FileHelper; +import org.opensearch.security.test.helper.rest.RestHelper; + +import static org.opensearch.security.OpenSearchSecurityPlugin.LEGACY_OPENDISTRO_PREFIX; +import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX; + +public class SecuritySSLCertsInfoActionTests extends SingleClusterTest { + private final List> NODE_CERT_DETAILS = 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" + )); + + @Test + public void testCertInfo_Legacy_Pass() throws Exception { + certInfo_Pass(LEGACY_OPENDISTRO_PREFIX + "/api/ssl/certs"); + } + + @Test + public void testCertInfo_Pass() throws Exception { + certInfo_Pass(PLUGINS_PREFIX + "/api/ssl/certs"); + } + + public void certInfo_Pass(final String endpoint) throws Exception { + initTestCluster(); + final RestHelper rh = restHelper(); + rh.enableHTTPClientSSL = true; + rh.trustHTTPServerCertificate = true; + rh.sendAdminCertificate = true; + rh.keystore = "kirk-keystore.jks"; + + final RestHelper.HttpResponse transportInfoRestResponse = rh.executeGetRequest(endpoint); + JSONObject expectedJsonResponse = new JSONObject(); + expectedJsonResponse.appendField("http_certificates_list", NODE_CERT_DETAILS); + expectedJsonResponse.appendField("transport_certificates_list", NODE_CERT_DETAILS); + Assert.assertEquals(expectedJsonResponse.toString(), transportInfoRestResponse.getBody()); + } + + @Test + public void testCertInfoFail_Legacy_NonAdmin() throws Exception { + certInfoFail_NonAdmin(LEGACY_OPENDISTRO_PREFIX + "/api/ssl/certs"); + } + + @Test + public void testCertInfoFail_NonAdmin() throws Exception { + certInfoFail_NonAdmin(PLUGINS_PREFIX + "/api/ssl/certs"); + } + + public void certInfoFail_NonAdmin(final String endpoint) throws Exception { + initTestCluster(); + final RestHelper rh = restHelper(); + rh.enableHTTPClientSSL = true; + rh.trustHTTPServerCertificate = true; + rh.sendAdminCertificate = true; + rh.keystore = "spock-keystore.jks"; + + final RestHelper.HttpResponse transportInfoRestResponse = rh.executeGetRequest(endpoint); + Assert.assertEquals(401, transportInfoRestResponse.getStatusCode()); // Forbidden for non-admin + Assert.assertEquals("Unauthorized", transportInfoRestResponse.getStatusReason()); + } + + /** + * Helper method to initialize test cluster for CertInfoAction Tests + */ + private void initTestCluster() throws Exception { + final Settings settings = Settings.builder() + .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENABLED, true) + .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PEMCERT_FILEPATH, FileHelper. getAbsoluteFilePathFromClassPath("ssl/node-0.crt.pem")) + .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PEMKEY_FILEPATH, FileHelper. getAbsoluteFilePathFromClassPath("ssl/node-0.key.pem")) + .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PEMTRUSTEDCAS_FILEPATH, FileHelper. getAbsoluteFilePathFromClassPath("ssl/root-ca.pem")) + .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENFORCE_HOSTNAME_VERIFICATION, false) + .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENFORCE_HOSTNAME_VERIFICATION_RESOLVE_HOST_NAME, false) + .put(SSLConfigConstants.SECURITY_SSL_HTTP_ENABLED, true) + .put(SSLConfigConstants.SECURITY_SSL_HTTP_PEMCERT_FILEPATH, FileHelper.getAbsoluteFilePathFromClassPath("ssl/node-0.crt.pem")) + .put(SSLConfigConstants.SECURITY_SSL_HTTP_PEMKEY_FILEPATH, FileHelper.getAbsoluteFilePathFromClassPath("ssl/node-0.key.pem")) + .put(SSLConfigConstants.SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH, FileHelper.getAbsoluteFilePathFromClassPath("ssl/root-ca.pem")) + .put(ConfigConstants.SECURITY_SSL_CERT_RELOAD_ENABLED, true) + .build(); + setup(settings); + } +} diff --git a/src/test/java/org/opensearch/security/ssl/SecuritySSLReloadCertsActionTests.java b/src/test/java/org/opensearch/security/ssl/SecuritySSLReloadCertsActionTests.java index f7684a9c51..ea78ee043c 100644 --- a/src/test/java/org/opensearch/security/ssl/SecuritySSLReloadCertsActionTests.java +++ b/src/test/java/org/opensearch/security/ssl/SecuritySSLReloadCertsActionTests.java @@ -127,6 +127,32 @@ 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(); @@ -143,6 +169,24 @@ 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 658d3f3aa1..0049ab8c86 100644 --- a/src/test/resources/restapi/internal_users.yml +++ b/src/test/resources/restapi/internal_users.yml @@ -61,69 +61,3 @@ 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 6deb194e9b..d82382e4f6 100644 --- a/src/test/resources/restapi/roles.yml +++ b/src/test/resources/restapi/roles.yml @@ -393,51 +393,3 @@ 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 a87287d5ff..8c46942854 100644 --- a/src/test/resources/restapi/roles_mapping.yml +++ b/src/test/resources/restapi/roles_mapping.yml @@ -185,16 +185,6 @@ 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: @@ -216,47 +206,3 @@ 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 e9b724e342..93b510dd16 100644 --- a/src/test/resources/restapi/roles_tenants.yml +++ b/src/test/resources/restapi/roles_tenants.yml @@ -2,13 +2,3 @@ _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"