Skip to content

Commit 89c6684

Browse files
authored
[8.15] Revert "Fix BWC for file-settings based role mappings (#113900)" and related (#114326) (#114405)
# Backport This will backport the following commits from `main` to `8.15`: - [Revert "Fix BWC for file-settings based role mappings (#113900)" and related (#114326)](#114326)
1 parent ed457e4 commit 89c6684

File tree

15 files changed

+69
-313
lines changed

15 files changed

+69
-313
lines changed

docs/changelog/113900.yaml

Lines changed: 0 additions & 5 deletions
This file was deleted.

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/GetRoleMappingsResponse.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping;
1212

1313
import java.io.IOException;
14-
import java.util.Collection;
1514

1615
/**
1716
* Response to {@link GetRoleMappingsAction get role-mappings API}.
@@ -22,10 +21,6 @@ public class GetRoleMappingsResponse extends ActionResponse {
2221

2322
private final ExpressionRoleMapping[] mappings;
2423

25-
public GetRoleMappingsResponse(Collection<ExpressionRoleMapping> mappings) {
26-
this(mappings.toArray(new ExpressionRoleMapping[0]));
27-
}
28-
2924
public GetRoleMappingsResponse(ExpressionRoleMapping... mappings) {
3025
this.mappings = mappings;
3126
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.elasticsearch.cluster.AbstractNamedDiffable;
1313
import org.elasticsearch.cluster.ClusterState;
1414
import org.elasticsearch.cluster.NamedDiff;
15-
import org.elasticsearch.cluster.block.ClusterBlockLevel;
1615
import org.elasticsearch.cluster.metadata.Metadata;
1716
import org.elasticsearch.common.collect.Iterators;
1817
import org.elasticsearch.common.io.stream.StreamInput;
@@ -58,7 +57,6 @@ public final class RoleMappingMetadata extends AbstractNamedDiffable<Metadata.Cu
5857
private static final RoleMappingMetadata EMPTY = new RoleMappingMetadata(Set.of());
5958

6059
public static RoleMappingMetadata getFromClusterState(ClusterState clusterState) {
61-
clusterState.blocks().globalBlockedRaiseException(ClusterBlockLevel.READ);
6260
return clusterState.metadata().custom(RoleMappingMetadata.TYPE, RoleMappingMetadata.EMPTY);
6361
}
6462

x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/integration/RoleMappingFileSettingsIT.java

Lines changed: 45 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingAction;
3535
import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingRequest;
3636
import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingRequestBuilder;
37-
import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingResponse;
3837
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
3938
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
4039
import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping;
@@ -59,11 +58,12 @@
5958
import static org.elasticsearch.indices.recovery.RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING;
6059
import static org.elasticsearch.xcontent.XContentType.JSON;
6160
import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.INTERNAL_SECURITY_MAIN_INDEX_7;
62-
import static org.elasticsearch.xpack.security.authc.support.mapper.ClusterStateRoleMapper.RESERVED_ROLE_MAPPING_SUFFIX;
6361
import static org.hamcrest.Matchers.allOf;
62+
import static org.hamcrest.Matchers.contains;
6463
import static org.hamcrest.Matchers.containsInAnyOrder;
6564
import static org.hamcrest.Matchers.containsString;
6665
import static org.hamcrest.Matchers.empty;
66+
import static org.hamcrest.Matchers.emptyArray;
6767
import static org.hamcrest.Matchers.equalTo;
6868
import static org.hamcrest.Matchers.hasSize;
6969
import static org.hamcrest.Matchers.notNullValue;
@@ -270,28 +270,21 @@ private void assertRoleMappingsSaveOK(CountDownLatch savedClusterState, AtomicLo
270270
assertThat(resolveRolesFuture.get(), containsInAnyOrder("kibana_user", "fleet_user"));
271271
}
272272

273-
// the role mappings are retrievable by the role mapping action for BWC
274-
assertGetResponseHasMappings(true, "everyone_kibana", "everyone_fleet");
275-
276-
// role mappings (with the same names) can be stored in the "native" store
277-
{
278-
PutRoleMappingResponse response = client().execute(PutRoleMappingAction.INSTANCE, sampleRestRequest("everyone_kibana"))
279-
.actionGet();
280-
assertTrue(response.isCreated());
281-
response = client().execute(PutRoleMappingAction.INSTANCE, sampleRestRequest("everyone_fleet")).actionGet();
282-
assertTrue(response.isCreated());
283-
}
284-
{
285-
// deleting role mappings that exist in the native store and in cluster-state should result in success
286-
var response = client().execute(DeleteRoleMappingAction.INSTANCE, deleteRequest("everyone_kibana")).actionGet();
287-
assertTrue(response.isFound());
288-
response = client().execute(DeleteRoleMappingAction.INSTANCE, deleteRequest("everyone_fleet")).actionGet();
289-
assertTrue(response.isFound());
290-
}
291-
273+
// the role mappings are not retrievable by the role mapping action (which only accesses "native" i.e. index-based role mappings)
274+
var request = new GetRoleMappingsRequest();
275+
request.setNames("everyone_kibana", "everyone_fleet");
276+
var response = client().execute(GetRoleMappingsAction.INSTANCE, request).get();
277+
assertFalse(response.hasMappings());
278+
assertThat(response.mappings(), emptyArray());
279+
280+
// role mappings (with the same names) can also be stored in the "native" store
281+
var putRoleMappingResponse = client().execute(PutRoleMappingAction.INSTANCE, sampleRestRequest("everyone_kibana")).actionGet();
282+
assertTrue(putRoleMappingResponse.isCreated());
283+
putRoleMappingResponse = client().execute(PutRoleMappingAction.INSTANCE, sampleRestRequest("everyone_fleet")).actionGet();
284+
assertTrue(putRoleMappingResponse.isCreated());
292285
}
293286

294-
public void testClusterStateRoleMappingsAddedThenDeleted() throws Exception {
287+
public void testRoleMappingsApplied() throws Exception {
295288
ensureGreen();
296289

297290
var savedClusterState = setupClusterStateListener(internalCluster().getMasterName(), "everyone_kibana");
@@ -300,12 +293,6 @@ public void testClusterStateRoleMappingsAddedThenDeleted() throws Exception {
300293
assertRoleMappingsSaveOK(savedClusterState.v1(), savedClusterState.v2());
301294
logger.info("---> cleanup cluster settings...");
302295

303-
{
304-
// Deleting non-existent native role mappings returns not found even if they exist in config file
305-
var response = client().execute(DeleteRoleMappingAction.INSTANCE, deleteRequest("everyone_kibana")).get();
306-
assertFalse(response.isFound());
307-
}
308-
309296
savedClusterState = setupClusterStateListenerForCleanup(internalCluster().getMasterName());
310297

311298
writeJSONFile(internalCluster().getMasterName(), emptyJSON, logger, versionCounter);
@@ -320,95 +307,48 @@ public void testClusterStateRoleMappingsAddedThenDeleted() throws Exception {
320307
clusterStateResponse.getState().metadata().persistentSettings().get(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey())
321308
);
322309

323-
// cluster-state role mapping was removed and is not returned in the API anymore
310+
// native role mappings are not affected by the removal of the cluster-state based ones
324311
{
325312
var request = new GetRoleMappingsRequest();
326313
request.setNames("everyone_kibana", "everyone_fleet");
327314
var response = client().execute(GetRoleMappingsAction.INSTANCE, request).get();
328-
assertFalse(response.hasMappings());
315+
assertTrue(response.hasMappings());
316+
assertThat(
317+
Arrays.stream(response.mappings()).map(ExpressionRoleMapping::getName).toList(),
318+
containsInAnyOrder("everyone_kibana", "everyone_fleet")
319+
);
329320
}
330321

331-
// no role mappings means no roles are resolved
322+
// and roles are resolved based on the native role mappings
332323
for (UserRoleMapper userRoleMapper : internalCluster().getInstances(UserRoleMapper.class)) {
333324
PlainActionFuture<Set<String>> resolveRolesFuture = new PlainActionFuture<>();
334325
userRoleMapper.resolveRoles(
335326
new UserRoleMapper.UserData("anyUsername", null, List.of(), Map.of(), mock(RealmConfig.class)),
336327
resolveRolesFuture
337328
);
338-
assertThat(resolveRolesFuture.get(), empty());
329+
assertThat(resolveRolesFuture.get(), contains("kibana_user_native"));
339330
}
340-
}
341331

342-
public void testGetRoleMappings() throws Exception {
343-
ensureGreen();
344-
345-
final List<String> nativeMappings = List.of("everyone_kibana", "_everyone_kibana", "zzz_mapping", "123_mapping");
346-
for (var mapping : nativeMappings) {
347-
client().execute(PutRoleMappingAction.INSTANCE, sampleRestRequest(mapping)).actionGet();
332+
{
333+
var request = new DeleteRoleMappingRequest();
334+
request.setName("everyone_kibana");
335+
var response = client().execute(DeleteRoleMappingAction.INSTANCE, request).get();
336+
assertTrue(response.isFound());
337+
request = new DeleteRoleMappingRequest();
338+
request.setName("everyone_fleet");
339+
response = client().execute(DeleteRoleMappingAction.INSTANCE, request).get();
340+
assertTrue(response.isFound());
348341
}
349342

350-
var savedClusterState = setupClusterStateListener(internalCluster().getMasterName(), "everyone_kibana");
351-
writeJSONFile(internalCluster().getMasterName(), testJSON, logger, versionCounter);
352-
boolean awaitSuccessful = savedClusterState.v1().await(20, TimeUnit.SECONDS);
353-
assertTrue(awaitSuccessful);
354-
355-
var request = new GetRoleMappingsRequest();
356-
var response = client().execute(GetRoleMappingsAction.INSTANCE, request).get();
357-
assertTrue(response.hasMappings());
358-
assertThat(
359-
Arrays.stream(response.mappings()).map(ExpressionRoleMapping::getName).toList(),
360-
containsInAnyOrder(
361-
"everyone_kibana",
362-
"everyone_kibana" + RESERVED_ROLE_MAPPING_SUFFIX,
363-
"_everyone_kibana",
364-
"everyone_fleet" + RESERVED_ROLE_MAPPING_SUFFIX,
365-
"zzz_mapping",
366-
"123_mapping"
367-
)
368-
);
369-
370-
int readOnlyCount = 0;
371-
// assert that cluster-state role mappings come last
372-
for (ExpressionRoleMapping mapping : response.mappings()) {
373-
readOnlyCount = mapping.getName().endsWith(RESERVED_ROLE_MAPPING_SUFFIX) ? readOnlyCount + 1 : readOnlyCount;
343+
// no roles are resolved now, because both native and cluster-state based stores have been cleared
344+
for (UserRoleMapper userRoleMapper : internalCluster().getInstances(UserRoleMapper.class)) {
345+
PlainActionFuture<Set<String>> resolveRolesFuture = new PlainActionFuture<>();
346+
userRoleMapper.resolveRoles(
347+
new UserRoleMapper.UserData("anyUsername", null, List.of(), Map.of(), mock(RealmConfig.class)),
348+
resolveRolesFuture
349+
);
350+
assertThat(resolveRolesFuture.get(), empty());
374351
}
375-
// Two sourced from cluster-state
376-
assertEquals(readOnlyCount, 2);
377-
378-
// it's possible to delete overlapping native role mapping
379-
assertTrue(client().execute(DeleteRoleMappingAction.INSTANCE, deleteRequest("everyone_kibana")).actionGet().isFound());
380-
381-
// Fetch a specific file based role
382-
request = new GetRoleMappingsRequest();
383-
request.setNames("everyone_kibana" + RESERVED_ROLE_MAPPING_SUFFIX);
384-
response = client().execute(GetRoleMappingsAction.INSTANCE, request).get();
385-
assertTrue(response.hasMappings());
386-
assertThat(
387-
Arrays.stream(response.mappings()).map(ExpressionRoleMapping::getName).toList(),
388-
containsInAnyOrder("everyone_kibana" + RESERVED_ROLE_MAPPING_SUFFIX)
389-
);
390-
391-
savedClusterState = setupClusterStateListenerForCleanup(internalCluster().getMasterName());
392-
writeJSONFile(internalCluster().getMasterName(), emptyJSON, logger, versionCounter);
393-
awaitSuccessful = savedClusterState.v1().await(20, TimeUnit.SECONDS);
394-
assertTrue(awaitSuccessful);
395-
396-
final ClusterStateResponse clusterStateResponse = clusterAdmin().state(
397-
new ClusterStateRequest().waitForMetadataVersion(savedClusterState.v2().get())
398-
).get();
399-
400-
assertNull(
401-
clusterStateResponse.getState().metadata().persistentSettings().get(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey())
402-
);
403-
404-
// Make sure remaining native mappings can still be fetched
405-
request = new GetRoleMappingsRequest();
406-
response = client().execute(GetRoleMappingsAction.INSTANCE, request).get();
407-
assertTrue(response.hasMappings());
408-
assertThat(
409-
Arrays.stream(response.mappings()).map(ExpressionRoleMapping::getName).toList(),
410-
containsInAnyOrder("_everyone_kibana", "zzz_mapping", "123_mapping")
411-
);
412352
}
413353

414354
public static Tuple<CountDownLatch, AtomicLong> setupClusterStateListenerForError(
@@ -493,8 +433,11 @@ public void testRoleMappingApplyWithSecurityIndexClosed() throws Exception {
493433
boolean awaitSuccessful = savedClusterState.v1().await(20, TimeUnit.SECONDS);
494434
assertTrue(awaitSuccessful);
495435

496-
// even if index is closed, cluster-state role mappings are still returned
497-
assertGetResponseHasMappings(true, "everyone_kibana", "everyone_fleet");
436+
// no native role mappings exist
437+
var request = new GetRoleMappingsRequest();
438+
request.setNames("everyone_kibana", "everyone_fleet");
439+
var response = client().execute(GetRoleMappingsAction.INSTANCE, request).get();
440+
assertFalse(response.hasMappings());
498441

499442
// cluster state settings are also applied
500443
var clusterStateResponse = clusterAdmin().state(new ClusterStateRequest().waitForMetadataVersion(savedClusterState.v2().get()))
@@ -532,12 +475,6 @@ public void testRoleMappingApplyWithSecurityIndexClosed() throws Exception {
532475
}
533476
}
534477

535-
private DeleteRoleMappingRequest deleteRequest(String name) {
536-
var request = new DeleteRoleMappingRequest();
537-
request.setName(name);
538-
return request;
539-
}
540-
541478
private PutRoleMappingRequest sampleRestRequest(String name) throws Exception {
542479
var json = """
543480
{
@@ -556,17 +493,4 @@ private PutRoleMappingRequest sampleRestRequest(String name) throws Exception {
556493
return new PutRoleMappingRequestBuilder(null).source(name, parser).request();
557494
}
558495
}
559-
560-
private static void assertGetResponseHasMappings(boolean readOnly, String... mappings) throws InterruptedException, ExecutionException {
561-
var request = new GetRoleMappingsRequest();
562-
request.setNames(mappings);
563-
var response = client().execute(GetRoleMappingsAction.INSTANCE, request).get();
564-
assertTrue(response.hasMappings());
565-
assertThat(
566-
Arrays.stream(response.mappings()).map(ExpressionRoleMapping::getName).toList(),
567-
containsInAnyOrder(
568-
Arrays.stream(mappings).map(mapping -> mapping + (readOnly ? RESERVED_ROLE_MAPPING_SUFFIX : "")).toArray(String[]::new)
569-
)
570-
);
571-
}
572496
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -893,8 +893,7 @@ Collection<Object> createComponents(
893893
reservedRealm
894894
);
895895
components.add(nativeUsersStore);
896-
components.add(clusterStateRoleMapper);
897-
components.add(nativeRoleMappingStore);
896+
components.add(new PluginComponentBinding<>(NativeRoleMappingStore.class, nativeRoleMappingStore));
898897
components.add(new PluginComponentBinding<>(UserRoleMapper.class, userRoleMapper));
899898
components.add(reservedRealm);
900899
components.add(realms);

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/role/TransportDeleteRoleAction.java

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@
1111
import org.elasticsearch.action.support.ActionFilters;
1212
import org.elasticsearch.action.support.TransportAction;
1313
import org.elasticsearch.common.inject.Inject;
14-
import org.elasticsearch.common.logging.HeaderWarning;
1514
import org.elasticsearch.tasks.Task;
1615
import org.elasticsearch.transport.TransportService;
1716
import org.elasticsearch.xpack.core.security.action.role.DeleteRoleAction;
1817
import org.elasticsearch.xpack.core.security.action.role.DeleteRoleRequest;
1918
import org.elasticsearch.xpack.core.security.action.role.DeleteRoleResponse;
20-
import org.elasticsearch.xpack.security.authc.support.mapper.ClusterStateRoleMapper;
2119
import org.elasticsearch.xpack.security.authz.ReservedRoleNameChecker;
2220
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
2321

@@ -26,20 +24,16 @@ public class TransportDeleteRoleAction extends TransportAction<DeleteRoleRequest
2624
private final NativeRolesStore rolesStore;
2725
private final ReservedRoleNameChecker reservedRoleNameChecker;
2826

29-
private final ClusterStateRoleMapper clusterStateRoleMapper;
30-
3127
@Inject
3228
public TransportDeleteRoleAction(
3329
ActionFilters actionFilters,
3430
NativeRolesStore rolesStore,
3531
TransportService transportService,
36-
ReservedRoleNameChecker reservedRoleNameChecker,
37-
ClusterStateRoleMapper clusterStateRoleMapper
32+
ReservedRoleNameChecker reservedRoleNameChecker
3833
) {
3934
super(DeleteRoleAction.NAME, actionFilters, transportService.getTaskManager());
4035
this.rolesStore = rolesStore;
4136
this.reservedRoleNameChecker = reservedRoleNameChecker;
42-
this.clusterStateRoleMapper = clusterStateRoleMapper;
4337
}
4438

4539
@Override
@@ -50,19 +44,7 @@ protected void doExecute(Task task, DeleteRoleRequest request, ActionListener<De
5044
}
5145

5246
try {
53-
rolesStore.deleteRole(request, listener.safeMap((found) -> {
54-
if (clusterStateRoleMapper.hasMapping(request.name())) {
55-
// Allow to delete a mapping with the same name in the native role mapping store as the file_settings namespace, but
56-
// add a warning header to signal to the caller that this could be a problem.
57-
HeaderWarning.addWarning(
58-
"A read only role mapping with the same name ["
59-
+ request.name()
60-
+ "] has been previously been defined in a configuration file. "
61-
+ "The read only role mapping will still be active."
62-
);
63-
}
64-
return new DeleteRoleResponse(found);
65-
}));
47+
rolesStore.deleteRole(request, listener.safeMap(DeleteRoleResponse::new));
6648
} catch (Exception e) {
6749
logger.error((Supplier<?>) () -> "failed to delete role [" + request.name() + "]", e);
6850
listener.onFailure(e);

0 commit comments

Comments
 (0)