Skip to content

Commit

Permalink
securityadmin: Replace TransportClient by RestHighLevelClient (#1638)
Browse files Browse the repository at this point in the history
Signed-off-by: rs-eliatra <rafal.stobiecki@eliatra.com>
  • Loading branch information
rs-eliatra committed Mar 2, 2022
1 parent 0b63577 commit 81a43c2
Show file tree
Hide file tree
Showing 14 changed files with 1,026 additions and 418 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
import org.opensearch.security.dlic.rest.api.SecurityRestApiActions;
import org.opensearch.security.filter.SecurityRestFilter;
import org.opensearch.security.http.SecurityHttpServerTransport;
import org.opensearch.security.rest.SecurityConfigUpdateAction;
import org.opensearch.security.rest.SecurityWhoAmIAction;
import org.opensearch.security.ssl.OpenSearchSecuritySSLPlugin;
import org.opensearch.security.ssl.rest.SecuritySSLReloadCertsAction;
import org.opensearch.security.ssl.rest.SecuritySSLCertsInfoAction;
Expand Down Expand Up @@ -459,7 +461,8 @@ public List<RestHandler> getRestHandlers(Settings settings, RestController restC
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));
if (sslCertReloadEnabled) {
handlers.add(new SecuritySSLReloadCertsAction(settings, restController, sks, Objects.requireNonNull(threadPool), Objects.requireNonNull(adminDns)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
import org.opensearch.cluster.node.DiscoveryNode;
import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;
import org.opensearch.common.xcontent.ToXContentObject;
import org.opensearch.common.xcontent.XContentBuilder;

public class ConfigUpdateNodeResponse extends BaseNodeResponse {
public class ConfigUpdateNodeResponse extends BaseNodeResponse implements ToXContentObject {

private String[] updatedConfigTypes;
private String message;
Expand Down Expand Up @@ -78,4 +80,14 @@ public void writeTo(StreamOutput out) throws IOException {
public String toString() {
return "ConfigUpdateNodeResponse [updatedConfigTypes=" + Arrays.toString(updatedConfigTypes) + ", message=" + message + "]";
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field("updated_config_types", updatedConfigTypes);
builder.field("updated_config_size", updatedConfigTypes == null ? 0: updatedConfigTypes.length);
builder.field("message", message);
builder.endObject();
return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@
import org.opensearch.cluster.ClusterName;
import org.opensearch.common.io.stream.StreamInput;
import org.opensearch.common.io.stream.StreamOutput;
import org.opensearch.common.xcontent.ToXContentObject;
import org.opensearch.common.xcontent.XContentBuilder;

public class ConfigUpdateResponse extends BaseNodesResponse<ConfigUpdateNodeResponse> {
public class ConfigUpdateResponse extends BaseNodesResponse<ConfigUpdateNodeResponse> implements ToXContentObject {

public ConfigUpdateResponse(StreamInput in) throws IOException {
super(in);
}

public ConfigUpdateResponse(final ClusterName clusterName, List<ConfigUpdateNodeResponse> nodes, List<FailedNodeException> failures) {
super(clusterName, nodes, failures);
}
Expand All @@ -58,4 +60,16 @@ public List<ConfigUpdateNodeResponse> readNodesFrom(final StreamInput in) throws
public void writeNodesTo(final StreamOutput out, List<ConfigUpdateNodeResponse> nodes) throws IOException {
out.writeList(nodes);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("configupdate_response");
builder.field("nodes", getNodesMap());
builder.field("node_size", getNodes().size());
builder.field("has_failures", hasFailures());
builder.field("failures_size", failures().size());
builder.endObject();

return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public class SecurityRestFilter {
private WhitelistingSettings whitelistingSettings;

private static final String HEALTH_SUFFIX = "health";
private static final String WHO_AM_I_SUFFIX = "whoami";

private static final String REGEX_PATH_PREFIX = "/("+ LEGACY_OPENDISTRO_PREFIX + "|" + PLUGINS_PREFIX + ")/" +"(.*)";
private static final Pattern PATTERN_PATH_PREFIX = Pattern.compile(REGEX_PATH_PREFIX);

Expand Down Expand Up @@ -184,7 +186,9 @@ private boolean checkAndAuthenticateRequest(RestRequest request, RestChannel cha

Matcher matcher = PATTERN_PATH_PREFIX.matcher(request.path());
final String suffix = matcher.matches() ? matcher.group(2) : null;
if(request.method() != Method.OPTIONS && !(HEALTH_SUFFIX.equals(suffix))) {
if(request.method() != Method.OPTIONS
&& !(HEALTH_SUFFIX.equals(suffix))
&& !(WHO_AM_I_SUFFIX.equals(suffix))) {
if (!registry.authenticate(request, channel, threadContext)) {
// another roundtrip
org.apache.logging.log4j.ThreadContext.remove("user");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.security.rest;

import com.google.common.collect.ImmutableList;
import org.opensearch.client.node.NodeClient;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.rest.*;
import org.opensearch.rest.action.RestActions.NodesResponseRestListener;
import org.opensearch.security.action.configupdate.ConfigUpdateAction;
import org.opensearch.security.action.configupdate.ConfigUpdateRequest;
import org.opensearch.security.configuration.AdminDNs;
import org.opensearch.security.ssl.transport.PrincipalExtractor;
import org.opensearch.security.ssl.util.SSLRequestHelper;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.user.User;
import org.opensearch.threadpool.ThreadPool;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;

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

public class SecurityConfigUpdateAction extends BaseRestHandler {

private static final List<Route> routes = addRoutesPrefix(ImmutableList.of(
new Route(PUT, "/configupdate")),
"/_plugins/_security");

private final ThreadContext threadContext;
private final AdminDNs adminDns;
private final Settings settings;
private final Path configPath;
private final PrincipalExtractor principalExtractor;

public SecurityConfigUpdateAction(final Settings settings, final RestController controller, final ThreadPool threadPool, final AdminDNs adminDns,
Path configPath, PrincipalExtractor principalExtractor) {
super();
this.threadContext = threadPool.getThreadContext();
this.adminDns = adminDns;
this.settings = settings;
this.configPath = configPath;
this.principalExtractor = principalExtractor;
}

@Override public List<Route> routes() {
return routes;
}

@Override protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
String[] configTypes = request.paramAsStringArrayOrEmptyIfAll("config_types");

SSLRequestHelper.SSLInfo sslInfo = SSLRequestHelper.getSSLInfo(settings, configPath, request, principalExtractor);

if (sslInfo == null) {
return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, ""));
}

final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER);

//only allowed for admins
if (user == null || !adminDns.isAdmin(user)) {
return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, ""));
} else {
ConfigUpdateRequest configUpdateRequest = new ConfigUpdateRequest(configTypes);
return channel -> {
client.execute(ConfigUpdateAction.INSTANCE, configUpdateRequest, new NodesResponseRestListener<>(channel));
};
}
}

@Override public String getName() {
return "Security config update";
}

}
121 changes: 121 additions & 0 deletions src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.security.rest;

import com.google.common.collect.ImmutableList;
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.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.transport.PrincipalExtractor;
import org.opensearch.security.ssl.util.SSLRequestHelper;
import org.opensearch.security.ssl.util.SSLRequestHelper.SSLInfo;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.support.WildcardMatcher;
import org.opensearch.threadpool.ThreadPool;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;

import static org.opensearch.rest.RestRequest.Method.GET;
import static org.opensearch.rest.RestRequest.Method.POST;
import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix;


public class SecurityWhoAmIAction extends BaseRestHandler {

private static final List<Route> routes = addRoutesPrefix(ImmutableList.of(
new Route(GET, "/whoami"),
new Route(POST, "/whoami")),
"/_plugins/_security");

private final Logger log = LogManager.getLogger(this.getClass());
private final AdminDNs adminDns;
private final Settings settings;
private final Path configPath;
private final PrincipalExtractor principalExtractor;
private final List<String> nodesDn ;

public SecurityWhoAmIAction(final Settings settings, final RestController controller,
final ThreadPool threadPool, final AdminDNs adminDns, Path configPath, PrincipalExtractor principalExtractor) {
super();
this.adminDns = adminDns;
this.settings = settings;
this.configPath = configPath;
this.principalExtractor = principalExtractor;

nodesDn = settings.getAsList(ConfigConstants.SECURITY_NODES_DN, Collections.emptyList());
}

@Override
public List<Route> routes() {
return routes;
}

@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;

try {

SSLInfo sslInfo = SSLRequestHelper.getSSLInfo(settings, configPath, request, principalExtractor);

if(sslInfo == null) {
response = new BytesRestResponse(RestStatus.FORBIDDEN, "No security data");
} else {

final String dn = sslInfo.getPrincipal();
final boolean isAdmin = adminDns.isAdminDN(dn);
final boolean isNodeCertificateRequest = dn != null && WildcardMatcher.from(nodesDn, true).matchAny(dn);

builder.startObject();
builder.field("dn", dn);
builder.field("is_admin", isAdmin);
builder.field("is_node_certificate_request", isNodeCertificateRequest);
builder.endObject();

response = new BytesRestResponse(RestStatus.OK, builder);

}
} catch (final Exception e1) {
log.error(e1.toString(), 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 "Security Plugin Who am i";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ public boolean matchAny(Collection<String> candidates) {
return matchAny(candidates.stream());
}

public boolean matchAny(String[] candidates) {
public boolean matchAny(String... candidates) {
return matchAny(Arrays.stream(candidates));
}

Expand Down
Loading

0 comments on commit 81a43c2

Please sign in to comment.