Skip to content

Commit

Permalink
fix #362
Browse files Browse the repository at this point in the history
  • Loading branch information
rajanadar committed Sep 14, 2024
1 parent c0429d3 commit fa384b5
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using VaultSharp.V1.SecretsEngines.Database.Models;
using VaultSharp.V1.SecretsEngines.Database.Models.PostgreSQL;

namespace VaultSharp.V1.SecretsEngines.Database
{
/// <summary>
/// Converts the <see cref="ConnectionConfigModel" /> object from JSON.
/// </summary>
internal class ConnectionConfigModelJsonConverter : JsonConverter<ConnectionConfigModel>
{
public override void Write(Utf8JsonWriter writer, ConnectionConfigModel value, JsonSerializerOptions serializer)
{
// VERY IMPORTANT
// This will only work if the converter is registered programmatically and not via attributes
// Via attributes, the code goes into a stack overflow exception.
JsonSerializer.Serialize(writer, value);
}

public override ConnectionConfigModel Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}

using (var jsonDocument = JsonDocument.ParseValue(ref reader))
{
if (!jsonDocument.RootElement.TryGetProperty("plugin_name",
out var pluginNameProperty))
{
throw new JsonException();
}

string pluginName = pluginNameProperty.GetString();
var jsonString = jsonDocument.RootElement.GetRawText();

if (pluginName.Equals("postgresql-database-plugin"))
{
return JsonSerializer.Deserialize<PostgreSQLConnectionConfigModel>(jsonString);
}

return JsonSerializer.Deserialize<ConnectionConfigModel>(jsonString);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,38 @@ public async Task ConfigureConnectionAsync(string connectionName, ConnectionConf
{
await _polymath.MakeVaultApiRequest(mountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.Database, "/config/" + connectionName, HttpMethod.Post, requestData: connectionConfigModel).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext);
}

public async Task<Secret<ConnectionConfigModel>> ReadConnectionConfigAsync(string connectionName, string mountPoint = null, string wrapTimeToLive = null)
{
Checker.NotNull(connectionName, "connectionName");

return await _polymath.MakeVaultApiRequest<Secret<ConnectionConfigModel>>(mountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.Database, "/config/" + connectionName, HttpMethod.Get, wrapTimeToLive: wrapTimeToLive).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext);
}

public async Task<Secret<ListInfo>> ReadAllConnectionsAsync(string mountPoint = null, string wrapTimeToLive = null)
{
return await _polymath.MakeVaultApiRequest<Secret<ListInfo>>(mountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.Database, "/config?list=true", HttpMethod.Get, wrapTimeToLive: wrapTimeToLive).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext);
}

public async Task DeleteConnectionAsync(string connectionName, string mountPoint = null)
{
await _polymath.MakeVaultApiRequest(mountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.Database, "/config/" + connectionName, HttpMethod.Delete).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext);
}

public async Task ResetConnectionAsync(string connectionName, string mountPoint = null)
{
await _polymath.MakeVaultApiRequest(mountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.Database, "/reset/" + connectionName, HttpMethod.Post).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext);
}

public async Task ReloadPluginAsync(string pluginName, string mountPoint = null)
{
await _polymath.MakeVaultApiRequest(mountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.Database, "/reload/" + pluginName, HttpMethod.Post).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext);
}

public async Task RotateRootCredentialsAsync(string connectionName, string mountPoint = null)
{
await _polymath.MakeVaultApiRequest(mountPoint ?? _polymath.VaultClientSettings.SecretsEngineMountPoints.Database, "/rotate-root/" + connectionName, HttpMethod.Post).ConfigureAwait(_polymath.VaultClientSettings.ContinueAsyncTasksOnCapturedContext);
}

public async Task CreateRoleAsync(string roleName, Role role, string mountPoint = null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,96 @@ public interface IDatabaseSecretsEngine
/// <returns>The task</returns>
Task ConfigureConnectionAsync(string connectionName, ConnectionConfigModel connectionConfigModel, string mountPoint = null);

/// <summary>
/// This endpoint returns the configuration settings for a connection.
/// </summary>
/// <param name="connectionName">
/// <para>[required]</para>
/// Specifies the name for this database connection.
/// </param>
/// <param name="mountPoint"><para>[optional]</para>
/// The mount point for the Database backend. Defaults to <see cref="SecretsEngineMountPoints.Database" />
/// Provide a value only if you have customized the mount point.</param>
/// <param name="wrapTimeToLive">
/// <para>[optional]</para>
/// The TTL for the token and can be either an integer number of seconds or a string duration of seconds.
/// </param>
/// <returns>The role info.</returns>
Task<Secret<ConnectionConfigModel>> ReadConnectionConfigAsync(string connectionName, string mountPoint = null, string wrapTimeToLive = null);

/// <summary>
/// This endpoint returns a list of available connections.
/// Only the connection names are returned, not any values.
/// </summary>
/// <param name="mountPoint"><para>[optional]</para>
/// The mount point for the Database backend. Defaults to <see cref="SecretsEngineMountPoints.Database" />
/// Provide a value only if you have customized the mount point.</param>
/// <param name="wrapTimeToLive">
/// <para>[optional]</para>
/// The TTL for the token and can be either an integer number of seconds or a string duration of seconds.
/// </param>
/// <returns>The connection names.</returns>
Task<Secret<ListInfo>> ReadAllConnectionsAsync(string mountPoint = null, string wrapTimeToLive = null);

/// <summary>
/// Deletes the connection config.
/// </summary>
/// <param name="connectionName">
/// <para>[required]</para>
/// Specifies the name for this database connection.
/// </param>
/// <param name="mountPoint"><para>[optional]</para>
/// The mount point for the AD backend. Defaults to <see cref="SecretsEngineMountPoints.ActiveDirectory" />
/// Provide a value only if you have customized the mount point.</param>
/// <returns>The task</returns>
Task DeleteConnectionAsync(string connectionName, string mountPoint = null);

/// <summary>
/// This endpoint closes a connection and it's underlying plugin and restarts it
/// with the configuration stored in the barrier.
/// </summary>
/// <param name="connectionName">
/// <para>[required]</para>
/// Specifies the name for this database connection.
/// </param>
/// <param name="mountPoint"><para>[optional]</para>
/// The mount point for the AD backend. Defaults to <see cref="SecretsEngineMountPoints.ActiveDirectory" />
/// Provide a value only if you have customized the mount point.</param>
/// <returns>The task</returns>
Task ResetConnectionAsync(string connectionName, string mountPoint = null);

/// <summary>
/// This endpoint performs the same operation as reset connection but for all connections
/// that reference a specific plugin name.
/// This can be useful to restart a specific plugin after it's been upgraded in the plugin catalog.
/// </summary>
/// <param name="pluginName">
/// <para>[required]</para>
/// Specifies the name of the plugin for which all connections should be reset.
/// </param>
/// <param name="mountPoint"><para>[optional]</para>
/// The mount point for the AD backend. Defaults to <see cref="SecretsEngineMountPoints.ActiveDirectory" />
/// Provide a value only if you have customized the mount point.</param>
/// <returns>The task</returns>
Task ReloadPluginAsync(string pluginName, string mountPoint = null);

/// <summary>
/// This endpoint is used to rotate the "root" user credentials stored for the database connection.
/// This user must have permissions to update its own password.
/// Use caution: the root user's password will not be accessible once rotated so
/// it is highly recommended that you create a user for Vault to utilize
/// rather than using the actual root user.
/// </summary>
/// <param name="connectionName">
/// <para>[required]</para>
/// Specifies the name for this database connection.
/// </param>
/// <param name="mountPoint"><para>[optional]</para>
/// The mount point for the AD backend. Defaults to <see cref="SecretsEngineMountPoints.ActiveDirectory" />
/// Provide a value only if you have customized the mount point.</param>
/// <returns>The task</returns>
Task RotateRootCredentialsAsync(string connectionName, string mountPoint = null);

/// <summary>
/// This endpoint creates or updates a role definition.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using VaultSharp.V1.SecretsEngines.Database.Models.PostgreSQL;

namespace VaultSharp.V1.SecretsEngines.Database.Models
{
/// <summary>
/// Connection Config Model.
/// </summary>

// VERY IMPORTANT: Do not use Attribute based converter notation.
// It is a short-coming of STJ to go into a infinite loop
// https://github.com/dotnet/runtime/issues/46372
// Use programmatic registration.
// [JsonConverter(typeof(ConnectionConfigModelJsonConverter))]

[JsonDerivedType(typeof(PostgreSQLConnectionConfigModel))]
public class ConnectionConfigModel
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace VaultSharp.V1.SecretsEngines.Database.Models.PostgreSQL
/// </summary>
public class PostgreSQLConnectionConfigModel : ConnectionConfigModel
{
[JsonIgnore]
public DatabaseProviderType DatabaseProviderType { get; } = DatabaseProviderType.PostgreSQL;

/// <summary>
Expand Down
10 changes: 9 additions & 1 deletion src/VaultSharp/VaultClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using VaultSharp.Core;
using System.Text.Json;
using VaultSharp.Core;
using VaultSharp.V1;
using VaultSharp.V1.SecretsEngines.Database;

namespace VaultSharp
{
Expand All @@ -16,6 +18,12 @@ public class VaultClient : IVaultClient
/// <param name="vaultClientSettings"></param>
public VaultClient(VaultClientSettings vaultClientSettings)
{
// Due to the bug https://github.com/dotnet/runtime/issues/46372
// Register some converters programmatically here
JsonSerializerOptions.Default.Converters.Add(new ConnectionConfigModelJsonConverter());

// raja todo: Change AuditBackend converter from Attribute to this.

polymath = new Polymath(vaultClientSettings);
V1 = new VaultClientV1(polymath);
}
Expand Down

0 comments on commit fa384b5

Please sign in to comment.