Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace CodeDesignPlus.Net.Vault.Abstractions.Options;

/// <summary>
/// Type of authentication to use in the Vault
/// </summary>
public enum TypeAuth
{
/// <summary>
/// None authentication
/// </summary>
None,
/// <summary>
/// Token authentication
/// </summary>
Token,
/// <summary>
/// AppRole authentication
/// </summary>
AppRole,
/// <summary>
/// Kubernetes authentication
/// </summary>
Kubernetes
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,31 @@ public class VaultOptions : IValidatableObject
/// </summary>
public static readonly string Section = "Vault";
/// <summary>
/// Gets the type of authentication to use in the Vault
/// </summary>
public TypeAuth TypeAuth
{
get
{
if (!string.IsNullOrEmpty(this.Token))
return TypeAuth.Token;
else if (!string.IsNullOrEmpty(this.RoleId) && !string.IsNullOrEmpty(this.SecretId))
return TypeAuth.AppRole;
else if (this.Kubernetes != null && this.Kubernetes.Enable)
return TypeAuth.Kubernetes;

return TypeAuth.None;
}
}
/// <summary>
/// Gets or sets the enable of the Vault server
/// </summary>
public bool Enable { get; set; }
/// <summary>
/// Gets or sets the token of the Vault server
/// </summary>
public string Token { get; set; }
/// <summary>
/// Gets or sets the address of the Vault server
/// </summary>
[Required]
Expand Down Expand Up @@ -69,10 +94,8 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
{
var results = new List<ValidationResult>();

if (string.IsNullOrEmpty(this.RoleId) && string.IsNullOrEmpty(this.SecretId) && this.Kubernetes != null && !this.Kubernetes.Enable)
{
results.Add(new ValidationResult("The RoleId and SecretId is required."));
}
if (this.TypeAuth == TypeAuth.None)
results.Add(new ValidationResult("The TypeAuth is required."));

if (this.Mongo != null && this.Mongo.Enable)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,18 @@ public static IServiceCollection AddVault(this IServiceCollection services, ICon
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add configuration to.</param>
/// <param name="options">An action to configure the <see cref="VaultOptions"/>.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> or <paramref name="options"/> is null.</exception>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> is null.</exception>
/// <exception cref="VaultException">Thrown if the Vault configuration section does not exist.</exception>
/// <returns>The updated <see cref="IConfigurationBuilder"/>.</returns>
public static IConfigurationBuilder AddVault(this IConfigurationBuilder builder, Action<VaultOptions> options)
public static IConfigurationBuilder AddVault(this IConfigurationBuilder builder, Action<VaultOptions> options = null)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(options);

var configuration = builder.Build();

var vaultOptions = configuration.GetSection(VaultOptions.Section).Get<VaultOptions>();

options.Invoke(vaultOptions);
options?.Invoke(vaultOptions);

var source = new VaultConfigurationSource(vaultOptions);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using VaultSharp.V1.AuthMethods.Token;

namespace CodeDesignPlus.Net.Vault.Services;

/// <summary>
Expand All @@ -15,22 +17,23 @@ public static IVaultClient Create(VaultOptions options)
{
ArgumentNullException.ThrowIfNull(options);

var vaultClientSettings = new VaultClientSettings(
options.Address,
new AppRoleAuthMethodInfo(options.RoleId, options.SecretId)
);
if (options.TypeAuth == TypeAuth.Token)
return new VaultClient(new VaultClientSettings(options.Address, new TokenAuthMethodInfo(options.Token)));

if (options.TypeAuth == TypeAuth.AppRole)
return new VaultClient(new VaultClientSettings(options.Address, new AppRoleAuthMethodInfo(options.RoleId, options.SecretId)));

if (options.Kubernetes.Enable)
if (options.TypeAuth == TypeAuth.Kubernetes)
{
var jwt = File.ReadAllText(options.Kubernetes.PathTokenKubernetes);

vaultClientSettings = new VaultClientSettings(
return new VaultClient(new VaultClientSettings(
options.Address,
new KubernetesAuthMethodInfo($"{options.AppName}-{options.Kubernetes.RoleSufix}", jwt)
);
));
}

return new VaultClient(vaultClientSettings);
throw new VaultException("The authentication type is not defined.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,50 @@ public void AddVault_ConfigurationBuilder_OvverideAppSettings()
Assert.NotNull(server);
}

[Fact]
public void AddVault_ConfigurationBuilder_TokenAppSettings()
{
var server = new TestServer(new WebHostBuilder()
.ConfigureAppConfiguration((context, builder) =>
{
builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);

builder.AddVault(options =>
{
options.Address = $"http://localhost:{fixture.Container.Port}";
options.Token = "root";
options.Solution = "unit-test";
options.KeyVault.Enable = true;
options.Mongo.Enable = true;
options.RabbitMQ.Enable = true;
options.AppName = "my-app";
});
}).ConfigureServices(services =>
{

}).Configure(app =>
{
var configuration = app.ApplicationServices.GetRequiredService<IConfiguration>();

var securityOptions = configuration.GetSection("Security").Get<SecurityOptions>();
var mongoOptions = configuration.GetSection("Mongo").Get<MongoOptions>();
var rabbitMQOptions = configuration.GetSection("RabbitMQ").Get<RabbitMQOptions>();

Assert.NotNull(securityOptions);
Assert.NotEmpty(securityOptions.ValidAudiences);
Assert.NotNull(securityOptions.ClientId);

Assert.NotNull(mongoOptions);
Assert.NotNull(mongoOptions.ConnectionString);
Assert.NotEqual("mongodb://localhost:27017", mongoOptions.ConnectionString);

Assert.NotNull(rabbitMQOptions);
Assert.NotNull(rabbitMQOptions.UserName);
Assert.NotNull(rabbitMQOptions.Password);
})
);

Assert.NotNull(server);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ public void VaultOptions_InvalidValues_Failed()
// Assert
Assert.NotEmpty(results);
Assert.Contains(results, x => x.ErrorMessage == "The Address field is required.");
Assert.Contains(results, x => x.ErrorMessage == "The RoleId and SecretId is required.");
Assert.Contains(results, x => x.ErrorMessage == "The AppName field is required.");
Assert.Contains(results, x => x.ErrorMessage == "The Solution field is required.");
Assert.Contains(results, x => x.ErrorMessage == "The RoleSufix field is required.");
Assert.Contains(results, x => x.ErrorMessage == "The SufixMoundPoint field is required.");
Assert.Contains(results, x => x.ErrorMessage == "The TemplateConnectionString field is required.");
Assert.Contains(results, x => x.ErrorMessage == "The TypeAuth is required.");
}

[Fact]
Expand Down Expand Up @@ -114,5 +114,4 @@ public void VaultOptions_KubernetesValues_Valid()
// Assert
Assert.Empty(results);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public void CreateClient_OptionsIsNull_ThrowArgumentNullException()

// Act
var exception = Assert.Throws<ArgumentNullException>(() => VaultClientFactory.Create(options));

// Assert
Assert.Equal("Value cannot be null. (Parameter 'options')", exception.Message);
}
Expand All @@ -40,14 +40,12 @@ public void CreateClient_KubernetesEnableIsFalse_ReturnVaultClient()
}

[Fact]
public void CreateClient_KubernetesEnableIsTrue_ReturnVaultClient()
public void CreateClient_KubernetesAuth_ReturnVaultClient()
{
// Arrange
var options = new VaultOptions
{
Address = "http://localhost:8200",
RoleId = "role-id",
SecretId = "secret-id",
AppName = "app-name"
};

Expand All @@ -60,4 +58,21 @@ public void CreateClient_KubernetesEnableIsTrue_ReturnVaultClient()
// Assert
Assert.NotNull(client);
}

[Fact]
public void CreateClient_ThrowVaultException_InvalidAuth()
{
// Arrange
var options = new VaultOptions
{
Address = "http://localhost:8200",
AppName = "app-name"
};

// Act
var exception = Assert.Throws<VaultException>(() => VaultClientFactory.Create(options));

// Assert
Assert.Equal("The authentication type is not defined.", exception.Message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,4 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Helpers\VaultContainer\resources\" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ services:
image: hashicorp/vault:latest
ports:
- "0:8200"
restart: unless-stopped
environment:
VAULT_DEV_ROOT_TOKEN_ID: "root"
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
Expand All @@ -18,8 +17,9 @@ services:
- ./resources/entrypoint.sh:/resources/entrypoint.sh
cap_add:
- IPC_LOCK
command: /bin/sh -c "chmod +x /resources/entrypoint.sh && chmod +x /resources/config.sh && /resources/entrypoint.sh"

entrypoint: /bin/sh -c "chmod +x /resources/entrypoint.sh && chmod +x /resources/config.sh && /resources/entrypoint.sh"


mongo:
image: mongo:latest
environment:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/bin/sh

export VAULT_ADDR="http://0.0.0.0:8200"

vault login token=root
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/sh
echo "Hello, world!"

echo "Starting Vault server in dev mode..."
vault server -dev &
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"role_id": "39d25686-e06b-a1ff-93a7-35eba37e7c6e",
"secret_id": "66615763-1ed1-4963-7ca7-c9d25778b1ab"
"role_id": "d02c6882-3189-a9b7-7dab-8eef2ead8d60",
"secret_id": "382b241b-3446-49b6-30d4-958abed58665"
}