Skip to content

Commit

Permalink
Tests and wrapping
Browse files Browse the repository at this point in the history
  • Loading branch information
n1v0lg committed Dec 7, 2023
1 parent 4cf9b4c commit a039367
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.TransportVersion;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionRequest;
Expand Down Expand Up @@ -627,6 +627,14 @@ protected XPackLicenseState getLicenseState() {
return XPackPlugin.getSharedLicenseState();
}

protected Client getClient() {
return client.get();
}

protected Realms getRealms() {
return realms.get();
}

@Override
public Collection<?> createComponents(PluginServices services) {
try {
Expand Down Expand Up @@ -1898,27 +1906,33 @@ public BiConsumer<DiscoveryNode, ClusterState> getJoinValidator() {
@Override
public void reload(Settings settings) throws Exception {
if (enabled) {
final List<Exception> exceptions = new ArrayList<>();
final List<Exception> reloadExceptions = new ArrayList<>();
try {
reloadRemoteClusterCredentials(settings);
} catch (Exception ex) {
exceptions.add(ex);
reloadExceptions.add(ex);
}

try {
reloadSharedSecretsForJwtRealms(settings);
} catch (Exception ex) {
exceptions.add(ex);
reloadExceptions.add(ex);
}

ExceptionsHelper.rethrowAndSuppress(exceptions);
if (false == reloadExceptions.isEmpty()) {
final var combinedException = new ElasticsearchException(
"secure settings reload failed for one or more security component"
);
reloadExceptions.forEach(combinedException::addSuppressed);
throw combinedException;
}
} else {
ensureNoRemoteClusterCredentialsOnDisabledSecurity(settings);
}
}

private void reloadSharedSecretsForJwtRealms(Settings settingsWithKeystore) {
realms.get().stream().filter(r -> JwtRealmSettings.TYPE.equals(r.realmRef().getType())).forEach(realm -> {
getRealms().stream().filter(r -> JwtRealmSettings.TYPE.equals(r.realmRef().getType())).forEach(realm -> {
if (realm instanceof JwtRealm jwtRealm) {
jwtRealm.rotateClientSecret(
CLIENT_AUTHENTICATION_SHARED_SECRET.getConcreteSettingForNamespace(realm.realmRef().getName()).get(settingsWithKeystore)
Expand All @@ -1930,9 +1944,11 @@ private void reloadSharedSecretsForJwtRealms(Settings settingsWithKeystore) {
private void reloadRemoteClusterCredentials(Settings settingsWithKeystore) {
// Accepting a blocking call here since the underlying action is local-only and only performs fast in-memory ops
// (extracts a subset of passed in `settingsWithKeystore` and stores them in a map)
client.get()
.execute(ReloadRemoteClusterCredentialsAction.INSTANCE, new ReloadRemoteClusterCredentialsAction.Request(settingsWithKeystore))
.actionGet();
// TODO wrap exception here?
getClient().execute(
ReloadRemoteClusterCredentialsAction.INSTANCE,
new ReloadRemoteClusterCredentialsAction.Request(settingsWithKeystore)
).actionGet();
}

static final class ValidateLicenseForFIPS implements BiConsumer<DiscoveryNode, ClusterState> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ActionModule;
import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.client.internal.Client;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
Expand Down Expand Up @@ -72,6 +75,7 @@
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.SecurityExtension;
import org.elasticsearch.xpack.core.security.SecurityField;
import org.elasticsearch.xpack.core.security.action.settings.ReloadRemoteClusterCredentialsAction;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationTestHelper;
import org.elasticsearch.xpack.core.security.authc.Realm;
Expand Down Expand Up @@ -116,6 +120,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Collections.emptyMap;
import static org.elasticsearch.xpack.core.security.authc.RealmSettings.getFullSettingKey;
Expand All @@ -133,7 +138,9 @@
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class SecurityTests extends ESTestCase {
Expand Down Expand Up @@ -922,6 +929,66 @@ public <T> List<T> loadExtensions(Class<T> extensionPointType) {
assertThat(registry, instanceOf(DummyOperatorOnlyRegistry.class));
}

public void testReload() throws Exception {
final Settings settings = Settings.builder().put("xpack.security.enabled", true).put("path.home", createTempDir()).build();

final PlainActionFuture<ActionResponse.Empty> value = new PlainActionFuture<>();
value.onResponse(ActionResponse.Empty.INSTANCE);
final Client mockedClient = mock(Client.class);

final Realms mockedRealms = mock(Realms.class);
when(mockedRealms.stream()).thenReturn(Stream.of());
when(mockedClient.execute(eq(ReloadRemoteClusterCredentialsAction.INSTANCE), any())).thenReturn(value);
security = new Security(settings, Collections.emptyList()) {
@Override
protected Client getClient() {
return mockedClient;
}

@Override
protected Realms getRealms() {
return mockedRealms;
}
};

final Settings inputSettings = Settings.EMPTY;
security.reload(inputSettings);

verify(mockedClient).execute(eq(ReloadRemoteClusterCredentialsAction.INSTANCE), any());
verify(mockedRealms).stream();
}

public void testReloadWithFailures() {
final Settings settings = Settings.builder().put("xpack.security.enabled", true).put("path.home", createTempDir()).build();

final PlainActionFuture<ActionResponse.Empty> value = new PlainActionFuture<>();
value.onFailure(new Exception("bad potato"));
final Client mockedClient = mock(Client.class);

final Realms mockedRealms = mock(Realms.class);
when(mockedRealms.stream()).thenReturn(Stream.of());
when(mockedClient.execute(eq(ReloadRemoteClusterCredentialsAction.INSTANCE), any())).thenReturn(value);
security = new Security(settings, Collections.emptyList()) {
@Override
protected Client getClient() {
return mockedClient;
}

@Override
protected Realms getRealms() {
return mockedRealms;
}
};

final Settings inputSettings = Settings.EMPTY;
var exception = expectThrows(ElasticsearchException.class, () -> security.reload(inputSettings));

assertThat(exception.getMessage(), containsString("secure settings reload failed for one or more security component"));
// Verify both called despite failure
verify(mockedClient).execute(eq(ReloadRemoteClusterCredentialsAction.INSTANCE), any());
verify(mockedRealms).stream();
}

public void testLoadNoExtensions() throws Exception {
Settings settings = Settings.builder()
.put("xpack.security.enabled", true)
Expand Down

0 comments on commit a039367

Please sign in to comment.