tests-integration/Elastic.Serilog.Sinks.IntegrationTests/BootstrapMinimumSecurityPrivilegesTests.cs (118 lines of code) (raw):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Elastic.Channels;
using Elastic.Channels.Diagnostics;
using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.IndexManagement;
using Elastic.CommonSchema;
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
using Elastic.Ingest.Elasticsearch;
using Elastic.Transport;
using Elasticsearch.IntegrationDefaults;
using FluentAssertions;
using Serilog;
using Serilog.Core;
using Xunit.Abstractions;
using DataStreamName = Elastic.Ingest.Elasticsearch.DataStreams.DataStreamName;
namespace Elastic.Serilog.Sinks.IntegrationTests;
public class BootstrapMinimumSecurityPrivilegesTests : SecurityPrivilegesTestsBase
{
public BootstrapMinimumSecurityPrivilegesTests(SecurityCluster cluster, ITestOutputHelper output)
: base(cluster, output)
{
}
protected override BootstrapMethod Bootstrap => BootstrapMethod.Failure;
protected override DataStreamName Target { get; } = new ("logs", "serilog.setup");
protected override string ApiKeyJson => $@"{{
""name"": ""ecs_setup"",
""role_descriptors"": {{
""ecs_setup"": {{
""cluster"": [""monitor"", ""manage_ilm"", ""manage_index_templates"", ""manage_pipeline""],
""index"": [{{
""names"": [""{Target.GetNamespaceWildcard()}""],
""privileges"": [""manage"", ""create_doc""]
}}]
}}
}}
}}";
}
public class NoBootstrapMinimumSecurityPrivilegesTests : SecurityPrivilegesTestsBase
{
public NoBootstrapMinimumSecurityPrivilegesTests(SecurityCluster cluster, ITestOutputHelper output)
: base(cluster, output)
{
}
protected override BootstrapMethod Bootstrap => BootstrapMethod.None;
protected override DataStreamName Target { get; } = new ("logs", "serilog.write");
protected override string ApiKeyJson => $@"{{
""name"": ""ecs_write"",
""role_descriptors"": {{
""ecs_write"": {{
""cluster"": [""monitor""],
""index"": [{{
""names"": [""{Target.GetNamespaceWildcard()}""],
""privileges"": [""auto_configure"", ""create_doc""]
}}]
}}
}}
}}";
}
public abstract class SecurityPrivilegesTestsBase : SerilogTestBase<SecurityCluster>
{
private IChannelDiagnosticsListener? _listener;
private readonly CountdownEvent _waitHandle = new(1);
private ElasticsearchSinkOptions SinkOptions { get; }
private ElasticsearchClient ApiScopedClient { get; }
protected abstract string ApiKeyJson { get; }
protected abstract DataStreamName Target { get; }
protected abstract BootstrapMethod Bootstrap { get; }
protected SecurityPrivilegesTestsBase(SecurityCluster cluster, ITestOutputHelper output) : base(cluster, output)
{
var logs = new List<Action<Logger>>
{
l => l.Information("Hello Information"),
l => l.Debug("Hello Debug"),
l => l.Warning("Hello Warning"),
l => l.Error("Hello Error"),
l => l.Fatal("Hello Fatal")
};
var apiKey = SecurityCluster.CreateApiKey(Client, ApiKeyJson);
ApiScopedClient = cluster.CreateElasticsearchClient(output,
s=>s.Authentication(new ApiKey(apiKey.Encoded))
);
SinkOptions = new ElasticsearchSinkOptions(ApiScopedClient.Transport)
{
BootstrapMethod = Bootstrap,
DataStream = Target,
ConfigureChannel = c =>
{
c.BufferOptions = new BufferOptions
{
WaitHandle = _waitHandle,
OutboundBufferMaxSize = logs.Count
};
},
ChannelDiagnosticsCallback = (l) => _listener = l
};
var loggerConfig = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.ColoredConsole()
.WriteTo.Elasticsearch(SinkOptions);
using var logger = loggerConfig.CreateLogger();
foreach (var a in logs) a(logger);
}
[I] public async Task AssertLogs()
{
if (!_waitHandle.WaitHandle.WaitOne(TimeSpan.FromSeconds(10)))
throw new Exception($"No flush occurred in 10 seconds: {_listener}", _listener?.ObservedException);
var indexName = SinkOptions.DataStream.ToString();
var refreshed = await Client.Indices.RefreshAsync(new RefreshRequest(indexName));
refreshed.IsValidResponse.Should().BeTrue("{0}", refreshed.DebugInformation);
var search = await Client.SearchAsync<EcsDocument>(new SearchRequest(indexName));
// Informational should be filtered
search.Documents.Count().Should().Be(4);
var messages = search.Documents.Select(e => e.Message);
messages.Should().Contain("Hello Error");
}
}