Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extension registration #2750

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public enum Endpoint {
ROLES,
ROLESMAPPING,
INTERNALUSERS,
EXTENSIONREGISTRATION,
SYSTEMINFO,
PERMISSIONSINFO,
AUTHTOKEN,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* 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.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;

import org.opensearch.action.index.IndexRequest;
import org.opensearch.client.Client;
import org.opensearch.cluster.service.ClusterService;
import org.opensearch.common.bytes.BytesReference;
import org.opensearch.common.inject.Inject;
import org.opensearch.common.settings.Settings;
import org.opensearch.rest.RestChannel;
import org.opensearch.rest.RestController;
import org.opensearch.rest.RestRequest;
import org.opensearch.rest.RestRequest.Method;
import org.opensearch.security.DefaultObjectMapper;
import org.opensearch.security.auditlog.AuditLog;
import org.opensearch.security.configuration.AdminDNs;
import org.opensearch.security.configuration.ConfigurationRepository;
import org.opensearch.security.dlic.rest.validation.AbstractConfigurationValidator;
import org.opensearch.security.dlic.rest.validation.ExtensionRegistrationValidator;
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.security.support.SecurityJsonNode;
import org.opensearch.threadpool.ThreadPool;

import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix;

public class ExtensionRegistrationApiAction extends AbstractApiAction {
private static final List<Route> routes = addRoutesPrefix(ImmutableList.of(
new Route(Method.GET, "/extensions/register"),
new Route(Method.DELETE, "/extensions/register"),
new Route(Method.PUT, "/extensions/register"),
new Route(Method.PATCH, "/extension/register")
));

//Sample Request
// {
// "unique_id": "hello_world",
// "description": "Extension that greets the user",
// "developer": "messages",
// "indices": "messages",
// "protected_indices": null,
// "endpoints": "hello, goodbye",
// "protected_endpoints": "/update/{name}"
//}

@Inject
public ExtensionRegistrationApiAction(final Settings settings, final Path configPath, final RestController controller,
final Client client, final AdminDNs adminDNs, final ConfigurationRepository cl,
final ClusterService cs, final PrincipalExtractor principalExtractor, final PrivilegesEvaluator evaluator,
ThreadPool threadPool, AuditLog auditLog) {
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<Route> routes() {
return routes;
}

@Override
protected Endpoint getEndpoint() {
return Endpoint.EXTENSIONREGISTRATION;
}

@Override
protected void handleGet(final RestChannel channel, RestRequest request, Client client, final JsonNode content)
throws IOException{

createdResponse(channel, " updated");

;
}

@Override
protected void handlePut(RestChannel channel, final RestRequest request, final Client client, final JsonNode content) throws IOException {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The request body arrives here empty .

An im missing something obvious?

final String uniqueId = request.param("unique_id");
final String description = request.param("description");
final String developer = request.param("developer");
final List<String> indices = Arrays.asList(request.param("indices"));
final List<String> protected_indices = Arrays.asList(request.param("protected_indices"));
final List<String> endpoints = Arrays.asList(request.param("endpoints"));
final List<String> protected_endpoints = Arrays.asList(request.param("protected_endpoints"));

final ObjectNode contentAsNode = (ObjectNode) content;

contentAsNode.put("uniqueId", uniqueId);
contentAsNode.put("description", description);
contentAsNode.put("developer", developer);
contentAsNode.put("indices", indices.toString());
contentAsNode.put("protected_indices", protected_indices.toString());
contentAsNode.put("endpoints", endpoints.toString());
contentAsNode.put("protected_endpoints", protected_endpoints.toString());

client.index(new IndexRequest("registered_extensions").source(contentAsNode));

if("allGood" == "allGood"){
generateAuthToken();
createdResponse(channel, "Extension " + uniqueId + " was Created");
}

}

private boolean save(RestRequest request) {

return true;
}

@Override
protected void filter(SecurityDynamicConfiguration<?> builder) {
super.filter(builder);
// replace password hashes in addition. We must not remove them from the
// Builder since this would remove users completely if they
// do not have any addition properties like roles or attributes
builder.clearHashes();
}

private String generateAuthToken(){
return "bearer: 9999999999999999";
}

@Override
protected String getResourceName() {
return "extensionsregistry";
}

@Override
protected CType getConfigName() {
return CType.CONFIG;
}

@Override
protected AbstractConfigurationValidator getValidator(RestRequest request, BytesReference ref, Object... params) {
return new ExtensionRegistrationValidator(request, isSuperAdmin(), ref, this.settings, params);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public static Collection<RestHandler> getHandler(final Settings settings,
handlers.add(new NodesDnApiAction(settings, configPath, controller, client, adminDns, cr, cs, principalExtractor, evaluator, threadPool, auditLog));
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 ExtensionRegistrationApiAction(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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.validation;

import org.opensearch.common.bytes.BytesReference;
import org.opensearch.common.settings.Settings;
import org.opensearch.rest.RestRequest;

/**
* Validator for Internal Users Api Action.
*/
public class ExtensionRegistrationValidator extends AbstractConfigurationValidator {

public ExtensionRegistrationValidator(final RestRequest request, boolean isSuperAdmin, BytesReference ref, final Settings opensearchSettings,
Object... param) {
super(request, ref, opensearchSettings, param);
allowedKeys.put("unique_id", DataType.STRING);
allowedKeys.put("description", DataType.STRING);
allowedKeys.put("developer", DataType.STRING);
allowedKeys.put("indices", DataType.STRING);
allowedKeys.put("protected_indices", DataType.STRING);
allowedKeys.put("endpoints", DataType.STRING);
allowedKeys.put("protected_endpoints", DataType.STRING);
}

@Override
public boolean validate() {
//TODO
return true;


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* 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 org.apache.hc.core5.http.HttpStatus;
import org.junit.Assert;
import org.junit.Test;

import org.opensearch.common.settings.Settings;
import org.opensearch.rest.RestStatus;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.system_indices.SystemIndicesTests;
import org.opensearch.security.test.DynamicSecurityConfig;
import org.opensearch.security.test.helper.file.FileHelper;
import org.opensearch.security.test.helper.rest.RestHelper;

import static org.junit.Assert.assertEquals;
import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX;

public class ExtensionRegistrationApiActionTest extends SystemIndicesTests {
private final String ENDPOINT = PLUGINS_PREFIX + "/api/extensions/register";

//Sample Request
// {
// "unique_id": "hello_world",
// "description": "Extension that greets the user",
// "developer": "messages",
// "indices": "messages",
// "protected_indices": null,
// "endpoints": "hello, goodbye",
// "protected_endpoints": "/update/{name}"
//}

private final String correctExtensionRequest = "{\"unique_id\":\"hello_world\",\"indices\":\"messages\",\"protected_indices\":\"null\",\"endpoints\":\"hello, goodbye\",\"protected_endpoints\":\"/update/{name}\"}\n";

private final String wrongExtensionRequest = " {\n" + " \"indices\": \"messages\",\n" + " \"protected_indices\": {},\n" + " \"endpoints\": \"/hello, /goodbye\",\n" + " \"protected_endpoints\": \"/update/{name}\"\n" + " }";

private void setupSettingsWithSsl() throws Exception {

Settings systemIndexSettings = Settings.builder()
.put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, false)
.put("plugins.security.ssl.http.enabled",true)
.put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks"))
.put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks"))
.put("path.repo", repositoryPath.getRoot().getAbsolutePath())
.build();
setup(Settings.EMPTY,
new DynamicSecurityConfig()
.setConfig("config_system_indices.yml")
.setSecurityRoles("roles_system_indices.yml")
.setSecurityInternalUsers("internal_users_system_indices.yml")
.setSecurityRolesMapping("roles_mapping_system_indices.yml"),
systemIndexSettings,
true);
}
private RestHelper keyStoreRestHelper() {
RestHelper restHelper = restHelper();
restHelper.keystore = "kirk-keystore.jks";
restHelper.enableHTTPClientSSL = true;
restHelper.trustHTTPServerCertificate = true;
restHelper.sendAdminCertificate = true;
return restHelper;
}

private RestHelper sslRestHelper() {
RestHelper restHelper = restHelper();
restHelper.enableHTTPClientSSL = true;
return restHelper;
}
@Test
public void CorrectExtensionRegistrationShouldReturnCreatedTest() throws Exception {
setupSettingsWithSsl();

RestHelper keyStoreRestHelper = keyStoreRestHelper();
RestHelper sslRestHelper = sslRestHelper();

//as Superadmin
// RestHelper.HttpResponse responsea = keyStoreRestHelper.executeGetRequest( ENDPOINT, correctExtensionRequest);
// assertEquals(RestStatus.CREATED.getStatus(), responsea.getStatusCode());

RestHelper.HttpResponse responsea = keyStoreRestHelper.executePutRequest( ENDPOINT, correctExtensionRequest);
assertEquals(RestStatus.CREATED.getStatus(), responsea.getStatusCode());

//as admin
// responsea = sslRestHelper.executeGetRequest( ENDPOINT, indexSettings, allAccessUserHeader);
// assertEquals(RestStatus.CREATED.getStatus(), responsea.getStatusCode());
//
// responsea = sslRestHelper.executePutRequest( ENDPOINT, indexSettings, allAccessUserHeader);
// assertEquals(RestStatus.CREATED.getStatus(), responsea.getStatusCode());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX;

/**
* Test for opendistro system indices, to restrict configured indices access to adminDn
Expand Down