Skip to content

Commit b7ef3eb

Browse files
committed
[CCR] Add create_follow_index privilege (#33559)
This is a new index privilege that the user needs to have in the follow cluster. This privilege is required in addition to the `manage_ccr` cluster privilege in order to execute the create and follow api. Closes #33555
1 parent 3bc7e1c commit b7ef3eb

File tree

4 files changed

+37
-9
lines changed

4 files changed

+37
-9
lines changed

x-pack/plugin/ccr/qa/multi-cluster-with-security/roles.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ ccruser:
77
- monitor
88
- read
99
- write
10+
- create_follow_index

x-pack/plugin/ccr/qa/multi-cluster-with-security/src/test/java/org/elasticsearch/xpack/ccr/FollowIndexSecurityIT.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.apache.http.util.EntityUtils;
99
import org.elasticsearch.client.Request;
1010
import org.elasticsearch.client.Response;
11+
import org.elasticsearch.client.ResponseException;
1112
import org.elasticsearch.client.RestClient;
1213
import org.elasticsearch.common.Booleans;
1314
import org.elasticsearch.common.Strings;
@@ -18,6 +19,7 @@
1819
import org.elasticsearch.common.xcontent.XContentHelper;
1920
import org.elasticsearch.common.xcontent.json.JsonXContent;
2021
import org.elasticsearch.common.xcontent.support.XContentMapValues;
22+
import org.elasticsearch.rest.RestStatus;
2123
import org.elasticsearch.test.rest.ESRestTestCase;
2224

2325
import java.io.IOException;
@@ -26,7 +28,9 @@
2628

2729
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
2830
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
31+
import static org.hamcrest.Matchers.containsString;
2932
import static org.hamcrest.Matchers.equalTo;
33+
import static org.hamcrest.Matchers.is;
3034

3135
public class FollowIndexSecurityIT extends ESRestTestCase {
3236

@@ -96,16 +100,19 @@ public void testFollowIndex() throws Exception {
96100
assertThat(countCcrNodeTasks(), equalTo(0));
97101
});
98102

99-
createAndFollowIndex("leader_cluster:" + unallowedIndex, unallowedIndex);
100-
// Verify that nothing has been replicated and no node tasks are running
101-
// These node tasks should have been failed due to the fact that the user
102-
// has no sufficient priviledges.
103+
Exception e = expectThrows(ResponseException.class,
104+
() -> createAndFollowIndex("leader_cluster:" + unallowedIndex, unallowedIndex));
105+
assertThat(e.getMessage(),
106+
containsString("action [indices:admin/xpack/ccr/create_and_follow_index] is unauthorized for user [test_ccr]"));
107+
// Verify that the follow index has not been created and no node tasks are running
108+
assertThat(indexExists(adminClient(), unallowedIndex), is(false));
103109
assertBusy(() -> assertThat(countCcrNodeTasks(), equalTo(0)));
104-
verifyDocuments(adminClient(), unallowedIndex, 0);
105110

106-
followIndex("leader_cluster:" + unallowedIndex, unallowedIndex);
111+
e = expectThrows(ResponseException.class,
112+
() -> followIndex("leader_cluster:" + unallowedIndex, unallowedIndex));
113+
assertThat(e.getMessage(), containsString("follow index [" + unallowedIndex + "] does not exist"));
114+
assertThat(indexExists(adminClient(), unallowedIndex), is(false));
107115
assertBusy(() -> assertThat(countCcrNodeTasks(), equalTo(0)));
108-
verifyDocuments(adminClient(), unallowedIndex, 0);
109116
}
110117
}
111118

@@ -191,4 +198,9 @@ protected static void createIndex(String name, Settings settings, String mapping
191198
assertOK(adminClient().performRequest(request));
192199
}
193200

201+
private static boolean indexExists(RestClient client, String index) throws IOException {
202+
Response response = client.performRequest(new Request("HEAD", "/" + index));
203+
return RestStatus.OK.getStatus() == response.getStatusLine().getStatusCode();
204+
}
205+
194206
}

x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/CreateAndFollowIndexAction.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
import org.elasticsearch.action.ActionRequestBuilder;
1414
import org.elasticsearch.action.ActionRequestValidationException;
1515
import org.elasticsearch.action.ActionResponse;
16+
import org.elasticsearch.action.IndicesRequest;
1617
import org.elasticsearch.action.support.ActionFilters;
1718
import org.elasticsearch.action.support.ActiveShardCount;
1819
import org.elasticsearch.action.support.ActiveShardsObserver;
20+
import org.elasticsearch.action.support.IndicesOptions;
1921
import org.elasticsearch.action.support.master.AcknowledgedRequest;
2022
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
2123
import org.elasticsearch.client.Client;
@@ -55,7 +57,7 @@ public class CreateAndFollowIndexAction extends Action<CreateAndFollowIndexActio
5557
CreateAndFollowIndexAction.RequestBuilder> {
5658

5759
public static final CreateAndFollowIndexAction INSTANCE = new CreateAndFollowIndexAction();
58-
public static final String NAME = "cluster:admin/xpack/ccr/create_and_follow_index";
60+
public static final String NAME = "indices:admin/xpack/ccr/create_and_follow_index";
5961

6062
private CreateAndFollowIndexAction() {
6163
super(NAME);
@@ -71,7 +73,7 @@ public Response newResponse() {
7173
return new Response();
7274
}
7375

74-
public static class Request extends AcknowledgedRequest<Request> {
76+
public static class Request extends AcknowledgedRequest<Request> implements IndicesRequest {
7577

7678
private FollowIndexAction.Request followRequest;
7779

@@ -91,6 +93,16 @@ public ActionRequestValidationException validate() {
9193
return followRequest.validate();
9294
}
9395

96+
@Override
97+
public String[] indices() {
98+
return new String[]{followRequest.getFollowerIndex()};
99+
}
100+
101+
@Override
102+
public IndicesOptions indicesOptions() {
103+
return IndicesOptions.strictSingleIndexNoExpandForbidClosed();
104+
}
105+
94106
@Override
95107
public void readFrom(StreamInput in) throws IOException {
96108
super.readFrom(in);

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/IndexPrivilege.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public final class IndexPrivilege extends Privilege {
5555
private static final Automaton VIEW_METADATA_AUTOMATON = patterns(GetAliasesAction.NAME, AliasesExistAction.NAME,
5656
GetIndexAction.NAME, IndicesExistsAction.NAME, GetFieldMappingsAction.NAME + "*", GetMappingsAction.NAME,
5757
ClusterSearchShardsAction.NAME, TypesExistsAction.NAME, ValidateQueryAction.NAME + "*", GetSettingsAction.NAME);
58+
private static final Automaton CREATE_FOLLOW_INDEX_AUTOMATON = patterns("indices:admin/xpack/ccr/create_and_follow_index");
5859

5960
public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY);
6061
public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON);
@@ -69,6 +70,7 @@ public final class IndexPrivilege extends Privilege {
6970
public static final IndexPrivilege DELETE_INDEX = new IndexPrivilege("delete_index", DELETE_INDEX_AUTOMATON);
7071
public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", CREATE_INDEX_AUTOMATON);
7172
public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata", VIEW_METADATA_AUTOMATON);
73+
public static final IndexPrivilege CREATE_FOLLOW_INDEX = new IndexPrivilege("create_follow_index", CREATE_FOLLOW_INDEX_AUTOMATON);
7274

7375
private static final Map<String, IndexPrivilege> VALUES = MapBuilder.<String, IndexPrivilege>newMapBuilder()
7476
.put("none", NONE)
@@ -84,6 +86,7 @@ public final class IndexPrivilege extends Privilege {
8486
.put("delete_index", DELETE_INDEX)
8587
.put("view_index_metadata", VIEW_METADATA)
8688
.put("read_cross_cluster", READ_CROSS_CLUSTER)
89+
.put("create_follow_index", CREATE_FOLLOW_INDEX)
8790
.immutableMap();
8891

8992
public static final Predicate<String> ACTION_MATCHER = ALL.predicate();

0 commit comments

Comments
 (0)