diff --git a/server/src/main/java/org/elasticsearch/TransportVersions.java b/server/src/main/java/org/elasticsearch/TransportVersions.java index 93fa590ebe97f..f627272951487 100644 --- a/server/src/main/java/org/elasticsearch/TransportVersions.java +++ b/server/src/main/java/org/elasticsearch/TransportVersions.java @@ -224,6 +224,7 @@ static TransportVersion def(int id) { * */ public static final TransportVersion ESQL_ATTRIBUTE_CACHED_SERIALIZATION_8_15 = def(8_702_00_3); + public static final TransportVersion ADD_ROLE_MAPPING_CLEANUP_TASK = def(8_702_00_4); /* * STOP! READ THIS FIRST! No, really, diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java index a2c3e40c76ae4..f30a8b2f14803 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java @@ -89,6 +89,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.RemoteClusterPermissions; import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege; import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges; +import org.elasticsearch.xpack.core.security.support.RoleMappingCleanupTaskParams; import org.elasticsearch.xpack.core.security.support.SecurityMigrationTaskParams; import org.elasticsearch.xpack.core.slm.SLMFeatureSetUsage; import org.elasticsearch.xpack.core.slm.SnapshotLifecycleMetadata; @@ -304,6 +305,11 @@ public List getNamedWriteables() { PersistentTaskParams.class, SecurityMigrationTaskParams.TASK_NAME, SecurityMigrationTaskParams::new + ), + new NamedWriteableRegistry.Entry( + PersistentTaskParams.class, + RoleMappingCleanupTaskParams.TASK_NAME, + RoleMappingCleanupTaskParams::new ) ).filter(Objects::nonNull).toList(); } @@ -382,6 +388,11 @@ public List getNamedXContent() { new ParseField(SecurityMigrationTaskParams.TASK_NAME), SecurityMigrationTaskParams::fromXContent ), + new NamedXContentRegistry.Entry( + PersistentTaskParams.class, + new ParseField(RoleMappingCleanupTaskParams.TASK_NAME), + RoleMappingCleanupTaskParams::fromXContent + ), new NamedXContentRegistry.Entry( Metadata.Custom.class, new ParseField(RoleMappingMetadata.TYPE), diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleMappingMetadata.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleMappingMetadata.java index 8f78fdbccd923..a4e4d004d1e47 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleMappingMetadata.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleMappingMetadata.java @@ -12,7 +12,6 @@ import org.elasticsearch.cluster.AbstractNamedDiffable; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.NamedDiff; -import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.Metadata; import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.common.io.stream.StreamInput; @@ -34,16 +33,23 @@ import static org.elasticsearch.cluster.metadata.Metadata.ALL_CONTEXTS; import static org.elasticsearch.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.xcontent.ConstructingObjectParser.optionalConstructorArg; public final class RoleMappingMetadata extends AbstractNamedDiffable implements Metadata.Custom { public static final String TYPE = "role_mappings"; + public static int ROLE_MAPPING_METADATA_VERSION = 1; + + private static final ParseField VERSION_PARSE_FIELD = new ParseField("version"); @SuppressWarnings("unchecked") private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( TYPE, // serialization tests rely on the order of the ExpressionRoleMapping - args -> new RoleMappingMetadata(new LinkedHashSet<>((Collection) args[0])) + args -> new RoleMappingMetadata( + new LinkedHashSet<>((Collection) args[0]), + args[1] == null ? 0 : (int) args[1] + ) ); static { @@ -53,23 +59,27 @@ public final class RoleMappingMetadata extends AbstractNamedDiffable ExpressionRoleMapping.parse("name_not_available_after_deserialization", p), new ParseField(TYPE) ); + PARSER.declareIntOrNull(optionalConstructorArg(), 0, VERSION_PARSE_FIELD); } - private static final RoleMappingMetadata EMPTY = new RoleMappingMetadata(Set.of()); + private static final RoleMappingMetadata EMPTY = new RoleMappingMetadata(Set.of(), 0); public static RoleMappingMetadata getFromClusterState(ClusterState clusterState) { - clusterState.blocks().globalBlockedRaiseException(ClusterBlockLevel.READ); return clusterState.metadata().custom(RoleMappingMetadata.TYPE, RoleMappingMetadata.EMPTY); } private final Set roleMappings; - public RoleMappingMetadata(Set roleMappings) { + private final int version; + + public RoleMappingMetadata(Set roleMappings, int version) { + this.version = version; this.roleMappings = roleMappings; } public RoleMappingMetadata(StreamInput input) throws IOException { this.roleMappings = input.readCollectionAsSet(ExpressionRoleMapping::new); + this.version = input.readOptionalInt(); } public Set getRoleMappings() { @@ -96,13 +106,22 @@ public static NamedDiff readDiffFrom(StreamInput streamInput) t @Override public Iterator toXContentChunked(ToXContent.Params params) { // role mappings are serialized without their names - return Iterators.concat(ChunkedToXContentHelper.startArray(TYPE), roleMappings.iterator(), ChunkedToXContentHelper.endArray()); + return Iterators.concat( + ChunkedToXContentHelper.field(VERSION_PARSE_FIELD.getPreferredName(), version), + ChunkedToXContentHelper.startArray(TYPE), + roleMappings.iterator(), + ChunkedToXContentHelper.endArray() + ); } public static RoleMappingMetadata fromXContent(XContentParser parser) throws IOException { return PARSER.apply(parser, null); } + public int getVersion() { + return this.version; + } + @Override public String getWriteableName() { return TYPE; @@ -123,17 +142,17 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final var other = (RoleMappingMetadata) o; - return Objects.equals(roleMappings, other.roleMappings); + return version == other.version && Objects.equals(roleMappings, other.roleMappings); } @Override public int hashCode() { - return Objects.hash(roleMappings); + return Objects.hash(roleMappings, version); } @Override public String toString() { - StringBuilder builder = new StringBuilder("RoleMapping[entries=["); + StringBuilder builder = new StringBuilder("RoleMapping[version=[" + version + "],entries=["); final Iterator entryList = roleMappings.iterator(); boolean firstEntry = true; while (entryList.hasNext()) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/RoleMappingCleanupTaskParams.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/RoleMappingCleanupTaskParams.java new file mode 100644 index 0000000000000..29555d026789a --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/RoleMappingCleanupTaskParams.java @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.core.security.support; + +import org.elasticsearch.TransportVersion; +import org.elasticsearch.TransportVersions; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.persistent.PersistentTaskParams; +import org.elasticsearch.xcontent.ObjectParser; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.XContentParser; + +import java.io.IOException; + +public class RoleMappingCleanupTaskParams implements PersistentTaskParams { + public static final String TASK_NAME = "role-mapping-cleanup"; + + public static final ObjectParser PARSER = new ObjectParser<>( + TASK_NAME, + true, + RoleMappingCleanupTaskParams::new + ); + + public RoleMappingCleanupTaskParams() {} + + public RoleMappingCleanupTaskParams(StreamInput in) throws IOException {} + + @Override + public void writeTo(StreamOutput out) throws IOException {} + + @Override + public String getWriteableName() { + return TASK_NAME; + } + + @Override + public TransportVersion getMinimalSupportedVersion() { + return TransportVersions.ADD_ROLE_MAPPING_CLEANUP_TASK; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.endObject(); + return builder; + } + + public static RoleMappingCleanupTaskParams fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } +} diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/jwt/JwtRoleMappingsIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/jwt/JwtRoleMappingsIntegTests.java index 0a4a379e3a060..b1efd7d186bf8 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/jwt/JwtRoleMappingsIntegTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/jwt/JwtRoleMappingsIntegTests.java @@ -446,7 +446,7 @@ private SignedJWT getSignedJWT(JWTClaimsSet claimsSet) throws Exception { } private void publishRoleMappings(Set roleMappings) throws InterruptedException { - RoleMappingMetadata roleMappingMetadata = new RoleMappingMetadata(roleMappings); + RoleMappingMetadata roleMappingMetadata = new RoleMappingMetadata(roleMappings, 0); List clusterServices = new ArrayList<>(); internalCluster().getInstances(ClusterService.class).forEach(clusterServices::add); CountDownLatch publishedClusterState = new CountDownLatch(clusterServices.size()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 233c9dbfac35f..f5bcdde002178 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -217,6 +217,7 @@ import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult; import org.elasticsearch.xpack.core.security.support.Automatons; +import org.elasticsearch.xpack.core.security.support.RoleMappingCleanupTaskParams; import org.elasticsearch.xpack.core.security.support.SecurityMigrationTaskParams; import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings; @@ -409,6 +410,7 @@ import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.ExtensionComponents; import org.elasticsearch.xpack.security.support.ReloadableSecurityComponent; +import org.elasticsearch.xpack.security.support.RoleMappingCleanupExecutor; import org.elasticsearch.xpack.security.support.SecurityIndexManager; import org.elasticsearch.xpack.security.support.SecurityMigrationExecutor; import org.elasticsearch.xpack.security.support.SecurityMigrations; @@ -629,6 +631,7 @@ public class Security extends Plugin private final SetOnce secondaryAuthActions = new SetOnce<>(); private final SetOnce securityMigrationExecutor = new SetOnce<>(); + private final SetOnce roleMappingCleanupExecutor = new SetOnce<>(); // Node local retry count for migration jobs that's checked only on the master node to make sure // submit migration jobs doesn't get out of hand and retries forever if they fail. Reset by a @@ -853,6 +856,15 @@ Collection createComponents( systemIndices.getMainIndexManager(), scriptService ); + this.roleMappingCleanupExecutor.set( + new RoleMappingCleanupExecutor( + clusterService, + RoleMappingCleanupTaskParams.TASK_NAME, + threadPool.executor(ThreadPool.Names.MANAGEMENT), + nativeRoleMappingStore, + systemIndices.getMainIndexManager() + ) + ); final ClusterStateRoleMapper clusterStateRoleMapper = new ClusterStateRoleMapper(settings, scriptService, clusterService); final UserRoleMapper userRoleMapper = new CompositeRoleMapper(nativeRoleMappingStore, clusterStateRoleMapper); final AnonymousUser anonymousUser = new AnonymousUser(settings); @@ -1198,7 +1210,7 @@ Collection createComponents( new SecurityUsageServices(realms, allRolesStore, nativeRoleMappingStore, ipFilter.get(), profileService, apiKeyService) ); - reservedRoleMappingAction.set(new ReservedRoleMappingAction()); + reservedRoleMappingAction.set(new ReservedRoleMappingAction(persistentTasksService)); cacheInvalidatorRegistry.validate(); @@ -2345,7 +2357,15 @@ public List> getPersistentTasksExecutor( SettingsModule settingsModule, IndexNameExpressionResolver expressionResolver ) { - return this.securityMigrationExecutor.get() != null ? List.of(this.securityMigrationExecutor.get()) : List.of(); + List> executors = new ArrayList<>(); + if (this.securityMigrationExecutor.get() != null) { + executors.add(this.securityMigrationExecutor.get()); + } + if (this.roleMappingCleanupExecutor.get() != null) { + executors.add(this.roleMappingCleanupExecutor.get()); + } + + return executors; } List> reservedClusterStateHandlers() { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/rolemapping/ReservedRoleMappingAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/rolemapping/ReservedRoleMappingAction.java index 73d1a1abcdb50..42ed4d4c797d9 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/rolemapping/ReservedRoleMappingAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/rolemapping/ReservedRoleMappingAction.java @@ -7,7 +7,13 @@ package org.elasticsearch.xpack.security.action.rolemapping; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.ResourceAlreadyExistsException; +import org.elasticsearch.action.ActionListener; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.persistent.PersistentTasksService; import org.elasticsearch.reservedstate.ReservedClusterStateHandler; import org.elasticsearch.reservedstate.TransformState; import org.elasticsearch.xcontent.XContentParser; @@ -16,6 +22,7 @@ import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingRequestBuilder; import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping; import org.elasticsearch.xpack.core.security.authz.RoleMappingMetadata; +import org.elasticsearch.xpack.core.security.support.RoleMappingCleanupTaskParams; import java.io.IOException; import java.util.ArrayList; @@ -25,6 +32,7 @@ import java.util.stream.Collectors; import static org.elasticsearch.common.xcontent.XContentHelper.mapToXContentParser; +import static org.elasticsearch.xpack.core.security.authz.RoleMappingMetadata.ROLE_MAPPING_METADATA_VERSION; /** * This Action is the reserved state save version of RestPutRoleMappingAction/RestDeleteRoleMappingAction @@ -35,6 +43,14 @@ public class ReservedRoleMappingAction implements ReservedClusterStateHandler> { public static final String NAME = "role_mappings"; + private final PersistentTasksService persistentTasksService; + + private static final Logger logger = LogManager.getLogger(ReservedRoleMappingAction.class); + + public ReservedRoleMappingAction(PersistentTasksService persistentTasksService) { + this.persistentTasksService = persistentTasksService; + } + @Override public String name() { return NAME; @@ -44,8 +60,13 @@ public String name() { public TransformState transform(Object source, TransformState prevState) throws Exception { @SuppressWarnings("unchecked") Set roleMappings = validate((List) source); - RoleMappingMetadata newRoleMappingMetadata = new RoleMappingMetadata(roleMappings); - if (newRoleMappingMetadata.equals(RoleMappingMetadata.getFromClusterState(prevState.state()))) { + RoleMappingMetadata newRoleMappingMetadata = new RoleMappingMetadata(roleMappings, ROLE_MAPPING_METADATA_VERSION); + RoleMappingMetadata currentRoleMappingMetadata = RoleMappingMetadata.getFromClusterState(prevState.state()); + + if (currentRoleMappingMetadata.getVersion() < ROLE_MAPPING_METADATA_VERSION) { + submitCleanupTask(); + } + if (newRoleMappingMetadata.equals(currentRoleMappingMetadata)) { return prevState; } else { ClusterState newState = newRoleMappingMetadata.updateClusterState(prevState.state()); @@ -57,6 +78,25 @@ public TransformState transform(Object source, TransformState prevState) throws } } + private void submitCleanupTask() { + persistentTasksService.sendStartRequest( + RoleMappingCleanupTaskParams.TASK_NAME, + RoleMappingCleanupTaskParams.TASK_NAME, + new RoleMappingCleanupTaskParams(), + null, + ActionListener.wrap((response) -> { + logger.info("Role mapping cleanup task submitted"); + }, (exception) -> { + // Do nothing if the task is already in progress + if (ExceptionsHelper.unwrapCause(exception) instanceof ResourceAlreadyExistsException) { + logger.info("Cleanup task already started Already started"); + } else { + logger.warn("Role mapping cleanup task failed: " + exception); + } + }) + ); + } + @Override public List fromXContent(XContentParser parser) throws IOException { List result = new ArrayList<>(); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/RoleMappingCleanupExecutor.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/RoleMappingCleanupExecutor.java new file mode 100644 index 0000000000000..377abcd3bfb97 --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/support/RoleMappingCleanupExecutor.java @@ -0,0 +1,129 @@ +/* + * 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.security.support; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.support.GroupedActionListener; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.persistent.AllocatedPersistentTask; +import org.elasticsearch.persistent.PersistentTaskState; +import org.elasticsearch.persistent.PersistentTasksExecutor; +import org.elasticsearch.xpack.core.security.action.rolemapping.PutRoleMappingRequest; +import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping; +import org.elasticsearch.xpack.core.security.authz.RoleMappingMetadata; +import org.elasticsearch.xpack.core.security.support.RoleMappingCleanupTaskParams; +import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore; + +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; +import java.util.stream.Collectors; + +public class RoleMappingCleanupExecutor extends PersistentTasksExecutor + implements + BiConsumer { + + private static final Logger logger = LogManager.getLogger(RoleMappingCleanupExecutor.class); + private final NativeRoleMappingStore roleMappingStore; + + private final ClusterService clusterService; + private AllocatedPersistentTask currentTask = null; + private final SecurityIndexManager securityIndexManager; + private final AtomicBoolean cleanupInProgress = new AtomicBoolean(false); + + public RoleMappingCleanupExecutor( + ClusterService clusterService, + String taskName, + Executor executor, + NativeRoleMappingStore roleMappingStore, + SecurityIndexManager securityIndexManager + ) { + super(taskName, executor); + this.clusterService = clusterService; + this.roleMappingStore = roleMappingStore; + this.securityIndexManager = securityIndexManager; + } + + @Override + protected void nodeOperation(AllocatedPersistentTask task, RoleMappingCleanupTaskParams params, PersistentTaskState state) { + currentTask = task; + if (securityIndexManager.isAvailable(SecurityIndexManager.Availability.SEARCH_SHARDS) == false) { + logger.trace("Security index not available for role mapping cleanup, waiting..."); + securityIndexManager.addStateListener(this); + } else if (cleanupInProgress.compareAndSet(false, true)) { + doCleanup(); + } + } + + private void doCleanup() { + getRoleMappingBothInClusterStateAndIndex( + clusterService.state(), + ActionListener.wrap(roleMappings -> disableRoleMappings(roleMappings, ActionListener.wrap(response -> { + markAsCompleted(roleMappings); + }, this::markAsFailed)), this::markAsFailed) + ); + } + + private void markAsCompleted(List roleMappings) { + currentTask.markAsCompleted(); + logger.info("Successfully cleaned up [" + roleMappings.size() + "] stale role mappings"); + cleanupInProgress.set(false); + } + + private void markAsFailed(Exception exception) { + currentTask.markAsFailed(exception); + cleanupInProgress.set(false); + } + + private void getRoleMappingBothInClusterStateAndIndex(ClusterState state, ActionListener> listener) { + RoleMappingMetadata roleMappingMetadata = RoleMappingMetadata.getFromClusterState(state); + Set roleMappingNames = roleMappingMetadata.getRoleMappings() + .stream() + .map(ExpressionRoleMapping::getName) + .collect(Collectors.toSet()); + + if (roleMappingNames.isEmpty()) { + listener.onResponse(List.of()); + return; + } + roleMappingStore.getRoleMappings(roleMappingNames, listener); + } + + private void disableRoleMappings(List roleMappings, ActionListener> listener) { + GroupedActionListener groupedActionListener = new GroupedActionListener<>(roleMappings.size(), listener); + for (ExpressionRoleMapping roleMapping : roleMappings) { + var request = new PutRoleMappingRequest(); + request.setName(roleMapping.getName()); + request.setEnabled(false); + request.setRoles(roleMapping.getRoles()); + request.setRoleTemplates(roleMapping.getRoleTemplates()); + request.setRules(roleMapping.getExpression()); + request.setMetadata(roleMapping.getMetadata()); + roleMappingStore.putRoleMapping( + request, + ActionListener.wrap(response -> groupedActionListener.onResponse(null), groupedActionListener::onFailure) + ); + } + } + + @Override + public void accept(SecurityIndexManager.State oldState, SecurityIndexManager.State newState) { + if (securityIndexManager.isAvailable(SecurityIndexManager.Availability.SEARCH_SHARDS)) { + if (cleanupInProgress.compareAndSet(false, true)) { + securityIndexManager.removeStateListener(this); + doCleanup(); + } + } + } +} diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/reservedstate/ReservedRoleMappingActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/reservedstate/ReservedRoleMappingActionTests.java index cac7c91f73ed1..671ea011122a0 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/reservedstate/ReservedRoleMappingActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/reservedstate/ReservedRoleMappingActionTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.common.ParsingException; +import org.elasticsearch.persistent.PersistentTasksService; import org.elasticsearch.reservedstate.TransformState; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.XContentParser; @@ -22,6 +23,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.mock; /** * Tests that the ReservedRoleMappingAction does validation, can add and remove role mappings @@ -40,7 +42,7 @@ private TransformState processJSON(ReservedRoleMappingAction action, TransformSt public void testValidation() { ClusterState state = ClusterState.builder(new ClusterName("elasticsearch")).build(); TransformState prevState = new TransformState(state, Collections.emptySet()); - ReservedRoleMappingAction action = new ReservedRoleMappingAction(); + ReservedRoleMappingAction action = new ReservedRoleMappingAction(mock(PersistentTasksService.class)); String badPolicyJSON = """ { "everyone_kibana": { @@ -69,7 +71,7 @@ public void testValidation() { public void testAddRemoveRoleMapping() throws Exception { ClusterState state = ClusterState.builder(new ClusterName("elasticsearch")).build(); TransformState prevState = new TransformState(state, Collections.emptySet()); - ReservedRoleMappingAction action = new ReservedRoleMappingAction(); + ReservedRoleMappingAction action = new ReservedRoleMappingAction(mock(PersistentTasksService.class)); String emptyJSON = ""; TransformState updatedState = processJSON(action, prevState, emptyJSON); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RoleMappingMetadataTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RoleMappingMetadataTests.java index a061106a979d7..1ebd13b3a36db 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RoleMappingMetadataTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RoleMappingMetadataTests.java @@ -33,7 +33,7 @@ public class RoleMappingMetadataTests extends AbstractWireSerializingTestCase randomRoleMapping(true))); + return new RoleMappingMetadata(randomSet(0, 3, () -> randomRoleMapping(true)), 0); } @Override @@ -47,7 +47,7 @@ protected RoleMappingMetadata mutateInstance(RoleMappingMetadata instance) throw if (randomBoolean() || mutated == false) { mutatedRoleMappings.add(randomRoleMapping(true)); } - return new RoleMappingMetadata(mutatedRoleMappings); + return new RoleMappingMetadata(mutatedRoleMappings, 0); } @Override @@ -61,7 +61,7 @@ protected NamedWriteableRegistry getNamedWriteableRegistry() { } public void testSerializationBWC() throws IOException { - RoleMappingMetadata original = new RoleMappingMetadata(randomSet(0, 3, () -> randomRoleMapping(true))); + RoleMappingMetadata original = new RoleMappingMetadata(randomSet(0, 3, () -> randomRoleMapping(true)), 0); TransportVersion version = TransportVersionUtils.randomVersionBetween(random(), TransportVersions.V_7_2_0, null); BytesStreamOutput output = new BytesStreamOutput(); output.setTransportVersion(version); @@ -79,8 +79,8 @@ public void testEquals() { Set roleMappings1 = randomSet(0, 3, () -> randomRoleMapping(true)); Set roleMappings2 = randomSet(0, 3, () -> randomRoleMapping(true)); assumeFalse("take 2 different role mappings", roleMappings1.equals(roleMappings2)); - assertThat(new RoleMappingMetadata(roleMappings1).equals(new RoleMappingMetadata(roleMappings2)), is(false)); - assertThat(new RoleMappingMetadata(roleMappings1).equals(new RoleMappingMetadata(roleMappings1)), is(true)); - assertThat(new RoleMappingMetadata(roleMappings2).equals(new RoleMappingMetadata(roleMappings2)), is(true)); + assertThat(new RoleMappingMetadata(roleMappings1, 0).equals(new RoleMappingMetadata(roleMappings2, 0)), is(false)); + assertThat(new RoleMappingMetadata(roleMappings1, 0).equals(new RoleMappingMetadata(roleMappings1, 0)), is(true)); + assertThat(new RoleMappingMetadata(roleMappings2, 0).equals(new RoleMappingMetadata(roleMappings2, 0)), is(true)); } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RoleMappingMetadataXContentSerializationTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RoleMappingMetadataXContentSerializationTests.java index db0201a1c072b..f051c5e1df898 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RoleMappingMetadataXContentSerializationTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/RoleMappingMetadataXContentSerializationTests.java @@ -31,7 +31,7 @@ protected Writeable.Reader instanceReader() { @Override protected RoleMappingMetadata createTestInstance() { - return new RoleMappingMetadata(randomSet(0, 3, () -> randomRoleMapping(true))); + return new RoleMappingMetadata(randomSet(0, 3, () -> randomRoleMapping(true)), 0); } @Override @@ -45,7 +45,7 @@ protected RoleMappingMetadata mutateInstance(RoleMappingMetadata instance) throw if (randomBoolean() || mutated == false) { mutatedRoleMappings.add(randomRoleMapping(true)); } - return new RoleMappingMetadata(mutatedRoleMappings); + return new RoleMappingMetadata(mutatedRoleMappings, 0); } @Override diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/ClusterStateRoleMapperTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/ClusterStateRoleMapperTests.java index 515b5ef741a00..a5a869251790b 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/ClusterStateRoleMapperTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/ClusterStateRoleMapperTests.java @@ -82,7 +82,7 @@ public void testRoleResolving() throws Exception { ExpressionRoleMapping mapping1 = mockExpressionRoleMapping(false, Set.of("role1"), expressionModel); ExpressionRoleMapping mapping2 = mockExpressionRoleMapping(true, Set.of("role2")); ExpressionRoleMapping mapping3 = mockExpressionRoleMapping(true, Set.of("role3"), expressionModel); - RoleMappingMetadata roleMappingMetadata = new RoleMappingMetadata(Set.of(mapping1, mapping2, mapping3)); + RoleMappingMetadata roleMappingMetadata = new RoleMappingMetadata(Set.of(mapping1, mapping2, mapping3), 0); ClusterState state = roleMappingMetadata.updateClusterState(ClusterState.builder(new ClusterName("elasticsearch")).build()); when(clusterService.state()).thenReturn(state); { @@ -123,19 +123,19 @@ public void testRoleMappingChangesTriggerRealmCacheClear() { ExpressionRoleMapping mapping2 = mockExpressionRoleMapping(true, Set.of("role"), model2); ExpressionRoleMapping mapping3 = mockExpressionRoleMapping(true, Set.of("role3"), model2); ClusterState emptyState = ClusterState.builder(new ClusterName("elasticsearch")).build(); - RoleMappingMetadata roleMappingMetadata1 = new RoleMappingMetadata(Set.of(mapping1)); + RoleMappingMetadata roleMappingMetadata1 = new RoleMappingMetadata(Set.of(mapping1), 0); ClusterState state1 = roleMappingMetadata1.updateClusterState(emptyState); roleMapper.clusterChanged(new ClusterChangedEvent("test", emptyState, state1)); verify(mockRealm, times(1)).expireAll(); - RoleMappingMetadata roleMappingMetadata2 = new RoleMappingMetadata(Set.of(mapping2)); + RoleMappingMetadata roleMappingMetadata2 = new RoleMappingMetadata(Set.of(mapping2), 0); ClusterState state2 = roleMappingMetadata2.updateClusterState(state1); roleMapper.clusterChanged(new ClusterChangedEvent("test", state1, state2)); verify(mockRealm, times(2)).expireAll(); - RoleMappingMetadata roleMappingMetadata3 = new RoleMappingMetadata(Set.of(mapping3)); + RoleMappingMetadata roleMappingMetadata3 = new RoleMappingMetadata(Set.of(mapping3), 0); ClusterState state3 = roleMappingMetadata3.updateClusterState(state2); roleMapper.clusterChanged(new ClusterChangedEvent("test", state2, state3)); verify(mockRealm, times(3)).expireAll(); - RoleMappingMetadata roleMappingMetadata4 = new RoleMappingMetadata(Set.of(mapping2, mapping3)); + RoleMappingMetadata roleMappingMetadata4 = new RoleMappingMetadata(Set.of(mapping2, mapping3), 0); ClusterState state4 = roleMappingMetadata4.updateClusterState(state3); roleMapper.clusterChanged(new ClusterChangedEvent("test", state3, state4)); verify(mockRealm, times(4)).expireAll();