From 3b720e04e852705ed54d954d6fe7ca0c65044d1e Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Tue, 8 Nov 2022 01:36:29 +0400 Subject: [PATCH] feat(core): implement `EnvCredentialProvider` to obtain credentials from env variables (#14) closes #11 --- src/SecTester.Core/Configuration.cs | 12 ++- .../EnvCredentialProvider.cs | 16 ++++ src/SecTester.Core/README.md | 95 ++++++++++++++++++- src/SecTester.Core/SecTester.Core.csproj | 1 + .../ConfigurationTests.cs | 17 +++- .../EnvCredentialProviderTests.cs | 44 +++++++++ .../SecTester.Core.Tests.csproj | 5 + 7 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 src/SecTester.Core/CredentialProviders/EnvCredentialProvider.cs create mode 100644 test/SecTester.Core.Tests/CredentialProviders/EnvCredentialProviderTests.cs diff --git a/src/SecTester.Core/Configuration.cs b/src/SecTester.Core/Configuration.cs index 1311476..6518234 100644 --- a/src/SecTester.Core/Configuration.cs +++ b/src/SecTester.Core/Configuration.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using SecTester.Core.CredentialProviders; using SecTester.Core.Utils; namespace SecTester.Core @@ -29,12 +30,21 @@ public class Configuration public Configuration(string? hostname, Credentials? credentials = null, List? credentialProviders = null) { + credentialProviders ??= new List { new EnvCredentialProvider() }; hostname = hostname?.Trim(); hostname = hostname ?? throw new ArgumentNullException(nameof(hostname), "Please provide 'hostname' option."); ResolveUrls(hostname); + + if (credentials == null && (credentialProviders == null || !credentialProviders.Any())) + { + throw new InvalidOperationException( + $"Please provide either '{nameof(credentials)}' or '{nameof(credentialProviders)}'" + ); + } + Credentials = credentials; - _credentialProviders = credentialProviders ?? new List(); + _credentialProviders = credentialProviders; } public async Task LoadCredentials() diff --git a/src/SecTester.Core/CredentialProviders/EnvCredentialProvider.cs b/src/SecTester.Core/CredentialProviders/EnvCredentialProvider.cs new file mode 100644 index 0000000..5d21001 --- /dev/null +++ b/src/SecTester.Core/CredentialProviders/EnvCredentialProvider.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; + +namespace SecTester.Core.CredentialProviders; + +public class EnvCredentialProvider : CredentialProvider +{ + public const string BrightToken = "BRIGHT_TOKEN"; + + public Task Get() + { + string token = Environment.GetEnvironmentVariable(BrightToken); + + return Task.FromResult(!string.IsNullOrWhiteSpace(token) ? new Credentials(token) : null); + } +} diff --git a/src/SecTester.Core/README.md b/src/SecTester.Core/README.md index 45b4a2c..367e2f1 100644 --- a/src/SecTester.Core/README.md +++ b/src/SecTester.Core/README.md @@ -16,7 +16,100 @@ $ dotnet add package SecTester.Core ## Usage -_TBU_ +### Configuration + +First, you need to generate a new instance of `Configuration`. + +```csharp +var config = new Configuration( + hostname: "app.neuralegion.com", + credentials: new Credentials("your API key")); +``` + +You can also register the configuration using the dependency injection framework providing information that will be used to construct other clients. + +```csharp +public void ConfigureServices(IServiceCollection services) +{ + services.AddSecTesterConfig("app.neuralegion.com"); + // or + services.AddSecTesterConfig(config); +} +``` + +#### Options + +Configuration can be customized using the following options: + +```csharp +public interface ConfigurationOptions { + string hostname + { + get; + } + Credentials? credentials + { + get; + } + List? credentialProviders + { + get; + } +} +``` + +The default configuration is as follows: + +```csharp +{ + credentialProviders = new List { new EnvCredentialProvider() } +} +``` + +#### hostname + +- type: `string` + +Set the application name (domain name), that is used to establish connection with. + +```csharp +var config = new Configuration(hostname: "app.neuralegion.com"); +``` + +#### credentials + +- type: `Credentials` + +Set credentials to access the application. + +```csharp +var config = new Configuration( + // ... + credentials: new Credential("your API key")); +``` + +More info about [setting up an API key](https://docs.brightsec.com/docs/manage-your-personal-account#manage-your-personal-api-keys-authentication-tokens) + +#### credentialProviders + +- type: `CredentialProvider[]` + +Allows you to provide credentials and load it in runtime. The configuration will invoke one provider at a time and only +continue to the next if no credentials have been located. For example, if the process finds values defined via +the `BRIGHT_TOKEN` environment variables, the file at `.sectesterrc` will not be read. + +#### EnvCredentialProvider + +Use this provider to read credentials from the following environment variable: `BRIGHT_TOKEN` + +If the `BRIGHT_TOKEN` environment variable is not set or contains a falsy value, it will return undefined. + +```csharp +var credentialsProvider = new EnvCredentialProvider(); +var config = new Configuration( + // ... + credentialProviders: new List { credentialsProvider }); +``` ## License diff --git a/src/SecTester.Core/SecTester.Core.csproj b/src/SecTester.Core/SecTester.Core.csproj index 938b229..04f2b42 100644 --- a/src/SecTester.Core/SecTester.Core.csproj +++ b/src/SecTester.Core/SecTester.Core.csproj @@ -5,6 +5,7 @@ + diff --git a/test/SecTester.Core.Tests/ConfigurationTests.cs b/test/SecTester.Core.Tests/ConfigurationTests.cs index 3898ce2..7ea41f7 100644 --- a/test/SecTester.Core.Tests/ConfigurationTests.cs +++ b/test/SecTester.Core.Tests/ConfigurationTests.cs @@ -47,6 +47,19 @@ public void Configuration_HostnameIsInvalid_ThrowError() act.Should().Throw(); } + [Fact] + public void Configuration_CredentialsOrCredentialProvidersNoDefined_ThrowError() + { + // arrange + const string hostname = "app.neuralegion.com"; + + // act + Action act = () => new Configuration(hostname, credentials: null, credentialProviders: new List()); + + // assert + act.Should().Throw(); + } + [Theory] [MemberData(nameof(Hostnames))] public void Configuration_ValidHostname_ResolveApiAndBus(string input, object address) @@ -81,7 +94,7 @@ public async Task LoadCredentials_GivenProvider_LoadCredentials() var credentialProviders = new List { сredentialProvider }; var configuration = new Configuration(hostname: "app.neuralegion.com", credentialProviders: credentialProviders); - сredentialProvider.Get().Returns(Task.FromResult(credentials)); + сredentialProvider.Get()!.Returns(Task.FromResult(credentials)); // act await configuration.LoadCredentials(); @@ -115,7 +128,7 @@ public async Task LoadCredentials_MultipleProviders_SetCredentialsFromFirst() var credentialProviders = new List { сredentialProvider, сredentialProvider, сredentialProvider }; var configuration = new Configuration(hostname: "app.neuralegion.com", credentialProviders: credentialProviders); - сredentialProvider.Get().Returns(Task.FromResult(null), Task.FromResult(credentials1), Task.FromResult(credentials2)); + сredentialProvider.Get()!.Returns(Task.FromResult(null)!, Task.FromResult(credentials1), Task.FromResult(credentials2)); // act await configuration.LoadCredentials(); diff --git a/test/SecTester.Core.Tests/CredentialProviders/EnvCredentialProviderTests.cs b/test/SecTester.Core.Tests/CredentialProviders/EnvCredentialProviderTests.cs new file mode 100644 index 0000000..41dfb98 --- /dev/null +++ b/test/SecTester.Core.Tests/CredentialProviders/EnvCredentialProviderTests.cs @@ -0,0 +1,44 @@ +using SecTester.Core.CredentialProviders; + +namespace SecTester.Core.Tests.CredentialProviders; + +public class EnvCredentialProviderTests +{ + [Fact] + public async Task Get_EnvVariableIsNotProvided_ReturnNull() + { + // arrange + var envCredentialProvider = new EnvCredentialProvider(); + + // act + var result = await envCredentialProvider.Get(); + + // assert + result.Should().BeNull(); + } + + [Fact] + public async Task Get_GivenEnvVariable_ReturnCredentials() + { + var oldValue = Environment.GetEnvironmentVariable(EnvCredentialProvider.BrightToken); + + try + { + // arrange + const string token = "0zmcwpe.nexr.0vlon8mp7lvxzjuvgjy88olrhadhiukk"; + Environment.SetEnvironmentVariable(EnvCredentialProvider.BrightToken, token); + var envCredentialProvider = new EnvCredentialProvider(); + + // act + var result = await envCredentialProvider.Get(); + + // assert + result.Should().BeEquivalentTo(new { Token = token }); + } + finally + { + Environment.SetEnvironmentVariable(EnvCredentialProvider.BrightToken, oldValue); + } + + } +} diff --git a/test/SecTester.Core.Tests/SecTester.Core.Tests.csproj b/test/SecTester.Core.Tests/SecTester.Core.Tests.csproj index 1a05fc9..7655eda 100644 --- a/test/SecTester.Core.Tests/SecTester.Core.Tests.csproj +++ b/test/SecTester.Core.Tests/SecTester.Core.Tests.csproj @@ -7,4 +7,9 @@ + + + + +