mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-09 06:25:07 +00:00
Introduced an API to clear realms caches
Since both LDAP and AD realms are caching users. If the groups of the users change on the LDAP side, these changes will not be visible in shield until the relevant cached users will be evicted from cache. This poses a problem, specially when degrading users in terms of their permission (e.g. after degrading them on LDAP, they still have higher privileges until they're evicted from cache). The default cache timeout today is 1 hour. For this reason, a new API is introduced which will enable administrators to force cache evictions. - Changed the default cache timeout to 20 minute - `ClearRealmCacheAction was introduced (along with the relevant request and response constructs). This is a cluster action - the corresponding rest action was introduced as well, under the `_shield/realm/{realm}/cache/clear` URI (where `{realm}` enables clearing specific realms, or all realms when passing `_all`. - With the introduction of an action, the `ActionModule` now is no longer a node module - it's bound on both node and transport client. - Added a new Cluster permission - `manage_shield` - Also cleaned up the `Permission` and `AuthorizationService` class Original commit: elastic/x-pack-elasticsearch@c59e244435
This commit is contained in:
parent
be768d5a44
commit
2f373f692f
@ -35,6 +35,7 @@ public class ShieldModule extends AbstractShieldModule.Spawn {
|
||||
// spawn needed parts in client mode
|
||||
if (clientMode) {
|
||||
return ImmutableList.<Module>of(
|
||||
new ShieldActionModule(settings),
|
||||
new SecuredTransportModule(settings),
|
||||
new SSLModule(settings));
|
||||
}
|
||||
|
@ -9,12 +9,14 @@ import org.elasticsearch.action.ActionModule;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.inject.PreProcessModule;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheAction;
|
||||
import org.elasticsearch.shield.action.authc.cache.TransportClearRealmCacheAction;
|
||||
import org.elasticsearch.shield.support.AbstractShieldModule;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ShieldActionModule extends AbstractShieldModule.Node implements PreProcessModule {
|
||||
public class ShieldActionModule extends AbstractShieldModule implements PreProcessModule {
|
||||
|
||||
public ShieldActionModule(Settings settings) {
|
||||
super(settings);
|
||||
@ -22,14 +24,23 @@ public class ShieldActionModule extends AbstractShieldModule.Node implements Pre
|
||||
|
||||
@Override
|
||||
public void processModule(Module module) {
|
||||
if (!clientMode && module instanceof ActionModule) {
|
||||
((ActionModule) module).registerFilter(ShieldActionFilter.class);
|
||||
if (module instanceof ActionModule) {
|
||||
|
||||
// registering the security filter only for nodes
|
||||
if (!clientMode) {
|
||||
((ActionModule) module).registerFilter(ShieldActionFilter.class);
|
||||
}
|
||||
|
||||
// registering all shield actions
|
||||
((ActionModule) module).registerAction(ClearRealmCacheAction.INSTANCE, TransportClearRealmCacheAction.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureNode() {
|
||||
// we need to ensure that there's only a single instance of this filter.
|
||||
bind(ShieldActionFilter.class).asEagerSingleton();
|
||||
protected void configure(boolean clientMode) {
|
||||
if (!clientMode) {
|
||||
// we need to ensure that there's only a single instance of this filter.
|
||||
bind(ShieldActionFilter.class).asEagerSingleton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
32
src/main/java/org/elasticsearch/shield/action/authc/cache/ClearRealmCacheAction.java
vendored
Normal file
32
src/main/java/org/elasticsearch/shield/action/authc/cache/ClearRealmCacheAction.java
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.action.authc.cache;
|
||||
|
||||
import org.elasticsearch.action.admin.cluster.ClusterAction;
|
||||
import org.elasticsearch.client.ClusterAdminClient;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ClearRealmCacheAction extends ClusterAction<ClearRealmCacheRequest, ClearRealmCacheResponse, ClearRealmCacheRequestBuilder> {
|
||||
|
||||
public static final ClearRealmCacheAction INSTANCE = new ClearRealmCacheAction();
|
||||
public static final String NAME = "cluster:admin/shield/realm/cache/clear";
|
||||
|
||||
protected ClearRealmCacheAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClearRealmCacheRequestBuilder newRequestBuilder(ClusterAdminClient client) {
|
||||
return new ClearRealmCacheRequestBuilder(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClearRealmCacheResponse newResponse() {
|
||||
return new ClearRealmCacheResponse();
|
||||
}
|
||||
}
|
115
src/main/java/org/elasticsearch/shield/action/authc/cache/ClearRealmCacheRequest.java
vendored
Normal file
115
src/main/java/org/elasticsearch/shield/action/authc/cache/ClearRealmCacheRequest.java
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.action.authc.cache;
|
||||
|
||||
import org.elasticsearch.action.support.nodes.NodeOperationRequest;
|
||||
import org.elasticsearch.action.support.nodes.NodesOperationRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ClearRealmCacheRequest extends NodesOperationRequest<ClearRealmCacheRequest> {
|
||||
|
||||
String[] realms;
|
||||
String[] usernames;
|
||||
|
||||
/**
|
||||
* @return {@code true} if this request targets realms, {@code false} otherwise.
|
||||
*/
|
||||
public boolean allRealms() {
|
||||
return realms == null || realms.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The realms that should be evicted. Empty array indicates all realms.
|
||||
*/
|
||||
public String[] realms() {
|
||||
return realms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the realms for which caches will be evicted. When not set all the caches of all realms will be
|
||||
* evicted.
|
||||
*
|
||||
* @param realms The realm names
|
||||
*/
|
||||
public ClearRealmCacheRequest realms(String... realms) {
|
||||
this.realms = realms;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if this request targets users, {@code false} otherwise.
|
||||
*/
|
||||
public boolean allUsernames() {
|
||||
return usernames == null || usernames.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The usernames of the users that should be evicted. Empty array indicates all users.
|
||||
*/
|
||||
public String[] usernames() {
|
||||
return usernames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the usernames of the users that should be evicted from the caches. When not set, all users
|
||||
* will be evicted.
|
||||
*
|
||||
* @param usernames The usernames
|
||||
*/
|
||||
public ClearRealmCacheRequest usernames(String... usernames) {
|
||||
this.usernames = usernames;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
realms = in.readStringArray();
|
||||
usernames = in.readStringArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeStringArrayNullable(realms);
|
||||
out.writeStringArrayNullable(usernames);
|
||||
}
|
||||
|
||||
static class Node extends NodeOperationRequest {
|
||||
|
||||
String[] realms;
|
||||
String[] usernames;
|
||||
|
||||
Node() {
|
||||
}
|
||||
|
||||
Node(ClearRealmCacheRequest request, String nodeId) {
|
||||
super(request, nodeId);
|
||||
this.realms = request.realms;
|
||||
this.usernames = request.usernames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
realms = in.readStringArray();
|
||||
usernames = in.readStringArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeStringArrayNullable(realms);
|
||||
out.writeStringArrayNullable(usernames);
|
||||
}
|
||||
}
|
||||
}
|
57
src/main/java/org/elasticsearch/shield/action/authc/cache/ClearRealmCacheRequestBuilder.java
vendored
Normal file
57
src/main/java/org/elasticsearch/shield/action/authc/cache/ClearRealmCacheRequestBuilder.java
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.action.authc.cache;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.support.nodes.NodesOperationRequestBuilder;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.ClusterAdminClient;
|
||||
import org.elasticsearch.shield.client.ShieldAuthcClient;
|
||||
import org.elasticsearch.shield.client.ShieldClient;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ClearRealmCacheRequestBuilder extends NodesOperationRequestBuilder<ClearRealmCacheRequest, ClearRealmCacheResponse, ClearRealmCacheRequestBuilder> {
|
||||
|
||||
private final ShieldAuthcClient authcClient;
|
||||
|
||||
public ClearRealmCacheRequestBuilder(Client client) {
|
||||
this(client.admin().cluster());
|
||||
}
|
||||
|
||||
public ClearRealmCacheRequestBuilder(ClusterAdminClient client) {
|
||||
super(client, new ClearRealmCacheRequest());
|
||||
authcClient = new ShieldClient(client).authc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the realms for which caches will be evicted. When not set all the caches of all realms will be
|
||||
* evicted.
|
||||
*
|
||||
* @param realms The realm names
|
||||
*/
|
||||
public ClearRealmCacheRequestBuilder realms(String... realms) {
|
||||
request.realms(realms);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the usernames of the users that should be evicted from the caches. When not set, all users
|
||||
* will be evicted.
|
||||
*
|
||||
* @param usernames The usernames
|
||||
*/
|
||||
public ClearRealmCacheRequestBuilder usernames(String... usernames) {
|
||||
request.usernames(usernames);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(ActionListener<ClearRealmCacheResponse> listener) {
|
||||
authcClient.clearRealmCache(request, listener);
|
||||
}
|
||||
}
|
91
src/main/java/org/elasticsearch/shield/action/authc/cache/ClearRealmCacheResponse.java
vendored
Normal file
91
src/main/java/org/elasticsearch/shield/action/authc/cache/ClearRealmCacheResponse.java
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.action.authc.cache;
|
||||
|
||||
import org.elasticsearch.action.support.nodes.NodeOperationResponse;
|
||||
import org.elasticsearch.action.support.nodes.NodesOperationResponse;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ClearRealmCacheResponse extends NodesOperationResponse<ClearRealmCacheResponse.Node> implements ToXContent {
|
||||
|
||||
public ClearRealmCacheResponse() {
|
||||
}
|
||||
|
||||
public ClearRealmCacheResponse(ClusterName clusterName, Node[] nodes) {
|
||||
super(clusterName, nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
nodes = new Node[in.readVInt()];
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
nodes[i] = Node.readNodeResponse(in);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeVInt(nodes.length);
|
||||
for (Node node : nodes) {
|
||||
node.writeTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.field("cluster_name", getClusterName().value());
|
||||
builder.startObject("nodes");
|
||||
for (ClearRealmCacheResponse.Node node: getNodes()) {
|
||||
builder.startObject(node.getNode().id());
|
||||
builder.field("name", node.getNode().name());
|
||||
builder.endObject();
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder().prettyPrint();
|
||||
builder.startObject();
|
||||
toXContent(builder, EMPTY_PARAMS);
|
||||
builder.endObject();
|
||||
return builder.string();
|
||||
} catch (IOException e) {
|
||||
return "{ \"error\" : \"" + e.getMessage() + "\"}";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Node extends NodeOperationResponse {
|
||||
|
||||
Node() {
|
||||
}
|
||||
|
||||
Node(DiscoveryNode node) {
|
||||
super(node);
|
||||
}
|
||||
|
||||
public static Node readNodeResponse(StreamInput in) throws IOException {
|
||||
Node node = new Node();
|
||||
node.readFrom(in);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
117
src/main/java/org/elasticsearch/shield/action/authc/cache/TransportClearRealmCacheAction.java
vendored
Normal file
117
src/main/java/org/elasticsearch/shield/action/authc/cache/TransportClearRealmCacheAction.java
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.action.authc.cache;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.nodes.TransportNodesOperationAction;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.common.collect.Lists;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.authc.Realm;
|
||||
import org.elasticsearch.shield.authc.RealmMissingException;
|
||||
import org.elasticsearch.shield.authc.Realms;
|
||||
import org.elasticsearch.shield.authc.support.CachingUsernamePasswordRealm;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class TransportClearRealmCacheAction extends TransportNodesOperationAction<ClearRealmCacheRequest, ClearRealmCacheResponse, ClearRealmCacheRequest.Node, ClearRealmCacheResponse.Node> {
|
||||
|
||||
private final Realms realms;
|
||||
|
||||
@Inject
|
||||
public TransportClearRealmCacheAction(Settings settings, ClusterName clusterName, ThreadPool threadPool,
|
||||
ClusterService clusterService, TransportService transportService,
|
||||
ActionFilters actionFilters, Realms realms) {
|
||||
super(settings, ClearRealmCacheAction.NAME, clusterName, threadPool, clusterService, transportService, actionFilters);
|
||||
this.realms = realms;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String executor() {
|
||||
return ThreadPool.Names.MANAGEMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClearRealmCacheRequest newRequest() {
|
||||
return new ClearRealmCacheRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClearRealmCacheResponse newResponse(ClearRealmCacheRequest request, AtomicReferenceArray responses) {
|
||||
final List<ClearRealmCacheResponse.Node> nodes = Lists.newArrayList();
|
||||
for (int i = 0; i < responses.length(); i++) {
|
||||
Object resp = responses.get(i);
|
||||
if (resp instanceof ClearRealmCacheResponse.Node) {
|
||||
nodes.add((ClearRealmCacheResponse.Node) resp);
|
||||
}
|
||||
}
|
||||
return new ClearRealmCacheResponse(clusterName, nodes.toArray(new ClearRealmCacheResponse.Node[nodes.size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClearRealmCacheRequest.Node newNodeRequest() {
|
||||
return new ClearRealmCacheRequest.Node();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClearRealmCacheRequest.Node newNodeRequest(String nodeId, ClearRealmCacheRequest request) {
|
||||
return new ClearRealmCacheRequest.Node(request, nodeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClearRealmCacheResponse.Node newNodeResponse() {
|
||||
return new ClearRealmCacheResponse.Node();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClearRealmCacheResponse.Node nodeOperation(ClearRealmCacheRequest.Node nodeRequest) throws ElasticsearchException {
|
||||
if (nodeRequest.realms == null || nodeRequest.realms.length == 0) {
|
||||
for (Realm realm : realms) {
|
||||
clearCache(realm, nodeRequest.usernames);
|
||||
}
|
||||
return new ClearRealmCacheResponse.Node(clusterService.localNode());
|
||||
}
|
||||
|
||||
for (String realmName : nodeRequest.realms) {
|
||||
Realm realm = realms.realm(realmName);
|
||||
if (realm == null) {
|
||||
throw new RealmMissingException("Could not find active realm [" + realmName + "]");
|
||||
}
|
||||
clearCache(realm, nodeRequest.usernames);
|
||||
}
|
||||
return new ClearRealmCacheResponse.Node(clusterService.localNode());
|
||||
}
|
||||
|
||||
private void clearCache(Realm realm, String[] usernames) {
|
||||
if (!(realm instanceof CachingUsernamePasswordRealm)) {
|
||||
return;
|
||||
}
|
||||
CachingUsernamePasswordRealm cachingRealm = (CachingUsernamePasswordRealm) realm;
|
||||
|
||||
if (usernames != null && usernames.length != 0) {
|
||||
for (String username : usernames) {
|
||||
cachingRealm.expire(username);
|
||||
}
|
||||
} else {
|
||||
cachingRealm.expireAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean accumulateExceptions() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.authc;
|
||||
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.shield.ShieldException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class RealmMissingException extends ShieldException {
|
||||
|
||||
public RealmMissingException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestStatus status() {
|
||||
return RestStatus.NOT_FOUND;
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@ import org.apache.lucene.util.CollectionUtil;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.collect.Lists;
|
||||
import org.elasticsearch.common.collect.Sets;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
@ -26,7 +25,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
public class Realms extends AbstractLifecycleComponent<Realms> implements Iterable<Realm> {
|
||||
|
||||
private final Map<String, Realm.Factory> factories;
|
||||
private List<Realm> realms = Collections.EMPTY_LIST;
|
||||
private List<Realm> realms = Collections.emptyList();
|
||||
|
||||
@Inject
|
||||
public Realms(Settings settings, Map<String, Realm.Factory> factories) {
|
||||
@ -50,6 +49,15 @@ public class Realms extends AbstractLifecycleComponent<Realms> implements Iterab
|
||||
return realms.iterator();
|
||||
}
|
||||
|
||||
public Realm realm(String name) {
|
||||
for (Realm realm : realms) {
|
||||
if (name.equals(realm.name)) {
|
||||
return realm;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Realm.Factory realmFactory(String type) {
|
||||
return factories.get(type);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm {
|
||||
|
||||
private static final TimeValue DEFAULT_TTL = TimeValue.timeValueHours(1);
|
||||
private static final TimeValue DEFAULT_TTL = TimeValue.timeValueMinutes(20);
|
||||
private static final int DEFAULT_MAX_USERS = 100000; //100k users
|
||||
public static final String CACHE_TTL = "cache.ttl";
|
||||
public static final String CACHE_MAX_USERS = "cache.max_users";
|
||||
@ -41,13 +41,13 @@ public abstract class CachingUsernamePasswordRealm extends UsernamePasswordRealm
|
||||
}
|
||||
}
|
||||
|
||||
protected final void expire(String username) {
|
||||
public final void expire(String username) {
|
||||
if (cache != null) {
|
||||
cache.invalidate(username);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void expireAll() {
|
||||
public final void expireAll() {
|
||||
if (cache != null) {
|
||||
cache.invalidateAll();
|
||||
}
|
||||
|
@ -92,27 +92,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A
|
||||
throw denial(user, action, request);
|
||||
}
|
||||
|
||||
String[] roleNames = user.roles();
|
||||
if (roleNames.length == 0) {
|
||||
throw denial(user, action, request);
|
||||
}
|
||||
|
||||
Permission.Global permission;
|
||||
if (roleNames.length == 1) {
|
||||
permission = rolesStore.role(roleNames[0]);
|
||||
} else {
|
||||
|
||||
// we'll take all the roles and combine their associated permissions
|
||||
|
||||
Permission.Global.Compound.Builder roles = Permission.Global.Compound.builder();
|
||||
for (String roleName : roleNames) {
|
||||
Permission.Global role = rolesStore.role(roleName);
|
||||
if (role != null) {
|
||||
roles.add(role);
|
||||
}
|
||||
}
|
||||
permission = roles.build();
|
||||
}
|
||||
Permission.Global permission = permission(user);
|
||||
|
||||
// permission can be null as it might be that the user's role
|
||||
// is unknown
|
||||
@ -168,6 +148,29 @@ public class InternalAuthorizationService extends AbstractComponent implements A
|
||||
grant(user, action, request);
|
||||
}
|
||||
|
||||
private Permission.Global permission(User user) {
|
||||
String[] roleNames = user.roles();
|
||||
if (roleNames.length == 0) {
|
||||
return Permission.Global.NONE;
|
||||
}
|
||||
|
||||
if (roleNames.length == 1) {
|
||||
Permission.Global.Role role = rolesStore.role(roleNames[0]);
|
||||
return role == null ? Permission.Global.NONE : role;
|
||||
}
|
||||
|
||||
// we'll take all the roles and combine their associated permissions
|
||||
|
||||
Permission.Global.Compound.Builder roles = Permission.Global.Compound.builder();
|
||||
for (String roleName : roleNames) {
|
||||
Permission.Global role = rolesStore.role(roleName);
|
||||
if (role != null) {
|
||||
roles.add(role);
|
||||
}
|
||||
}
|
||||
return roles.build();
|
||||
}
|
||||
|
||||
private AuthorizationException denial(User user, String action, TransportRequest request) {
|
||||
auditTrail.accessDenied(user, action, request);
|
||||
return new AuthorizationException("Action [" + action + "] is unauthorized for user [" + user.principal() + "]");
|
||||
|
@ -38,7 +38,9 @@ public interface Permission {
|
||||
|
||||
boolean isEmpty();
|
||||
|
||||
static abstract class Global implements Permission {
|
||||
static class Global implements Permission {
|
||||
|
||||
public static Global NONE = new Global(Cluster.Core.NONE, Indices.Core.NONE);
|
||||
|
||||
private final Cluster cluster;
|
||||
private final Indices indices;
|
||||
|
@ -204,13 +204,14 @@ public abstract class Privilege<P extends Privilege<P>> {
|
||||
|
||||
public static class Cluster extends AutomatonPrivilege<Cluster> {
|
||||
|
||||
public static final Cluster NONE = new Cluster(Name.NONE, Automata.makeEmpty());
|
||||
public static final Cluster ALL = new Cluster(Name.ALL, "cluster:*", "indices:admin/template/*");
|
||||
public static final Cluster MONITOR = new Cluster("monitor", "cluster:monitor/*");
|
||||
public static final Cluster NONE = new Cluster(Name.NONE, Automata.makeEmpty());
|
||||
public static final Cluster ALL = new Cluster(Name.ALL, "cluster:*", "indices:admin/template/*");
|
||||
public static final Cluster MONITOR = new Cluster("monitor", "cluster:monitor/*");
|
||||
public static final Cluster MANAGE_SHIELD = new Cluster("manage_shield", "cluster:admin/shield/*");
|
||||
|
||||
final static Predicate<String> ACTION_MATCHER = Privilege.Cluster.ALL.predicate();
|
||||
|
||||
private static final Cluster[] values = new Cluster[] { NONE, ALL, MONITOR };
|
||||
private static final Cluster[] values = new Cluster[] { NONE, ALL, MONITOR, MANAGE_SHIELD };
|
||||
|
||||
static Cluster[] values() {
|
||||
return values;
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.client;
|
||||
|
||||
import org.elasticsearch.action.ActionFuture;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.client.ClusterAdminClient;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheAction;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheRequest;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheRequestBuilder;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheResponse;
|
||||
|
||||
/**
|
||||
* A client to manage Shield's authentication
|
||||
*/
|
||||
public class ShieldAuthcClient {
|
||||
|
||||
private final ClusterAdminClient client;
|
||||
|
||||
ShieldAuthcClient(ClusterAdminClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the realm caches. It's possible to clear all user entries from all realms in the cluster or alternatively
|
||||
* select the realms (by their unique names) and/or users (by their usernames) that should be evicted.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ClearRealmCacheRequestBuilder prepareClearRealmCache() {
|
||||
return new ClearRealmCacheRequestBuilder(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the realm caches. It's possible to clear all user entries from all realms in the cluster or alternatively
|
||||
* select the realms (by their unique names) and/or users (by their usernames) that should be evicted.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void clearRealmCache(ClearRealmCacheRequest request, ActionListener<ClearRealmCacheResponse> listener) {
|
||||
client.execute(ClearRealmCacheAction.INSTANCE, request, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the realm caches. It's possible to clear all user entries from all realms in the cluster or alternatively
|
||||
* select the realms (by their unique names) and/or users (by their usernames) that should be evicted.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ActionFuture<ClearRealmCacheResponse> clearRealmCache(ClearRealmCacheRequest request) {
|
||||
return client.execute(ClearRealmCacheAction.INSTANCE, request);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.client;
|
||||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.ClusterAdminClient;
|
||||
|
||||
/**
|
||||
* A wrapper to elasticsearch clients that exposes all Shield related APIs
|
||||
*/
|
||||
public class ShieldClient {
|
||||
|
||||
private final ShieldAuthcClient authcClient;
|
||||
|
||||
public ShieldClient(Client client) {
|
||||
this(client.admin().cluster());
|
||||
}
|
||||
|
||||
public ShieldClient(ClusterAdminClient client) {
|
||||
this.authcClient = new ShieldAuthcClient(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Shield authenticatin client.
|
||||
*/
|
||||
public ShieldAuthcClient authc() {
|
||||
return authcClient;
|
||||
}
|
||||
|
||||
}
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.inject.PreProcessModule;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.rest.RestModule;
|
||||
import org.elasticsearch.shield.rest.action.RestShieldInfoAction;
|
||||
import org.elasticsearch.shield.rest.action.authc.cache.RestClearRealmCacheAction;
|
||||
import org.elasticsearch.shield.support.AbstractShieldModule;
|
||||
|
||||
/**
|
||||
@ -30,6 +31,7 @@ public class ShieldRestModule extends AbstractShieldModule.Node implements PrePr
|
||||
public void processModule(Module module) {
|
||||
if (module instanceof RestModule) {
|
||||
((RestModule) module).addRestAction(RestShieldInfoAction.class);
|
||||
((RestModule) module).addRestAction(RestClearRealmCacheAction.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
46
src/main/java/org/elasticsearch/shield/rest/action/authc/cache/RestClearRealmCacheAction.java
vendored
Normal file
46
src/main/java/org/elasticsearch/shield/rest/action/authc/cache/RestClearRealmCacheAction.java
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.shield.rest.action.authc.cache;
|
||||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.rest.*;
|
||||
import org.elasticsearch.rest.action.support.RestBuilderListener;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheRequest;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheResponse;
|
||||
import org.elasticsearch.shield.client.ShieldClient;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||
|
||||
public class RestClearRealmCacheAction extends BaseRestHandler {
|
||||
|
||||
@Inject
|
||||
public RestClearRealmCacheAction(Settings settings, RestController controller, Client client) {
|
||||
super(settings, controller, client);
|
||||
controller.registerHandler(POST, "/_shield/realm/{realms}/_cache/clear", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRequest(RestRequest request, final RestChannel channel, Client client) throws Exception {
|
||||
|
||||
String[] realms = request.paramAsStringArrayOrEmptyIfAll("realms");
|
||||
String[] usernames = request.paramAsStringArrayOrEmptyIfAll("usernames");
|
||||
|
||||
ClearRealmCacheRequest req = new ClearRealmCacheRequest().realms(realms).usernames(usernames);
|
||||
|
||||
new ShieldClient(client).authc().clearRealmCache(req, new RestBuilderListener<ClearRealmCacheResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(ClearRealmCacheResponse response, XContentBuilder builder) throws Exception {
|
||||
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
return new BytesRestResponse(RestStatus.OK, builder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.integration;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheRequest;
|
||||
import org.elasticsearch.shield.action.authc.cache.ClearRealmCacheResponse;
|
||||
import org.elasticsearch.shield.authc.Realm;
|
||||
import org.elasticsearch.shield.authc.Realms;
|
||||
import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.shield.client.ShieldClient;
|
||||
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import org.elasticsearch.test.ShieldIntegrationTest;
|
||||
import org.elasticsearch.test.ShieldSettingsSource;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.SUITE;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@ClusterScope(scope = SUITE)
|
||||
public class ClearRealmsCacheTests extends ShieldIntegrationTest {
|
||||
|
||||
private static String[] usernames;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
usernames = new String[randomIntBetween(5, 10)];
|
||||
for (int i = 0; i < usernames.length; i++) {
|
||||
usernames[i] = "user_" + i;
|
||||
}
|
||||
}
|
||||
|
||||
enum Scenario {
|
||||
|
||||
EVICT_ALL() {
|
||||
|
||||
@Override
|
||||
public ClearRealmCacheRequest createRequest() {
|
||||
return new ClearRealmCacheRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertEviction(User prevUser, User newUser) {
|
||||
assertThat(prevUser, not(sameInstance(newUser)));
|
||||
}
|
||||
},
|
||||
|
||||
EVICT_SOME() {
|
||||
|
||||
private final String[] evicted_usernames = randomSelection(usernames);
|
||||
|
||||
@Override
|
||||
public ClearRealmCacheRequest createRequest() {
|
||||
return new ClearRealmCacheRequest().usernames(evicted_usernames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertEviction(User prevUser, User newUser) {
|
||||
if (Arrays.binarySearch(evicted_usernames, prevUser.principal()) >= 0) {
|
||||
assertThat(prevUser, not(sameInstance(newUser)));
|
||||
} else {
|
||||
assertThat(prevUser, sameInstance(newUser));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public abstract ClearRealmCacheRequest createRequest();
|
||||
|
||||
public abstract void assertEviction(User prevUser, User newUser);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String configRoles() {
|
||||
return ShieldSettingsSource.CONFIG_ROLE_ALLOW_ALL + "\n" +
|
||||
"r1:\n" +
|
||||
" cluster: all\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String configUsers() {
|
||||
StringBuilder builder = new StringBuilder(ShieldSettingsSource.CONFIG_STANDARD_USER);
|
||||
for (String username : usernames) {
|
||||
builder.append(username).append(":{plain}passwd\n");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String configUsersRoles() {
|
||||
return ShieldSettingsSource.CONFIG_STANDARD_USER_ROLES +
|
||||
"r1:" + Strings.arrayToCommaDelimitedString(usernames);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEvictAll() throws Exception {
|
||||
testScenario(Scenario.EVICT_ALL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEvictSome() throws Exception {
|
||||
testScenario(Scenario.EVICT_SOME);
|
||||
}
|
||||
|
||||
private void testScenario(Scenario scenario) throws Exception {
|
||||
|
||||
Map<String, UsernamePasswordToken> tokens = new HashMap<>();
|
||||
for (String user : usernames) {
|
||||
tokens.put(user, new UsernamePasswordToken(user, SecuredStringTests.build("passwd")));
|
||||
}
|
||||
|
||||
List<Realm> realms = new ArrayList<>();
|
||||
for (Realms nodeRealms : internalCluster().getInstances(Realms.class)) {
|
||||
realms.add(nodeRealms.realm("esusers"));
|
||||
}
|
||||
|
||||
|
||||
// we authenticate each user on each of the realms to make sure they're all cached
|
||||
Map<String, Map<Realm, User>> users = new HashMap<>();
|
||||
for (Realm realm : realms) {
|
||||
for (String username : usernames) {
|
||||
User user = realm.authenticate(tokens.get(username));
|
||||
assertThat(user, notNullValue());
|
||||
Map<Realm, User> realmToUser = users.get(username);
|
||||
if (realmToUser == null) {
|
||||
realmToUser = new HashMap<>();
|
||||
users.put(username, realmToUser);
|
||||
}
|
||||
realmToUser.put(realm, user);
|
||||
}
|
||||
}
|
||||
|
||||
// all users should be cached now on all realms, lets verify
|
||||
|
||||
for (String username : usernames) {
|
||||
for (Realm realm : realms) {
|
||||
assertThat(realm.authenticate(tokens.get(username)), sameInstance(users.get(username).get(realm)));
|
||||
}
|
||||
}
|
||||
|
||||
// now, lets run the scenario
|
||||
|
||||
ShieldClient client = new ShieldClient(client());
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
client.authc().clearRealmCache(scenario.createRequest(), new ActionListener<ClearRealmCacheResponse>() {
|
||||
@Override
|
||||
public void onResponse(ClearRealmCacheResponse response) {
|
||||
assertThat(response.getNodes().length, equalTo(internalCluster().getNodeNames().length));
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
error.set(e);
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
if (!latch.await(5, TimeUnit.SECONDS)) {
|
||||
fail("waiting for clear realms cache request too long");
|
||||
}
|
||||
|
||||
if (error.get() != null) {
|
||||
logger.error("Failed to clear realm caches", error.get());
|
||||
fail("failed to clear realm caches");
|
||||
}
|
||||
|
||||
// now, user_a should have been evicted, but user_b should still be cached
|
||||
for (String username : usernames) {
|
||||
for (Realm realm : realms) {
|
||||
User user = realm.authenticate(tokens.get(username));
|
||||
assertThat(user, notNullValue());
|
||||
scenario.assertEviction(users.get(username).get(realm), user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// selects a random sub-set of the give values
|
||||
private static String[] randomSelection(String[] values) {
|
||||
double base = randomDouble();
|
||||
List<String> list = new ArrayList<>();
|
||||
for (String value : values) {
|
||||
if (randomDouble() < base) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import org.elasticsearch.ElasticsearchIllegalStateException;
|
||||
import org.elasticsearch.action.Action;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.util.Callback;
|
||||
import org.elasticsearch.shield.action.ShieldActionModule;
|
||||
import org.elasticsearch.test.ShieldIntegrationTest;
|
||||
import org.elasticsearch.license.plugin.LicensePlugin;
|
||||
import org.junit.BeforeClass;
|
||||
@ -29,17 +30,17 @@ public class KnownActionsTests extends ShieldIntegrationTest {
|
||||
|
||||
private static ImmutableSet<String> knownActions;
|
||||
private static ImmutableSet<String> knownHandlers;
|
||||
private static ImmutableSet<String> externalActions;
|
||||
private static ImmutableSet<String> codeActions;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() throws Exception {
|
||||
knownActions = loadKnownActions();
|
||||
knownHandlers = loadKnownHandlers();
|
||||
externalActions = loadExternalActions();
|
||||
codeActions = loadCodeActions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllExternalTransportHandlersAreKnown() {
|
||||
public void testAllTransportHandlersAreKnown() {
|
||||
TransportService transportService = internalCluster().getDataNodeInstance(TransportService.class);
|
||||
for (String handler : transportService.serverHandlers.keySet()) {
|
||||
if (!knownActions.contains(handler)) {
|
||||
@ -49,8 +50,8 @@ public class KnownActionsTests extends ShieldIntegrationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllExternalActionsAreKnown() throws Exception {
|
||||
for (String action : externalActions) {
|
||||
public void testAllCodeActionsAreKnown() throws Exception {
|
||||
for (String action : codeActions) {
|
||||
assertThat("elasticsearch core action [" + action + "] is unknown to shield", knownActions, hasItem(action));
|
||||
}
|
||||
}
|
||||
@ -58,7 +59,7 @@ public class KnownActionsTests extends ShieldIntegrationTest {
|
||||
@Test
|
||||
public void testAllKnownActionsAreValid() {
|
||||
for (String knownAction : knownActions) {
|
||||
assertThat("shield known action [" + knownAction + "] is unknown to core", externalActions, hasItems(knownAction));
|
||||
assertThat("shield known action [" + knownAction + "] is unknown to core", codeActions, hasItems(knownAction));
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,13 +101,17 @@ public class KnownActionsTests extends ShieldIntegrationTest {
|
||||
return knownHandlersBuilder.build();
|
||||
}
|
||||
|
||||
private static ImmutableSet<String> loadExternalActions() throws IOException, IllegalAccessException {
|
||||
private static ImmutableSet<String> loadCodeActions() throws IOException, IllegalAccessException {
|
||||
ImmutableSet.Builder<String> actions = ImmutableSet.builder();
|
||||
|
||||
// loading es core actions
|
||||
ClassPath classPath = ClassPath.from(Action.class.getClassLoader());
|
||||
loadActions(classPath, Action.class.getPackage().getName(), actions);
|
||||
|
||||
// loading shield actions
|
||||
classPath = ClassPath.from(ShieldActionModule.class.getClassLoader());
|
||||
loadActions(classPath, ShieldActionModule.class.getPackage().getName(), actions);
|
||||
|
||||
// also loading all actions from the licensing plugin
|
||||
classPath = ClassPath.from(LicensePlugin.class.getClassLoader());
|
||||
loadActions(classPath, LicensePlugin.class.getPackage().getName(), actions);
|
||||
|
@ -76,4 +76,5 @@ indices:data/write/script/put
|
||||
indices:data/write/update
|
||||
cluster:admin/plugin/license/get
|
||||
cluster:admin/plugin/license/delete
|
||||
cluster:admin/plugin/license/put
|
||||
cluster:admin/plugin/license/put
|
||||
cluster:admin/shield/realm/cache/clear
|
@ -6,6 +6,8 @@ cluster:monitor/nodes/hot_threads[n]
|
||||
cluster:monitor/nodes/info[n]
|
||||
cluster:monitor/nodes/stats[n]
|
||||
cluster:monitor/stats[n]
|
||||
cluster:admin/shield/realm/cache/clear
|
||||
cluster:admin/shield/realm/cache/clear[n]
|
||||
indices:admin/analyze[s]
|
||||
indices:admin/cache/clear[s]
|
||||
indices:admin/flush[s]
|
||||
@ -83,4 +85,4 @@ internal:index/shard/recovery/start_recovery
|
||||
internal:index/shard/recovery/translog_ops
|
||||
internal:river/state/publish
|
||||
internal:admin/repository/verify
|
||||
internal:plugin/license/cluster/register_trial_license
|
||||
internal:plugin/license/cluster/register_trial_license
|
Loading…
x
Reference in New Issue
Block a user