aboutsummaryrefslogtreecommitdiff
path: root/back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs
blob: 9ea9d24a555b40e6b218c18e5206c06d62f42dd9 (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright (C) 2023 Vaughn Nugent
//
// This program 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.
//
// This program 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.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

using VaultSharp;
using VaultSharp.V1.Commons;

using VNLib.Utils.Memory;
using VNLib.Plugins.Essentials.Extensions;


namespace NVault.VaultExtensions
{

    public static class VaultClientExtensions
    {
        
        private static string GetKeyPath(IVaultClientScope client, in VaultUserScope scope, string itemPath)
        {
            //Allow for null entry path
            return client.EntryPath == null ? $"{scope.UserId}/{itemPath}" : $"{client.EntryPath}/{scope.UserId}/{itemPath}";
        }


        public static Task<PrivateString?> GetSecretAsync(this IVaultClient client, IVaultKvClientScope scope, VaultUserScope user, string path)
        {
            return GetSecretAsync(client, scope, user, path, scope.StorageProperty);
        }

        public static async Task<PrivateString?> GetSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path, string property)
        {
            //Get the path complete path for the scope
            string fullPath = GetKeyPath(scope, user, path);

            //Get the secret from the vault
            Secret<SecretData> result = await client.V1.Secrets.KeyValue.V2.ReadSecretAsync(fullPath, mountPoint:scope.MountPoint);

            //Try to get the secret value from the store
            string? value = result.Data.Data.GetValueOrDefault(property)?.ToString();

            //Return the secret value as a private string
            return value == null ? null : PrivateString.ToPrivateString(value, true);
        }

        /// <summary>
        /// Writes a secret to the vault that is scoped by the vault scope, and the user scope.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="scope">The client scope configuration</param>
        /// <param name="user">The user scope to isolate the </param>
        /// <param name="path">The item path within the current scope</param>
        /// <param name="secret">The secret value to set at the desired property</param>
        /// <returns>A task that resolves when the secret has been updated</returns>
        public static async Task<CurrentSecretMetadata> SetSecretAsync(this IVaultClient client, IVaultKvClientScope scope, VaultUserScope user, string path, PrivateString secret)
        {
            Dictionary<string, string> secretDict = new()
            {
                //Dangerous cast, but we know the type
                { scope.StorageProperty, (string)secret }
            };

            //Await the result so we be sure the secret is not destroyed
            return await SetSecretAsync(client, scope, user, path, secretDict);
        }

        /// <summary>
        /// Writes a secret to the vault that is scoped by the vault scope, and the user scope.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="scope">The client scope configuration</param>
        /// <param name="user">The user scope to isolate the </param>
        /// <param name="path">The item path within the current scope</param>
        /// <param name="secret">The secret value to set at the desired property</param>
        /// <returns>A task that resolves when the secret has been updated</returns>
        public static async Task<CurrentSecretMetadata> SetSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path, IDictionary<string, string> secret)
        {
            //Get the path complete path for the scope
            string fullPath = GetKeyPath(scope, user, path);

            //Get the secret from the vault
            Secret<CurrentSecretMetadata> result = await client.V1.Secrets.KeyValue.V2.WriteSecretAsync(fullPath, secret, mountPoint:scope.MountPoint);

            return result.Data;
        }

        /// <summary>
        /// Deletes a secret from the vault that is scoped by the vault scope, and the user scope.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="scope">The client scope</param>
        /// <param name="user">The vault user scope</param>
        /// <param name="path">The path to the storage</param>
        /// <returns>A task that resolves when the delete operation has completed</returns>
        public static Task DeleteSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path)
        {
            string fullApth = GetKeyPath(scope, user, path);
            return client.V1.Secrets.KeyValue.V2.DeleteSecretAsync(fullApth, mountPoint:scope.MountPoint);
        }

        /// <summary>
        /// Deletes a secret from the vault
        /// </summary>
        /// <param name="user">The user scope of the secret</param>
        /// <param name="path">The path to the secret</param>
        /// <param name="cancellation">A token to cancel the operation</param>
        /// <returns>A task that returns when the operation has completed</returns>
        public static Task DeleteSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, CancellationToken cancellation)
        {
            return store.DeleteSecretAsync(user, path).WaitAsync(cancellation);
        }


        /// <summary>
        /// Gets a secret from the vault at the specified path and user scope
        /// </summary>
        /// <param name="user">The user scope to get the value from</param>
        /// <param name="path">The secret path</param>
        /// <param name="cancellation">A token to cancel the operation</param>
        /// <returns>A task that resolves the secret if found, null otherwise</returns>
        public static Task<PrivateString?> GetSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, CancellationToken cancellation)
        {
            return store.GetSecretAsync(user, path).WaitAsync(cancellation);
        }


        /// <summary>
        /// Sets a secret in the vault at the specified path and user scope
        /// </summary>
        /// <param name="user">The user scope to store the value at</param>
        /// <param name="path">The path to the secret</param>
        /// <param name="secret">The secret value to set</param>
        /// <param name="cancellation">The cancellation token</param>
        /// <returns>A task that resolves when the secret has been updated</returns>
        public static Task SetSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, PrivateString secret, CancellationToken cancellation)
        {
            return store.SetSecretAsync(user, path, secret).WaitAsync(cancellation);
        }


    }
}