Skip to content

Commit

Permalink
Also handle get builtin privileges API
Browse files Browse the repository at this point in the history
  • Loading branch information
n1v0lg committed Feb 15, 2024
1 parent a853d54 commit 30339d9
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,12 @@

import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.common.io.stream.StreamInput;

import java.io.IOException;

/**
* Request to retrieve built-in (cluster/index) privileges.
*/
public final class GetBuiltinPrivilegesRequest extends ActionRequest {

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

public GetBuiltinPrivilegesRequest() {}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
package org.elasticsearch.xpack.core.security.action.privilege;

import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.support.TransportAction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;

import java.io.IOException;
Expand Down Expand Up @@ -37,12 +37,6 @@ public GetBuiltinPrivilegesResponse() {
this(Collections.emptySet(), Collections.emptySet());
}

public GetBuiltinPrivilegesResponse(StreamInput in) throws IOException {
super(in);
this.clusterPrivileges = in.readStringArray();
this.indexPrivileges = in.readStringArray();
}

public String[] getClusterPrivileges() {
return clusterPrivileges;
}
Expand All @@ -53,7 +47,6 @@ public String[] getIndexPrivileges() {

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeStringArray(clusterPrivileges);
out.writeStringArray(indexPrivileges);
TransportAction.localOnly();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.core.security.action.privilege;

public interface GetBuiltinPrivilegesResponseTranslator {

GetBuiltinPrivilegesResponse translate(GetBuiltinPrivilegesResponse response, boolean restrictResponse);

class Default implements GetBuiltinPrivilegesResponseTranslator {
public GetBuiltinPrivilegesResponse translate(GetBuiltinPrivilegesResponse response, boolean restrictResponse) {
assert false == restrictResponse;
return response;
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
import org.elasticsearch.xpack.core.security.action.privilege.ClearPrivilegesCacheAction;
import org.elasticsearch.xpack.core.security.action.privilege.DeletePrivilegesAction;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesAction;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesResponseTranslator;
import org.elasticsearch.xpack.core.security.action.privilege.GetPrivilegesAction;
import org.elasticsearch.xpack.core.security.action.privilege.PutPrivilegesAction;
import org.elasticsearch.xpack.core.security.action.profile.ActivateProfileAction;
Expand Down Expand Up @@ -560,6 +561,7 @@ public class Security extends Plugin
private final SetOnce<ScriptService> scriptServiceReference = new SetOnce<>();
private final SetOnce<OperatorOnlyRegistry> operatorOnlyRegistry = new SetOnce<>();
private final SetOnce<PutRoleRequestBuilderFactory> putRoleRequestBuilderFactory = new SetOnce<>();
private final SetOnce<GetBuiltinPrivilegesResponseTranslator> getBuiltinPrivilegesResponseTranslator = new SetOnce<>();
private final SetOnce<FileRolesStore> fileRolesStore = new SetOnce<>();
private final SetOnce<OperatorPrivileges.OperatorPrivilegesService> operatorPrivilegesService = new SetOnce<>();
private final SetOnce<ReservedRoleMappingAction> reservedRoleMappingAction = new SetOnce<>();
Expand Down Expand Up @@ -820,6 +822,10 @@ Collection<Object> createComponents(
putRoleRequestBuilderFactory.set(new PutRoleRequestBuilderFactory.Default());
}

if (getBuiltinPrivilegesResponseTranslator.get() == null) {
getBuiltinPrivilegesResponseTranslator.set(new GetBuiltinPrivilegesResponseTranslator.Default());
}

final Map<String, List<BiConsumer<Set<String>, ActionListener<RoleRetrievalResult>>>> customRoleProviders = new LinkedHashMap<>();
for (SecurityExtension extension : securityExtensions) {
final List<BiConsumer<Set<String>, ActionListener<RoleRetrievalResult>>> providers = extension.getRolesProviders(
Expand Down Expand Up @@ -1446,7 +1452,7 @@ public List<RestHandler> getRestHandlers(
new RestOpenIdConnectPrepareAuthenticationAction(settings, getLicenseState()),
new RestOpenIdConnectAuthenticateAction(settings, getLicenseState()),
new RestOpenIdConnectLogoutAction(settings, getLicenseState()),
new RestGetBuiltinPrivilegesAction(settings, getLicenseState()),
new RestGetBuiltinPrivilegesAction(settings, getLicenseState(), getBuiltinPrivilegesResponseTranslator.get()),
new RestGetPrivilegesAction(settings, getLicenseState()),
new RestPutPrivilegesAction(settings, getLicenseState()),
new RestDeletePrivilegesAction(settings, getLicenseState()),
Expand Down Expand Up @@ -2030,33 +2036,21 @@ public void accept(DiscoveryNode node, ClusterState state) {
@Override
public void loadExtensions(ExtensionLoader loader) {
securityExtensions.addAll(loader.loadExtensions(SecurityExtension.class));
loadSingletonExtensionAndSetOnce(loader, operatorOnlyRegistry, OperatorOnlyRegistry.class);
loadSingletonExtensionAndSetOnce(loader, putRoleRequestBuilderFactory, PutRoleRequestBuilderFactory.class);
loadSingletonExtensionAndSetOnce(loader, getBuiltinPrivilegesResponseTranslator, GetBuiltinPrivilegesResponseTranslator.class);
}

// operator registry SPI
List<OperatorOnlyRegistry> operatorOnlyRegistries = loader.loadExtensions(OperatorOnlyRegistry.class);
if (operatorOnlyRegistries.size() > 1) {
throw new IllegalStateException(OperatorOnlyRegistry.class + " may not have multiple implementations");
} else if (operatorOnlyRegistries.size() == 1) {
OperatorOnlyRegistry operatorOnlyRegistry = operatorOnlyRegistries.get(0);
this.operatorOnlyRegistry.set(operatorOnlyRegistry);
logger.debug(
"Loaded implementation [{}] for interface OperatorOnlyRegistry",
operatorOnlyRegistry.getClass().getCanonicalName()
);
}

List<PutRoleRequestBuilderFactory> builderFactories = loader.loadExtensions(PutRoleRequestBuilderFactory.class);
if (builderFactories.size() > 1) {
throw new IllegalStateException(PutRoleRequestBuilderFactory.class + " may not have multiple implementations");
} else if (builderFactories.size() == 1) {
PutRoleRequestBuilderFactory builderFactory = builderFactories.get(0);
this.putRoleRequestBuilderFactory.set(builderFactory);
logger.debug(
"Loaded implementation [{}] for interface [{}]",
builderFactory.getClass().getCanonicalName(),
PutRoleRequestBuilderFactory.class
);
private <T> void loadSingletonExtensionAndSetOnce(ExtensionLoader loader, SetOnce<T> setOnce, Class<T> clazz) {
final List<T> loaded = loader.loadExtensions(clazz);
if (loaded.size() > 1) {
throw new IllegalStateException(clazz + " may not have multiple implementations");
} else if (loaded.size() == 1) {
final T singleLoaded = loaded.get(0);
setOnce.set(singleLoaded);
logger.debug("Loaded implementation [{}] for interface [{}]", singleLoaded.getClass().getCanonicalName(), clazz);
} else {
logger.debug("Will fall back on default implementation for interface [{}]", PutRoleRequestBuilderFactory.class);
logger.debug("Will fall back on default implementation for interface [{}]", clazz);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.action.support.TransportAction;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesAction;
Expand All @@ -22,19 +21,13 @@
import java.util.TreeSet;

/**
* Transport action to retrieve one or more application privileges from the security index
* Transport action to retrieve built-in (cluster/index) privileges
*/
public class TransportGetBuiltinPrivilegesAction extends HandledTransportAction<GetBuiltinPrivilegesRequest, GetBuiltinPrivilegesResponse> {
public class TransportGetBuiltinPrivilegesAction extends TransportAction<GetBuiltinPrivilegesRequest, GetBuiltinPrivilegesResponse> {

@Inject
public TransportGetBuiltinPrivilegesAction(ActionFilters actionFilters, TransportService transportService) {
super(
GetBuiltinPrivilegesAction.NAME,
transportService,
actionFilters,
GetBuiltinPrivilegesRequest::new,
EsExecutors.DIRECT_EXECUTOR_SERVICE
);
super(GetBuiltinPrivilegesAction.NAME, actionFilters, transportService.getTaskManager());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
*/
package org.elasticsearch.xpack.security.rest.action.privilege;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.client.internal.node.NodeClient;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.rest.RestRequest;
Expand All @@ -19,6 +23,8 @@
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesAction;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesRequest;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesResponse;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesResponseTranslator;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;

import java.io.IOException;
Expand All @@ -27,13 +33,21 @@
import static org.elasticsearch.rest.RestRequest.Method.GET;

/**
* Rest action to retrieve an application privilege from the security index
* Rest action to retrieve built-in (cluster/index) privileges
*/
@ServerlessScope(Scope.INTERNAL)
@ServerlessScope(Scope.PUBLIC)
public class RestGetBuiltinPrivilegesAction extends SecurityBaseRestHandler {

public RestGetBuiltinPrivilegesAction(Settings settings, XPackLicenseState licenseState) {
private static final Logger logger = LogManager.getLogger(RestGetBuiltinPrivilegesAction.class);
private final GetBuiltinPrivilegesResponseTranslator responseTranslator;

public RestGetBuiltinPrivilegesAction(
Settings settings,
XPackLicenseState licenseState,
GetBuiltinPrivilegesResponseTranslator responseTranslator
) {
super(settings, licenseState);
this.responseTranslator = responseTranslator;
}

@Override
Expand All @@ -48,20 +62,45 @@ public String getName() {

@Override
public RestChannelConsumer innerPrepareRequest(RestRequest request, NodeClient client) throws IOException {
final boolean restrictResponse = request.hasParam(RestRequest.RESPONSE_RESTRICTED);
return channel -> client.execute(
GetBuiltinPrivilegesAction.INSTANCE,
new GetBuiltinPrivilegesRequest(),
new RestBuilderListener<>(channel) {
@Override
public RestResponse buildResponse(GetBuiltinPrivilegesResponse response, XContentBuilder builder) throws Exception {
final var translatedResponse = responseTranslator.translate(response, restrictResponse);
builder.startObject();
builder.array("cluster", response.getClusterPrivileges());
builder.array("index", response.getIndexPrivileges());
builder.array("cluster", translatedResponse.getClusterPrivileges());
builder.array("index", translatedResponse.getIndexPrivileges());
builder.endObject();
return new RestResponse(RestStatus.OK, builder);
}
}
);
}

@Override
protected Exception innerCheckFeatureAvailable(RestRequest request) {
final boolean restrictPath = request.hasParam(RestRequest.RESPONSE_RESTRICTED);
assert false == restrictPath || DiscoveryNode.isStateless(settings);
if (false == restrictPath) {
return super.innerCheckFeatureAvailable(request);
}
// This is a temporary hack: we are re-using the native roles setting as an overall feature flag for custom roles.
final Boolean nativeRolesEnabled = settings.getAsBoolean(NativeRolesStore.NATIVE_ROLES_ENABLED, true);
if (nativeRolesEnabled == false) {
logger.debug(
"Attempt to call [{} {}] but [{}] is [{}]",
request.method(),
request.rawPath(),
NativeRolesStore.NATIVE_ROLES_ENABLED,
settings.get(NativeRolesStore.NATIVE_ROLES_ENABLED)
);
return new ElasticsearchStatusException("This API is not enabled on this Elasticsearch instance", RestStatus.GONE);
} else {
return null;
}
}

}

0 comments on commit 30339d9

Please sign in to comment.