aboutsummaryrefslogtreecommitdiff
path: root/lib/VNLib.Plugins.Extensions.Loading/src/Secrets/PluginSecretStore.cs
blob: 6b20e3030be77197e1f14764125f3ab8035acd64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
* Copyright (c) 2024 Vaughn Nugent
* 
* Library: VNLib
* Package: VNLib.Plugins.Extensions.Loading
* File: PluginSecretStore.cs 
*
* PluginSecretStore.cs is part of VNLib.Plugins.Extensions.Loading which is 
* part of the larger VNLib collection of libraries and utilities.
*
* VNLib.Plugins.Extensions.Loading is free software: you can redistribute it and/or modify 
* it under the terms of the GNU Affero General Public License as 
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* VNLib.Plugins.Extensions.Loading is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see https://www.gnu.org/licenses/.
*/

using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

using VNLib.Utils.Memory;

using static VNLib.Plugins.Extensions.Loading.PluginSecretConstants;

namespace VNLib.Plugins.Extensions.Loading
{
    /// <summary>
    /// A secret store for a plugin that can be used to fetch secrets from plugin configuration
    /// </summary>
    /// <param name="plugin">The plugin instance to get secrets from</param>
    public readonly struct PluginSecretStore(PluginBase plugin) : IEquatable<PluginSecretStore>
    {
        private readonly PluginBase _plugin = plugin;

        /// <summary>
        /// Gets the ambient vault client for the current plugin
        /// if the configuration is loaded, null otherwise
        /// </summary>
        /// <returns>The ambient <see cref="IVaultClient"/> if loaded, null otherwise</returns>
        /// <exception cref="KeyNotFoundException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        public IHCVaultClient? GetVaultClient() => LoadingExtensions.GetOrCreateSingleton(_plugin, TryGetVaultLoader);

        private static HCVaultClient? TryGetVaultLoader(PluginBase pbase)
        {
            //Get vault config
            IConfigScope? conf = pbase.TryGetConfig(VAULT_OBJECT_NAME);

            if (conf is null)
            {
                return null;
            }

            //try get server address creds from config
            string serverAddress = conf.GetRequiredProperty(VAULT_URL_KEY, p => p.GetString()!);
            bool trustCert = conf.TryGetValue(VAULT_TRUST_CERT_KEY, out JsonElement trustCertEl) && trustCertEl.GetBoolean();

            int version = 2;    //Default to version 2 now
            string? authToken;

            //Get authentication method from config
            if (conf.TryGetValue(VAULT_TOKEN_KEY, out JsonElement tokenEl))
            {
                //Init token
                authToken = tokenEl.GetString();
            }
            //Try to get the token as an environment variable
            else if (Environment.GetEnvironmentVariable(VAULT_TOKNE_ENV_NAME) != null)
            {
                authToken = Environment.GetEnvironmentVariable(VAULT_TOKNE_ENV_NAME)!;
            }
            else
            {
                throw new KeyNotFoundException($"Failed to load the vault authentication method from {VAULT_OBJECT_NAME}");
            }

            _ = authToken ?? throw new KeyNotFoundException($"Failed to load the vault authentication method from {VAULT_OBJECT_NAME}");

            //Check for vault kv version, otherwise use the default
            if (conf.TryGetValue(VAULT_KV_VERSION_KEY, out JsonElement kvVersionEl))
            {
                version = kvVersionEl.GetInt32();
            }

            //create vault client, invalid or nulls will raise exceptions here
            return HCVaultClient.Create(serverAddress, authToken, version, trustCert, MemoryUtil.Shared);
        }

        ///<inheritdoc/>
        public Task<ISecretResult?> TryGetSecretAsync(string secretName, CancellationToken cancellation = default)
        {
            IOnDemandSecret secret = GetOnDemandSecret(secretName);
            return secret.FetchSecretAsync(cancellation);
        }

        ///<inheritdoc/>
        public ISecretResult? TryGetSecret(string secretName)
        {
            IOnDemandSecret secret = GetOnDemandSecret(secretName);
            return secret.FetchSecret();
        }

        ///<inheritdoc/>
        public IOnDemandSecret GetOnDemandSecret(string secretName)
        {
            ArgumentException.ThrowIfNullOrWhiteSpace(secretName);
            return new OnDemandSecret(_plugin, secretName, GetVaultClient());
        }

        ///<inheritdoc/>
        public override bool Equals(object? obj) => obj is PluginSecretStore store && Equals(store);

        ///<inheritdoc/>
        public static bool operator ==(PluginSecretStore left, PluginSecretStore right) => left.Equals(right);

        ///<inheritdoc/>
        public static bool operator !=(PluginSecretStore left, PluginSecretStore right) => !(left == right);

        /// <inheritdoc/>
        public bool Equals(PluginSecretStore other) => ReferenceEquals(other._plugin, _plugin);

        ///<inheritdoc/>
        public override int GetHashCode() => _plugin.GetHashCode();
    }
}