diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f809f1f83c88..d16e3320f780a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Add getter for path field in NestedQueryBuilder ([#4636](https://github.com/opensearch-project/OpenSearch/pull/4636)) - Allow mmap to use new JDK-19 preview APIs in Apache Lucene 9.4+ ([#5151](https://github.com/opensearch-project/OpenSearch/pull/5151)) - Add events correlation engine plugin ([#6854](https://github.com/opensearch-project/OpenSearch/issues/6854)) +- Add support for ignoring missing Javadoc on generated code using annotation ([#7604](https://github.com/opensearch-project/OpenSearch/pull/7604)) +- Add partial results support for concurrent segment search ([#8306](https://github.com/opensearch-project/OpenSearch/pull/8306)) +- Pass OnBehalfOfToken in RestSendToExtensionAction ([#8679](https://github.com/opensearch-project/OpenSearch/pull/8679)) ### Dependencies - Bump `log4j-core` from 2.18.0 to 2.19.0 diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java index c3c08b1359aaa..77cab13880c27 100644 --- a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java @@ -8,15 +8,15 @@ package org.opensearch.identity.shiro; -import org.opensearch.identity.Subject; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.mgt.SecurityManager; +import org.opensearch.common.settings.Settings; +import org.opensearch.identity.Subject; import org.opensearch.identity.tokens.TokenManager; import org.opensearch.plugins.IdentityPlugin; -import org.opensearch.common.settings.Settings; import org.opensearch.plugins.Plugin; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.mgt.SecurityManager; /** * Identity implementation with Shiro diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenManager.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenManager.java index 110095a5cd4ef..f388a9eaf7bbd 100644 --- a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenManager.java +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenManager.java @@ -20,8 +20,11 @@ import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; +import org.opensearch.OpenSearchSecurityException; import org.opensearch.common.Randomness; import org.opensearch.identity.IdentityService; +import org.opensearch.identity.Subject; +import org.opensearch.identity.noop.NoopSubject; import org.opensearch.identity.tokens.AuthToken; import org.opensearch.identity.tokens.BasicAuthToken; import org.opensearch.identity.tokens.TokenManager; @@ -29,6 +32,7 @@ import org.passay.EnglishCharacterData; import org.passay.PasswordGenerator; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.opensearch.identity.noop.NoopTokenManager.NOOP_AUTH_TOKEN; /** * Extracts Shiro's {@link AuthenticationToken} from different types of auth headers @@ -51,15 +55,16 @@ public Optional translateAuthToken(org.opensearch.identity. final BasicAuthToken basicAuthToken = (BasicAuthToken) authenticationToken; return Optional.of(new UsernamePasswordToken(basicAuthToken.getUser(), basicAuthToken.getPassword())); } - return Optional.empty(); } @Override - public AuthToken issueToken(String audience) { + public AuthToken issueOnBehalfOfToken(Map claims) { String password = generatePassword(); - final byte[] rawEncoded = Base64.getEncoder().encode((audience + ":" + password).getBytes(UTF_8)); + final byte[] rawEncoded = Base64.getEncoder().encode((claims.get("aud") + ":" + password).getBytes(UTF_8)); // Make a new + // ShiroSubject w/ + // audience as name final String usernamePassword = new String(rawEncoded, UTF_8); final String header = "Basic " + usernamePassword; BasicAuthToken token = new BasicAuthToken(header); @@ -68,6 +73,16 @@ public AuthToken issueToken(String audience) { return token; } + @Override + public AuthToken issueServiceAccountToken(String extensionUniqueId) throws OpenSearchSecurityException { + return NOOP_AUTH_TOKEN; + } + + @Override + public Subject authenticateToken(AuthToken authToken) { + return new NoopSubject(); + } + public boolean validateToken(AuthToken token) { if (token instanceof BasicAuthToken) { final BasicAuthToken basicAuthToken = (BasicAuthToken) token; diff --git a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java index 540fed368aeda..e90457df41ce6 100644 --- a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java +++ b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java @@ -8,6 +8,8 @@ package org.opensearch.identity.shiro; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; @@ -16,6 +18,7 @@ import org.opensearch.identity.tokens.AuthToken; import org.opensearch.identity.tokens.BasicAuthToken; import org.opensearch.identity.tokens.BearerAuthToken; +import org.opensearch.identity.tokens.StandardTokenClaims; import org.opensearch.test.OpenSearchTestCase; import org.passay.CharacterCharacteristicsRule; import org.passay.CharacterRule; @@ -31,12 +34,10 @@ public class AuthTokenHandlerTests extends OpenSearchTestCase { private ShiroTokenManager shiroAuthTokenHandler; - private NoopTokenManager noopTokenManager; @Before public void testSetup() { shiroAuthTokenHandler = new ShiroTokenManager(); - noopTokenManager = new NoopTokenManager(); } public void testShouldExtractBasicAuthTokenSuccessfully() { @@ -144,4 +145,30 @@ public void testGeneratedPasswordContents() { validator.validate(data); } + public void testIssueOnBehalfOfTokenFromClaims() { + Map claims = new HashMap<>(); + claims.put("aud", "test"); + BasicAuthToken authToken = (BasicAuthToken) shiroAuthTokenHandler.issueOnBehalfOfToken(claims); + assertTrue(authToken instanceof BasicAuthToken); + UsernamePasswordToken translatedToken = (UsernamePasswordToken) shiroAuthTokenHandler.translateAuthToken(authToken).get(); + assertEquals(authToken.getPassword(), new String(translatedToken.getPassword())); + assertTrue(shiroAuthTokenHandler.getShiroTokenPasswordMap().containsKey(authToken)); + assertEquals(shiroAuthTokenHandler.getShiroTokenPasswordMap().get(authToken), new String(translatedToken.getPassword())); + } + + public void testTokenNoopIssuance() { + NoopTokenManager tokenManager = new NoopTokenManager(); + AuthToken token = tokenManager.issueOnBehalfOfToken(Map.of("test", "test")); + assertTrue(token instanceof AuthToken); + } + + public void testStandardTokenClaims() { + assertEquals(StandardTokenClaims.AUDIENCE.getName(), "aud"); + assertEquals(StandardTokenClaims.ISSUED_AT.getName(), "iat"); + assertEquals(StandardTokenClaims.ISSUER.getName(), "iss"); + assertEquals(StandardTokenClaims.EXPIRATION_TIME.getName(), "exp"); + assertEquals(StandardTokenClaims.JWT_ID.getName(), "jti"); + assertEquals(StandardTokenClaims.NOT_BEFORE.getName(), "nbf"); + assertEquals(StandardTokenClaims.SUBJECT.getName(), "sub"); + } } diff --git a/server/src/main/java/org/opensearch/common/settings/Settings.java b/server/src/main/java/org/opensearch/common/settings/Settings.java index a7649a1cd22c5..3c758d79ae407 100644 --- a/server/src/main/java/org/opensearch/common/settings/Settings.java +++ b/server/src/main/java/org/opensearch/common/settings/Settings.java @@ -267,6 +267,20 @@ public String get(String setting, String defaultValue) { return retVal == null ? defaultValue : retVal; } + /** + * Returns a setting value based on the setting key. + */ + public Settings getNestedSettings(String key) { + return (Settings) settings.get(key); + } + + /** + * Returns a setting value based on the setting key. + */ + public List getNestedListOfSettings(String key) { + return (List) settings.get(key); + } + /** * Returns the setting value (as float) associated with the setting key. If it does not exists, * returns the default value provided. @@ -663,6 +677,7 @@ private static void fromXContent(XContentParser parser, StringBuilder keyBuilder fromXContent(parser, keyBuilder, builder, allowNullValues); } else if (parser.currentToken() == XContentParser.Token.START_ARRAY) { List list = new ArrayList<>(); + List listOfObjects = new ArrayList<>(); while (parser.nextToken() != XContentParser.Token.END_ARRAY) { if (parser.currentToken() == XContentParser.Token.VALUE_STRING) { list.add(parser.text()); @@ -671,12 +686,19 @@ private static void fromXContent(XContentParser parser, StringBuilder keyBuilder } else if (parser.currentToken() == XContentParser.Token.VALUE_BOOLEAN) { list.add(String.valueOf(parser.text())); } else { - throw new IllegalStateException("only value lists are allowed in serialized settings"); + listOfObjects.add(fromXContent(parser, true, false)); + // throw new IllegalStateException("only value lists are allowed in serialized settings"); } } String key = keyBuilder.toString(); validateValue(key, list, parser, allowNullValues); builder.putList(key, list); + if (!listOfObjects.isEmpty()) { + builder.putListOfObjects(key, listOfObjects); + } + if (!list.isEmpty() && !listOfObjects.isEmpty()) { + throw new IllegalStateException("list cannot contain both values and objects"); + } } else if (parser.currentToken() == XContentParser.Token.VALUE_NULL) { String key = keyBuilder.toString(); validateValue(key, null, parser, allowNullValues); @@ -782,6 +804,20 @@ public String get(String key) { return Settings.toString(map.get(key)); } + /** + * Returns a setting value based on the setting key. + */ + public Settings getNestedSettings(String key) { + return (Settings) map.get(key); + } + + /** + * Returns a setting value based on the setting key. + */ + public List getNestedListOfSettings(String key) { + return (List) map.get(key); + } + /** Return the current secure settings, or {@code null} if none have been set. */ public SecureSettings getSecureSettings() { return secureSettings.get(); @@ -1019,6 +1055,19 @@ public Builder putList(String setting, List values) { return this; } + /** + * Sets the setting with the provided setting key and a list of values. + * + * @param setting The setting key + * @param values The values + * @return The builder + */ + public Builder putListOfObjects(String setting, List values) { + remove(setting); + map.put(setting, new ArrayList<>(values)); + return this; + } + /** * Sets all the provided settings including secure settings */ diff --git a/server/src/main/java/org/opensearch/discovery/InitializeExtensionSecurityRequest.java b/server/src/main/java/org/opensearch/discovery/InitializeExtensionSecurityRequest.java new file mode 100644 index 0000000000000..530d69e418b25 --- /dev/null +++ b/server/src/main/java/org/opensearch/discovery/InitializeExtensionSecurityRequest.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +package org.opensearch.discovery; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.transport.TransportRequest; + +import java.io.IOException; +import java.util.Objects; + +/** + * InitializeExtensionRequest to initialize plugin + * + * @opensearch.internal + */ +public class InitializeExtensionSecurityRequest extends TransportRequest { + + private final String serviceAccountToken; + + public InitializeExtensionSecurityRequest(String serviceAccountToken) { + this.serviceAccountToken = serviceAccountToken; + } + + public InitializeExtensionSecurityRequest(StreamInput in) throws IOException { + super(in); + serviceAccountToken = in.readString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(serviceAccountToken); + } + + public String getServiceAccountToken() { + return serviceAccountToken; + } + + @Override + public String toString() { + return "InitializeExtensionsRequest{" + "serviceAccountToken= " + serviceAccountToken + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InitializeExtensionSecurityRequest that = (InitializeExtensionSecurityRequest) o; + return Objects.equals(serviceAccountToken, that.serviceAccountToken); + } + + @Override + public int hashCode() { + return Objects.hash(serviceAccountToken); + } +} diff --git a/server/src/main/java/org/opensearch/discovery/InitializeExtensionSecurityResponse.java b/server/src/main/java/org/opensearch/discovery/InitializeExtensionSecurityResponse.java new file mode 100644 index 0000000000000..0618cc1f94b72 --- /dev/null +++ b/server/src/main/java/org/opensearch/discovery/InitializeExtensionSecurityResponse.java @@ -0,0 +1,88 @@ +/* + * 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. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.discovery; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.transport.TransportResponse; + +import java.io.IOException; +import java.util.Objects; + +/** + * PluginResponse to intialize plugin + * + * @opensearch.internal + */ +public class InitializeExtensionSecurityResponse extends TransportResponse { + private String name; + + public InitializeExtensionSecurityResponse(String name) { + this.name = name; + } + + public InitializeExtensionSecurityResponse(StreamInput in) throws IOException { + name = in.readString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(name); + } + + /** + * @return the node that is currently leading, according to the responding node. + */ + + public String getName() { + return this.name; + } + + @Override + public String toString() { + return "InitializeExtensionResponse{" + "name = " + name + "}"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InitializeExtensionSecurityResponse that = (InitializeExtensionSecurityResponse) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } +} diff --git a/server/src/main/java/org/opensearch/extensions/ExtensionsManager.java b/server/src/main/java/org/opensearch/extensions/ExtensionsManager.java index 468de4238f879..407f21eb756a5 100644 --- a/server/src/main/java/org/opensearch/extensions/ExtensionsManager.java +++ b/server/src/main/java/org/opensearch/extensions/ExtensionsManager.java @@ -16,6 +16,9 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; @@ -38,6 +41,8 @@ import org.opensearch.discovery.InitializeExtensionRequest; import org.opensearch.discovery.InitializeExtensionResponse; +import org.opensearch.discovery.InitializeExtensionSecurityRequest; +import org.opensearch.discovery.InitializeExtensionSecurityResponse; import org.opensearch.extensions.ExtensionsSettings.Extension; import org.opensearch.extensions.action.ExtensionActionRequest; import org.opensearch.extensions.action.ExtensionActionResponse; @@ -49,6 +54,8 @@ import org.opensearch.extensions.rest.RestActionsRequestHandler; import org.opensearch.extensions.settings.CustomSettingsRequestHandler; import org.opensearch.extensions.settings.RegisterCustomSettingsRequest; +import org.opensearch.identity.IdentityService; +import org.opensearch.identity.tokens.AuthToken; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.ConnectTransportException; import org.opensearch.transport.TransportException; @@ -70,6 +77,7 @@ public class ExtensionsManager { public static final String REQUEST_EXTENSION_ADD_SETTINGS_UPDATE_CONSUMER = "internal:discovery/addsettingsupdateconsumer"; public static final String REQUEST_EXTENSION_UPDATE_SETTINGS = "internal:discovery/updatesettings"; public static final String REQUEST_EXTENSION_DEPENDENCY_INFORMATION = "internal:discovery/dependencyinformation"; + public static final String REQUEST_EXTENSION_REGISTER_SECURITY_SETTINGS = "internal:discovery/registersecuritysettings"; public static final String REQUEST_EXTENSION_REGISTER_CUSTOM_SETTINGS = "internal:discovery/registercustomsettings"; public static final String REQUEST_EXTENSION_REGISTER_REST_ACTIONS = "internal:discovery/registerrestactions"; public static final String REQUEST_EXTENSION_REGISTER_TRANSPORT_ACTIONS = "internal:discovery/registertransportactions"; @@ -97,6 +105,8 @@ public static enum OpenSearchRequestType { private CustomSettingsRequestHandler customSettingsRequestHandler; private TransportService transportService; private ClusterService clusterService; + + private IdentityService identityService; private final Set> additionalSettings; private Settings environmentSettings; private AddSettingsUpdateConsumerRequestHandler addSettingsUpdateConsumerRequestHandler; @@ -142,9 +152,15 @@ public void initializeServicesAndRestHandler( TransportService transportService, ClusterService clusterService, Settings initialEnvironmentSettings, - NodeClient client + NodeClient client, + IdentityService identityService ) { - this.restActionsRequestHandler = new RestActionsRequestHandler(actionModule.getRestController(), extensionIdMap, transportService); + this.restActionsRequestHandler = new RestActionsRequestHandler( + actionModule.getRestController(), + extensionIdMap, + transportService, + identityService + ); this.customSettingsRequestHandler = new CustomSettingsRequestHandler(settingsModule); this.transportService = transportService; this.clusterService = clusterService; @@ -397,10 +413,62 @@ protected void doRun() throws Exception { new InitializeExtensionRequest(transportService.getLocalNode(), extension), initializeExtensionResponseHandler ); + initializeExtensionSecurity(extension); } }); } + private void initializeExtensionSecurity(DiscoveryExtensionNode extension) { + final CompletableFuture inProgressFuture = new CompletableFuture<>(); + final TransportResponseHandler initializeExtensionSecurityResponseHandler = + new TransportResponseHandler() { + + @Override + public InitializeExtensionSecurityResponse read(StreamInput in) throws IOException { + return new InitializeExtensionSecurityResponse(in); + } + + @Override + public void handleResponse(InitializeExtensionSecurityResponse response) { + System.out.println("Registered security settings for " + response.getName()); + inProgressFuture.complete(response); + } + + @Override + public void handleException(TransportException exp) { + logger.error(new ParameterizedMessage("Extension initialization failed"), exp); + inProgressFuture.completeExceptionally(exp); + } + + @Override + public String executor() { + return ThreadPool.Names.GENERIC; + } + }; + try { + logger.info("Sending extension request type: " + REQUEST_EXTENSION_REGISTER_SECURITY_SETTINGS); + AuthToken serviceAccountToken = identityService.getTokenManager().issueServiceAccountToken(extension.getId()); + transportService.sendRequest( + extension, + REQUEST_EXTENSION_REGISTER_SECURITY_SETTINGS, + new InitializeExtensionSecurityRequest(serviceAccountToken.getTokenValue()), + initializeExtensionSecurityResponseHandler + ); + + inProgressFuture.orTimeout(EXTENSION_REQUEST_WAIT_TIMEOUT, TimeUnit.SECONDS).join(); + } catch (CompletionException | ConnectTransportException e) { + if (e.getCause() instanceof TimeoutException || e instanceof ConnectTransportException) { + logger.info("No response from extension to request.", e); + } else if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } else if (e.getCause() instanceof Error) { + throw (Error) e.getCause(); + } else { + throw new RuntimeException(e.getCause()); + } + } + } + /** * Handles an {@link ExtensionRequest}. * @@ -485,6 +553,10 @@ void setClusterService(ClusterService clusterService) { this.clusterService = clusterService; } + public void setIdentityService(IdentityService identityService) { + this.identityService = identityService; + } + CustomSettingsRequestHandler getCustomSettingsRequestHandler() { return customSettingsRequestHandler; } diff --git a/server/src/main/java/org/opensearch/extensions/NoopExtensionsManager.java b/server/src/main/java/org/opensearch/extensions/NoopExtensionsManager.java index d434074279041..d0f69efc0de3a 100644 --- a/server/src/main/java/org/opensearch/extensions/NoopExtensionsManager.java +++ b/server/src/main/java/org/opensearch/extensions/NoopExtensionsManager.java @@ -21,6 +21,7 @@ import org.opensearch.extensions.action.ExtensionActionRequest; import org.opensearch.extensions.action.ExtensionActionResponse; import org.opensearch.extensions.action.RemoteExtensionActionResponse; +import org.opensearch.identity.IdentityService; import org.opensearch.transport.TransportService; /** @@ -41,7 +42,8 @@ public void initializeServicesAndRestHandler( TransportService transportService, ClusterService clusterService, Settings initialEnvironmentSettings, - NodeClient client + NodeClient client, + IdentityService identityService ) { // no-op } diff --git a/server/src/main/java/org/opensearch/extensions/rest/RestActionsRequestHandler.java b/server/src/main/java/org/opensearch/extensions/rest/RestActionsRequestHandler.java index d890c1b85bb81..130a45479b150 100644 --- a/server/src/main/java/org/opensearch/extensions/rest/RestActionsRequestHandler.java +++ b/server/src/main/java/org/opensearch/extensions/rest/RestActionsRequestHandler.java @@ -8,16 +8,16 @@ package org.opensearch.extensions.rest; +import java.util.Map; import org.opensearch.action.ActionModule.DynamicActionRegistry; import org.opensearch.extensions.AcknowledgedResponse; import org.opensearch.extensions.DiscoveryExtensionNode; +import org.opensearch.identity.IdentityService; import org.opensearch.rest.RestController; import org.opensearch.rest.RestHandler; import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportService; -import java.util.Map; - /** * Handles requests to register extension REST actions. * @@ -28,6 +28,7 @@ public class RestActionsRequestHandler { private final RestController restController; private final Map extensionIdMap; private final TransportService transportService; + private final IdentityService identityService; /** * Instantiates a new REST Actions Request Handler using the Node's RestController. @@ -39,11 +40,13 @@ public class RestActionsRequestHandler { public RestActionsRequestHandler( RestController restController, Map extensionIdMap, - TransportService transportService + TransportService transportService, + IdentityService identityService ) { this.restController = restController; this.extensionIdMap = extensionIdMap; this.transportService = transportService; + this.identityService = identityService; } /** @@ -62,7 +65,8 @@ public TransportResponse handleRegisterRestActionsRequest( restActionsRequest, discoveryExtensionNode, transportService, - dynamicActionRegistry + dynamicActionRegistry, + identityService ); restController.registerHandler(handler); return new AcknowledgedResponse(true); diff --git a/server/src/main/java/org/opensearch/extensions/rest/RestInitializeExtensionAction.java b/server/src/main/java/org/opensearch/extensions/rest/RestInitializeExtensionAction.java index 2a3ad63379556..66f1d3c12990d 100644 --- a/server/src/main/java/org/opensearch/extensions/rest/RestInitializeExtensionAction.java +++ b/server/src/main/java/org/opensearch/extensions/rest/RestInitializeExtensionAction.java @@ -62,6 +62,53 @@ public RestInitializeExtensionAction(ExtensionsManager extensionsManager) { this.extensionsManager = extensionsManager; } + private static Map unflattenMap(Map flatMap) { + Map unflattenedMap = new HashMap<>(); + + for (Map.Entry entry : flatMap.entrySet()) { + String[] keys = entry.getKey().split("\\."); + putNested(unflattenedMap, keys, entry.getValue()); + } + + return unflattenedMap; + } + + private static void putNested(Map map, String[] keys, Object value) { + for (int i = 0; i < keys.length; i++) { + String key = keys[i]; + + if (i == keys.length - 1) { + map.put(key, value); + } else if (keys[i + 1].matches("\\d+")) { + int index = Integer.parseInt(keys[++i]); + + List> list; + if (map.containsKey(key)) { + list = (List>) map.get(key); + } else { + list = new ArrayList<>(); + map.put(key, list); + } + + while (list.size() <= index) { + list.add(new HashMap<>()); + } + + map = list.get(index); + } else { + Map nestedMap; + if (map.containsKey(key)) { + nestedMap = (Map) map.get(key); + } else { + nestedMap = new HashMap<>(); + map.put(key, nestedMap); + } + + map = nestedMap; + } + } + } + @Override public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { String name = null; @@ -124,13 +171,26 @@ public RestChannelConsumer prepareRequest(RestRequest request, NodeClient client } } - Map additionalSettingsMap = extensionMap.entrySet() + Map additionalSettingsMap = extensionMap.entrySet() .stream() - .filter(kv -> additionalSettingsKeys.contains(kv.getKey())) + .filter(kv -> additionalSettingsKeys.stream().anyMatch(k -> { + if (k.endsWith(".")) { + return kv.getKey().startsWith(k); + } else { + return kv.getKey().equals(k); + } + })) .collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue())); + System.out.println("additionalSettingsKeys: " + additionalSettingsKeys); + System.out.println("additionalSettingsMap: " + additionalSettingsMap); + + Map unflattenedMap = unflattenMap(additionalSettingsMap); + + System.out.println("unflattenedMap: " + unflattenedMap); + Settings.Builder output = Settings.builder(); - output.loadFromMap(additionalSettingsMap); + output.loadFromMap(unflattenedMap); extAdditionalSettings.applySettings(output.build()); // Create extension read from initialization request diff --git a/server/src/main/java/org/opensearch/extensions/rest/RestSendToExtensionAction.java b/server/src/main/java/org/opensearch/extensions/rest/RestSendToExtensionAction.java index e20a3c6b2814c..f2195f3d6df14 100644 --- a/server/src/main/java/org/opensearch/extensions/rest/RestSendToExtensionAction.java +++ b/server/src/main/java/org/opensearch/extensions/rest/RestSendToExtensionAction.java @@ -8,41 +8,40 @@ package org.opensearch.extensions.rest; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.action.ActionModule.DynamicActionRegistry; import org.opensearch.client.node.NodeClient; +import org.opensearch.common.xcontent.XContentType; import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.core.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.rest.RestStatus; import org.opensearch.extensions.DiscoveryExtensionNode; import org.opensearch.extensions.ExtensionsManager; +import org.opensearch.http.HttpRequest; +import org.opensearch.identity.IdentityService; +import org.opensearch.identity.tokens.StandardTokenClaims; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.NamedRoute; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequest.Method; -import org.opensearch.core.rest.RestStatus; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; -import org.opensearch.http.HttpRequest; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.security.Principal; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableList; @@ -54,19 +53,13 @@ public class RestSendToExtensionAction extends BaseRestHandler { private static final String SEND_TO_EXTENSION_ACTION = "send_to_extension_action"; private static final Logger logger = LogManager.getLogger(RestSendToExtensionAction.class); - // To replace with user identity see https://github.com/opensearch-project/OpenSearch/pull/4247 - private static final Principal DEFAULT_PRINCIPAL = new Principal() { - @Override - public String getName() { - return "OpenSearchUser"; - } - }; private final List routes; private final List deprecatedRoutes; private final String pathPrefix; private final DiscoveryExtensionNode discoveryExtensionNode; private final TransportService transportService; + private final IdentityService identityService; private static final Set allowList = Set.of("Content-Type"); private static final Set denyList = Set.of("Authorization", "Proxy-Authorization"); @@ -82,7 +75,8 @@ public RestSendToExtensionAction( RegisterRestActionsRequest restActionsRequest, DiscoveryExtensionNode discoveryExtensionNode, TransportService transportService, - DynamicActionRegistry dynamicActionRegistry + DynamicActionRegistry dynamicActionRegistry, + IdentityService identityService ) { this.pathPrefix = "/_extensions/_" + restActionsRequest.getUniqueId(); RestRequest.Method method; @@ -147,6 +141,7 @@ public RestSendToExtensionAction( this.discoveryExtensionNode = discoveryExtensionNode; this.transportService = transportService; + this.identityService = identityService; } @Override @@ -240,9 +235,6 @@ public String executor() { }; try { - // Will be replaced with ExtensionTokenProcessor and PrincipalIdentifierToken classes from feature/identity - final String extensionTokenProcessor = "placeholder_token_processor"; - final String requestIssuerIdentity = "placeholder_request_issuer_identity"; Map> filteredHeaders = filterHeaders(headers, allowList, denyList); @@ -259,7 +251,9 @@ public String executor() { filteredHeaders, contentType, content, - requestIssuerIdentity, + identityService.getTokenManager() + .issueOnBehalfOfToken(Map.of(StandardTokenClaims.AUDIENCE.getName(), discoveryExtensionNode.getId())) + .getTokenValue(), // discoveryExtensionNode.getId() is extension's unique id httpVersion ), restExecuteOnExtensionResponseHandler diff --git a/server/src/main/java/org/opensearch/identity/noop/NoopIdentityPlugin.java b/server/src/main/java/org/opensearch/identity/noop/NoopIdentityPlugin.java index c6ed8d57da435..c2367383df595 100644 --- a/server/src/main/java/org/opensearch/identity/noop/NoopIdentityPlugin.java +++ b/server/src/main/java/org/opensearch/identity/noop/NoopIdentityPlugin.java @@ -8,9 +8,9 @@ package org.opensearch.identity.noop; +import org.opensearch.identity.Subject; import org.opensearch.identity.tokens.TokenManager; import org.opensearch.plugins.IdentityPlugin; -import org.opensearch.identity.Subject; /** * Implementation of identity plugin that does not enforce authentication or authorization diff --git a/server/src/main/java/org/opensearch/identity/noop/NoopTokenManager.java b/server/src/main/java/org/opensearch/identity/noop/NoopTokenManager.java index a55f28e02a8aa..39426aacce29d 100644 --- a/server/src/main/java/org/opensearch/identity/noop/NoopTokenManager.java +++ b/server/src/main/java/org/opensearch/identity/noop/NoopTokenManager.java @@ -8,9 +8,12 @@ package org.opensearch.identity.noop; +import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.opensearch.OpenSearchSecurityException; import org.opensearch.identity.IdentityService; +import org.opensearch.identity.Subject; import org.opensearch.identity.tokens.AuthToken; import org.opensearch.identity.tokens.TokenManager; @@ -21,13 +24,33 @@ public class NoopTokenManager implements TokenManager { private static final Logger log = LogManager.getLogger(IdentityService.class); + public static AuthToken NOOP_AUTH_TOKEN = new AuthToken() { + @Override + public String getTokenValue() { + return ""; + } + }; + /** * Issue a new Noop Token * @return a new Noop Token */ @Override - public AuthToken issueToken(String audience) { - return new AuthToken() { - }; + public AuthToken issueServiceAccountToken(String extensionUniqueId) throws OpenSearchSecurityException { + return NOOP_AUTH_TOKEN; + } + + /** + * Issue a new Noop Token + * @return a new Noop Token + */ + @Override + public AuthToken issueOnBehalfOfToken(Map claims) { + return NOOP_AUTH_TOKEN; + } + + @Override + public Subject authenticateToken(AuthToken authToken) { + return null; } } diff --git a/server/src/main/java/org/opensearch/identity/tokens/AuthToken.java b/server/src/main/java/org/opensearch/identity/tokens/AuthToken.java index 6e113f6eaa96a..5fa922c428cc4 100644 --- a/server/src/main/java/org/opensearch/identity/tokens/AuthToken.java +++ b/server/src/main/java/org/opensearch/identity/tokens/AuthToken.java @@ -15,4 +15,5 @@ */ public interface AuthToken { + String getTokenValue(); } diff --git a/server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java b/server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java index 9cd6cb6b6208a..f2f9c040f8966 100644 --- a/server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java +++ b/server/src/main/java/org/opensearch/identity/tokens/BasicAuthToken.java @@ -51,4 +51,13 @@ public void revoke() { this.password = ""; this.user = ""; } + + @Override + public String getTokenValue() { + if (user == null || password == null) { + return ""; + } + String usernamepassword = user + ":" + password; + return Base64.getEncoder().encodeToString(usernamepassword.getBytes(StandardCharsets.UTF_8)); + } } diff --git a/server/src/main/java/org/opensearch/identity/tokens/BearerAuthToken.java b/server/src/main/java/org/opensearch/identity/tokens/BearerAuthToken.java index eac164af1c5d3..32ec32673851a 100644 --- a/server/src/main/java/org/opensearch/identity/tokens/BearerAuthToken.java +++ b/server/src/main/java/org/opensearch/identity/tokens/BearerAuthToken.java @@ -58,4 +58,9 @@ public String getTokenIdentifier() { public String toString() { return "Bearer auth token with header=" + header + ", payload=" + payload + ", signature=" + signature; } + + @Override + public String getTokenValue() { + return completeToken; + } } diff --git a/server/src/main/java/org/opensearch/identity/tokens/StandardTokenClaims.java b/server/src/main/java/org/opensearch/identity/tokens/StandardTokenClaims.java new file mode 100644 index 0000000000000..09f60521d0e30 --- /dev/null +++ b/server/src/main/java/org/opensearch/identity/tokens/StandardTokenClaims.java @@ -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. + */ + +package org.opensearch.identity.tokens; + +/** + * This enum represents the standard claims that are used in the JWTs for OnBehalfOf tokens + */ +public enum StandardTokenClaims { + + ISSUER("iss"), + SUBJECT("sub"), + AUDIENCE("aud"), + EXPIRATION_TIME("exp"), + NOT_BEFORE("nbf"), + ISSUED_AT("iat"), + JWT_ID("jti"); + + private final String name; + + StandardTokenClaims(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + public static StandardTokenClaims fromString(String name) { + return StandardTokenClaims.valueOf(name); + } +} diff --git a/server/src/main/java/org/opensearch/identity/tokens/TokenManager.java b/server/src/main/java/org/opensearch/identity/tokens/TokenManager.java index 029ce430e7532..40f24998950f2 100644 --- a/server/src/main/java/org/opensearch/identity/tokens/TokenManager.java +++ b/server/src/main/java/org/opensearch/identity/tokens/TokenManager.java @@ -8,15 +8,32 @@ package org.opensearch.identity.tokens; +import org.opensearch.OpenSearchSecurityException; +import java.util.Map; +import org.opensearch.identity.Subject; + /** * This interface defines the expected methods of a token manager */ public interface TokenManager { /** - * Create a new auth token - * @param audience: The audience for the token + * Create a new on behalf of token + * + * @param claims: A list of claims for the token to be generated with * @return A new auth token */ - public AuthToken issueToken(String audience); + public AuthToken issueOnBehalfOfToken(Map claims); + + /** + * Issue a service account token for an extension's service account + * */ + AuthToken issueServiceAccountToken(String extensionUniqueId) throws OpenSearchSecurityException; + + /** + * Authenticates a provided authToken + * @param authToken: The authToken to authenticate + * @return The authenticated subject + */ + public Subject authenticateToken(AuthToken authToken); } diff --git a/server/src/main/java/org/opensearch/node/Node.java b/server/src/main/java/org/opensearch/node/Node.java index d768165451a5a..3ccf2a2e97531 100644 --- a/server/src/main/java/org/opensearch/node/Node.java +++ b/server/src/main/java/org/opensearch/node/Node.java @@ -32,47 +32,40 @@ package org.opensearch.node; +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.net.ssl.SNIHostName; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.Constants; -import org.opensearch.ExceptionsHelper; -import org.opensearch.common.SetOnce; -import org.opensearch.common.settings.SettingsException; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.util.FeatureFlags; -import org.opensearch.cluster.routing.allocation.AwarenessReplicaBalance; -import org.opensearch.index.IndexModule; -import org.opensearch.index.IndexingPressureService; -import org.opensearch.index.store.remote.filecache.FileCache; -import org.opensearch.index.store.remote.filecache.FileCacheCleaner; -import org.opensearch.index.store.remote.filecache.FileCacheFactory; -import org.opensearch.indices.replication.SegmentReplicationSourceFactory; -import org.opensearch.indices.replication.SegmentReplicationTargetService; -import org.opensearch.indices.replication.SegmentReplicationSourceService; -import org.opensearch.extensions.ExtensionsManager; -import org.opensearch.extensions.NoopExtensionsManager; -import org.opensearch.monitor.fs.FsInfo; -import org.opensearch.monitor.fs.FsProbe; -import org.opensearch.plugins.ExtensionAwarePlugin; -import org.opensearch.plugins.SearchPipelinePlugin; -import org.opensearch.telemetry.tracing.NoopTracerFactory; -import org.opensearch.telemetry.tracing.Tracer; -import org.opensearch.telemetry.tracing.TracerFactory; -import org.opensearch.search.backpressure.SearchBackpressureService; -import org.opensearch.search.backpressure.settings.SearchBackpressureSettings; -import org.opensearch.search.pipeline.SearchPipelineService; -import org.opensearch.tasks.TaskCancellationMonitoringService; -import org.opensearch.tasks.TaskCancellationMonitoringSettings; -import org.opensearch.tasks.TaskResourceTrackingService; -import org.opensearch.tasks.consumer.TopNSearchTasksLogger; -import org.opensearch.threadpool.RunnableTaskExecutionListener; -import org.opensearch.index.store.RemoteSegmentStoreDirectoryFactory; -import org.opensearch.telemetry.TelemetryModule; -import org.opensearch.telemetry.TelemetrySettings; -import org.opensearch.watcher.ResourceWatcherService; -import org.opensearch.core.Assertions; import org.opensearch.Build; +import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; import org.opensearch.OpenSearchTimeoutException; import org.opensearch.Version; @@ -109,8 +102,10 @@ import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.routing.BatchedRerouteService; import org.opensearch.cluster.routing.RerouteService; +import org.opensearch.cluster.routing.allocation.AwarenessReplicaBalance; import org.opensearch.cluster.routing.allocation.DiskThresholdMonitor; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.SetOnce; import org.opensearch.common.StopWatch; import org.opensearch.common.breaker.CircuitBreaker; import org.opensearch.common.component.Lifecycle; @@ -119,7 +114,10 @@ import org.opensearch.common.inject.Key; import org.opensearch.common.inject.Module; import org.opensearch.common.inject.ModulesBuilder; +import org.opensearch.common.lease.Releasables; + import org.opensearch.core.common.io.stream.NamedWriteableRegistry; + import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.logging.HeaderWarning; import org.opensearch.common.logging.NodeAndClusterIdStateListener; @@ -132,20 +130,26 @@ import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.SettingUpgrader; import org.opensearch.common.settings.Settings; +import org.opensearch.common.settings.SettingsException; import org.opensearch.common.settings.SettingsModule; import org.opensearch.common.transport.BoundTransportAddress; import org.opensearch.common.transport.TransportAddress; +import org.opensearch.common.unit.ByteSizeUnit; +import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.BigArrays; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.util.PageCacheRecycler; import org.opensearch.common.util.io.IOUtils; -import org.opensearch.common.lease.Releasables; +import org.opensearch.core.Assertions; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.discovery.Discovery; import org.opensearch.discovery.DiscoveryModule; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.env.NodeMetadata; +import org.opensearch.extensions.ExtensionsManager; +import org.opensearch.extensions.NoopExtensionsManager; import org.opensearch.gateway.GatewayAllocator; import org.opensearch.gateway.GatewayMetaState; import org.opensearch.gateway.GatewayModule; @@ -154,9 +158,15 @@ import org.opensearch.gateway.PersistedClusterStateService; import org.opensearch.http.HttpServerTransport; import org.opensearch.identity.IdentityService; +import org.opensearch.index.IndexModule; import org.opensearch.index.IndexSettings; +import org.opensearch.index.IndexingPressureService; import org.opensearch.index.analysis.AnalysisRegistry; import org.opensearch.index.engine.EngineFactory; +import org.opensearch.index.store.RemoteSegmentStoreDirectoryFactory; +import org.opensearch.index.store.remote.filecache.FileCache; +import org.opensearch.index.store.remote.filecache.FileCacheCleaner; +import org.opensearch.index.store.remote.filecache.FileCacheFactory; import org.opensearch.indices.IndicesModule; import org.opensearch.indices.IndicesService; import org.opensearch.indices.ShardLimitValidator; @@ -171,10 +181,15 @@ import org.opensearch.indices.recovery.PeerRecoverySourceService; import org.opensearch.indices.recovery.PeerRecoveryTargetService; import org.opensearch.indices.recovery.RecoverySettings; +import org.opensearch.indices.replication.SegmentReplicationSourceFactory; +import org.opensearch.indices.replication.SegmentReplicationSourceService; +import org.opensearch.indices.replication.SegmentReplicationTargetService; import org.opensearch.indices.store.IndicesStore; import org.opensearch.ingest.IngestService; import org.opensearch.monitor.MonitorService; import org.opensearch.monitor.fs.FsHealthService; +import org.opensearch.monitor.fs.FsInfo; +import org.opensearch.monitor.fs.FsProbe; import org.opensearch.monitor.jvm.JvmInfo; import org.opensearch.persistent.PersistentTasksClusterService; import org.opensearch.persistent.PersistentTasksExecutor; @@ -186,6 +201,7 @@ import org.opensearch.plugins.ClusterPlugin; import org.opensearch.plugins.DiscoveryPlugin; import org.opensearch.plugins.EnginePlugin; +import org.opensearch.plugins.ExtensionAwarePlugin; import org.opensearch.plugins.IdentityPlugin; import org.opensearch.plugins.IndexStorePlugin; import org.opensearch.plugins.IngestPlugin; @@ -197,6 +213,7 @@ import org.opensearch.plugins.PluginsService; import org.opensearch.plugins.RepositoryPlugin; import org.opensearch.plugins.ScriptPlugin; +import org.opensearch.plugins.SearchPipelinePlugin; import org.opensearch.plugins.SearchPlugin; import org.opensearch.plugins.SystemIndexPlugin; import org.opensearch.plugins.TelemetryPlugin; @@ -210,7 +227,10 @@ import org.opensearch.search.SearchModule; import org.opensearch.search.SearchService; import org.opensearch.search.aggregations.support.AggregationUsageService; +import org.opensearch.search.backpressure.SearchBackpressureService; +import org.opensearch.search.backpressure.settings.SearchBackpressureSettings; import org.opensearch.search.fetch.FetchPhase; +import org.opensearch.search.pipeline.SearchPipelineService; import org.opensearch.search.query.QueryPhase; import org.opensearch.snapshots.InternalSnapshotsInfoService; import org.opensearch.snapshots.RestoreService; @@ -218,46 +238,26 @@ import org.opensearch.snapshots.SnapshotsInfoService; import org.opensearch.snapshots.SnapshotsService; import org.opensearch.tasks.Task; +import org.opensearch.tasks.TaskCancellationMonitoringService; +import org.opensearch.tasks.TaskCancellationMonitoringSettings; import org.opensearch.tasks.TaskCancellationService; +import org.opensearch.tasks.TaskResourceTrackingService; import org.opensearch.tasks.TaskResultsService; +import org.opensearch.tasks.consumer.TopNSearchTasksLogger; +import org.opensearch.telemetry.TelemetryModule; +import org.opensearch.telemetry.TelemetrySettings; +import org.opensearch.telemetry.tracing.NoopTracerFactory; +import org.opensearch.telemetry.tracing.Tracer; +import org.opensearch.telemetry.tracing.TracerFactory; import org.opensearch.threadpool.ExecutorBuilder; +import org.opensearch.threadpool.RunnableTaskExecutionListener; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.RemoteClusterService; import org.opensearch.transport.Transport; import org.opensearch.transport.TransportInterceptor; import org.opensearch.transport.TransportService; import org.opensearch.usage.UsageService; - -import javax.net.ssl.SNIHostName; -import java.io.BufferedWriter; -import java.io.Closeable; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Function; -import java.util.function.UnaryOperator; -import java.util.stream.Collectors; -import java.util.stream.Stream; - +import org.opensearch.watcher.ResourceWatcherService; import static java.util.stream.Collectors.toList; import static org.opensearch.common.util.FeatureFlags.TELEMETRY; import static org.opensearch.env.NodeEnvironment.collectFileCacheDataPath; @@ -483,6 +483,7 @@ protected Node( additionalSettings.addAll(extAwarePlugin.getExtensionSettings()); } this.extensionsManager = new ExtensionsManager(additionalSettings); + this.extensionsManager.setIdentityService(identityService); } else { this.extensionsManager = new NoopExtensionsManager(); } @@ -869,7 +870,8 @@ protected Node( transportService, clusterService, environment.settings(), - client + client, + identityService ); final GatewayMetaState gatewayMetaState = new GatewayMetaState(); final ResponseCollectorService responseCollectorService = new ResponseCollectorService(clusterService); diff --git a/server/src/test/java/org/opensearch/extensions/ExtensionsManagerTests.java b/server/src/test/java/org/opensearch/extensions/ExtensionsManagerTests.java index 41b841aec7be8..fb2dde747251e 100644 --- a/server/src/test/java/org/opensearch/extensions/ExtensionsManagerTests.java +++ b/server/src/test/java/org/opensearch/extensions/ExtensionsManagerTests.java @@ -8,33 +8,31 @@ package org.opensearch.extensions; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.emptySet; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.mock; -import static org.opensearch.test.ClusterServiceUtils.createClusterService; - import java.io.IOException; import java.net.InetAddress; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; - import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.junit.After; import org.junit.Before; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import org.opensearch.OpenSearchException; import org.opensearch.Version; import org.opensearch.action.ActionModule; @@ -42,33 +40,34 @@ import org.opensearch.action.admin.cluster.state.ClusterStateResponse; import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.ClusterSettingsResponse; -import org.opensearch.common.util.FeatureFlags; -import org.opensearch.env.EnvironmentSettingsResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.io.stream.BytesStreamOutput; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; +import org.opensearch.common.settings.SettingsModule; import org.opensearch.common.settings.WriteableSetting; -import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.WriteableSetting.SettingType; -import org.opensearch.common.settings.SettingsModule; import org.opensearch.common.transport.TransportAddress; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.util.PageCacheRecycler; import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.core.common.io.stream.BytesStreamInput; import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.env.Environment; +import org.opensearch.env.EnvironmentSettingsResponse; +import org.opensearch.extensions.ExtensionsSettings.Extension; import org.opensearch.extensions.proto.ExtensionRequestProto; import org.opensearch.extensions.rest.RegisterRestActionsRequest; import org.opensearch.extensions.settings.RegisterCustomSettingsRequest; -import org.opensearch.extensions.ExtensionsSettings.Extension; import org.opensearch.identity.IdentityService; import org.opensearch.indices.breaker.NoneCircuitBreakerService; import org.opensearch.plugins.ExtensionAwarePlugin; import org.opensearch.rest.RestController; +import static org.opensearch.test.ClusterServiceUtils.createClusterService; import org.opensearch.test.FeatureFlagSetter; import org.opensearch.test.MockLogAppender; import org.opensearch.test.OpenSearchTestCase; @@ -93,6 +92,9 @@ public class ExtensionsManagerTests extends OpenSearchTestCase { private Setting customSetting = Setting.simpleString("custom_extension_setting", "none", Property.ExtensionScope); private NodeClient client; private MockNioTransport transport; + private IdentityService identityService; + private Path extensionDir; + private final ThreadPool threadPool = new TestThreadPool(ExtensionsManagerTests.class.getSimpleName()); private final Settings settings = Settings.builder() .put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT) @@ -164,6 +166,7 @@ public List> getExtensionSettings() { Collections.emptyList() ); client = new NoOpNodeClient(this.getTestName()); + identityService = new IdentityService(Settings.EMPTY, List.of()); } @Override @@ -767,7 +770,8 @@ public void testRegisterHandler() throws Exception { mockTransportService, clusterService, settings, - client + client, + identityService ); verify(mockTransportService, times(9)).registerRequestHandler(anyString(), anyString(), anyBoolean(), anyBoolean(), any(), any()); @@ -884,7 +888,8 @@ private void initialize(ExtensionsManager extensionsManager) { transportService, clusterService, settings, - client + client, + identityService ); } } diff --git a/server/src/test/java/org/opensearch/extensions/rest/RestSendToExtensionActionTests.java b/server/src/test/java/org/opensearch/extensions/rest/RestSendToExtensionActionTests.java index 8695e409de0bc..74f708f22d75f 100644 --- a/server/src/test/java/org/opensearch/extensions/rest/RestSendToExtensionActionTests.java +++ b/server/src/test/java/org/opensearch/extensions/rest/RestSendToExtensionActionTests.java @@ -10,20 +10,14 @@ import java.net.InetAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Set; import java.util.Map; -import java.util.Arrays; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; - -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.emptySet; -import static org.mockito.Mockito.mock; - import org.junit.After; import org.junit.Before; import org.opensearch.Version; @@ -57,6 +51,10 @@ import org.opensearch.transport.TransportService; import org.opensearch.transport.nio.MockNioTransport; import org.opensearch.usage.UsageService; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static org.mockito.Mockito.mock; public class RestSendToExtensionActionTests extends OpenSearchTestCase { @@ -65,6 +63,7 @@ public class RestSendToExtensionActionTests extends OpenSearchTestCase { private DiscoveryExtensionNode discoveryExtensionNode; private ActionModule actionModule; private DynamicActionRegistry dynamicActionRegistry; + private IdentityService identityService; private final ThreadPool threadPool = new TestThreadPool(RestSendToExtensionActionTests.class.getSimpleName()); @Before @@ -121,6 +120,7 @@ public void setup() throws Exception { new IdentityService(Settings.EMPTY, new ArrayList<>()), new ExtensionsManager(Set.of()) ); + identityService = new IdentityService(Settings.EMPTY, new ArrayList<>()); dynamicActionRegistry = actionModule.getDynamicActionRegistry(); } @@ -142,7 +142,8 @@ public void testRestSendToExtensionAction() throws Exception { registerRestActionRequest, discoveryExtensionNode, transportService, - dynamicActionRegistry + dynamicActionRegistry, + identityService ); assertEquals("send_to_extension_action", restSendToExtensionAction.getName()); @@ -174,7 +175,8 @@ public void testRestSendToExtensionActionWithNamedRoute() throws Exception { registerRestActionRequest, discoveryExtensionNode, transportService, - dynamicActionRegistry + dynamicActionRegistry, + identityService ); assertEquals("send_to_extension_action", restSendToExtensionAction.getName()); @@ -219,7 +221,8 @@ public void testRestSendToExtensionActionWithNamedRouteAndLegacyActionName() thr registerRestActionRequest, discoveryExtensionNode, transportService, - dynamicActionRegistry + dynamicActionRegistry, + identityService ); assertEquals("send_to_extension_action", restSendToExtensionAction.getName()); @@ -271,7 +274,13 @@ public void testRestSendToExtensionActionWithoutUniqueNameShouldFail() { ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -283,7 +292,13 @@ public void testRestSendToExtensionMultipleNamedRoutesWithSameName() throws Exce ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -295,7 +310,13 @@ public void testRestSendToExtensionMultipleNamedRoutesWithSameLegacyActionName() ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -307,7 +328,13 @@ public void testRestSendToExtensionMultipleRoutesWithSameMethodAndPath() throws ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -319,7 +346,13 @@ public void testRestSendToExtensionMultipleRoutesWithSameMethodAndPathWithDiffer ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -331,7 +364,13 @@ public void testRestSendToExtensionMultipleRoutesWithSameMethodAndPathWithPathPa ); try { - new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry); + new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ); } catch (IllegalArgumentException e) { fail("IllegalArgumentException should not be thrown for different paths"); } @@ -353,7 +392,13 @@ public void testRestSendToExtensionWithNamedRouteCollidingWithDynamicTransportAc expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -367,7 +412,13 @@ public void testRestSendToExtensionWithNamedRouteCollidingWithNativeTransportAct ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -381,7 +432,8 @@ public void testRestSendToExtensionActionFilterHeaders() throws Exception { registerRestActionRequest, discoveryExtensionNode, transportService, - dynamicActionRegistry + dynamicActionRegistry, + identityService ); Map> headers = new HashMap<>(); @@ -407,7 +459,13 @@ public void testRestSendToExtensionActionBadMethod() throws Exception { ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -419,7 +477,13 @@ public void testRestSendToExtensionActionBadDeprecatedMethod() throws Exception ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -431,7 +495,13 @@ public void testRestSendToExtensionActionMissingUri() throws Exception { ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } @@ -443,7 +513,13 @@ public void testRestSendToExtensionActionMissingDeprecatedUri() throws Exception ); expectThrows( IllegalArgumentException.class, - () -> new RestSendToExtensionAction(registerRestActionRequest, discoveryExtensionNode, transportService, dynamicActionRegistry) + () -> new RestSendToExtensionAction( + registerRestActionRequest, + discoveryExtensionNode, + transportService, + dynamicActionRegistry, + identityService + ) ); } }