Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
Add keyvault operations and models
Browse files Browse the repository at this point in the history
  • Loading branch information
stas committed Apr 25, 2022
1 parent 3a93de4 commit 7c39a84
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/ApiService/ApiService/ApiService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Faithlife.Utility" Version="0.12.2" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.3.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage" Version="5.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.EventGrid" Version="2.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
Expand Down
28 changes: 28 additions & 0 deletions src/ApiService/ApiService/OneFuzzTypes/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -496,3 +496,31 @@ public record AdoTemplate();
public record TeamsTemplate();

public record GithubIssuesTemplate();


public record SecretAddress(Uri Url);


/// This class allows us to store some data that are intended to be secret
/// The secret field stores either the raw data or the address of that data
/// This class allows us to maintain backward compatibility with existing
/// NotificationTemplate classes
public record SecretData<T>(T Secret)
{
public override string ToString()
{
if (Secret is SecretAddress)
{
if (Secret is null)
{
return string.Empty;
}
else
{
return Secret.ToString()!;
}
}
else
return "[REDACTED]";
}
}
3 changes: 2 additions & 1 deletion src/ApiService/ApiService/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,13 @@ public static void Main()
.AddScoped<IReports, Reports>()
.AddScoped<INotificationOperations, NotificationOperations>()
.AddScoped<IUserCredentials, UserCredentials>()

.AddScoped<ISecretsOperations, SecretsOperations>()

//Move out expensive resources into separate class, and add those as Singleton
// ArmClient, Table Client(s), Queue Client(s), HttpClient, etc.
.AddSingleton<ICreds, Creds>()
.AddSingleton<IServiceConfig, ServiceConfiguration>()
.AddSingleton<ISingletonResources, SingletonResources>()
)
.Build();

Expand Down
15 changes: 15 additions & 0 deletions src/ApiService/ApiService/SingletonResources.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Microsoft.OneFuzz.Service;

public interface ISingletonResources
{

public System.Net.Http.HttpClient HttpClient { get; }
}


public class SingletonResources : ISingletonResources
{
System.Net.Http.HttpClient _httpClient = new();

public System.Net.Http.HttpClient HttpClient => _httpClient;
}
4 changes: 2 additions & 2 deletions src/ApiService/ApiService/onefuzzlib/Creds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ public class Creds : ICreds

public Creds(IServiceConfig config)
{
_armClient = new ArmClient(this.GetIdentity(), this.GetSubcription());
_azureCredential = new DefaultAzureCredential();
_config = config;
_azureCredential = new DefaultAzureCredential();
_armClient = new ArmClient(this.GetIdentity(), this.GetSubcription());
}

public DefaultAzureCredential GetIdentity()
Expand Down
139 changes: 139 additions & 0 deletions src/ApiService/ApiService/onefuzzlib/Secrets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Security.KeyVault.Secrets;
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;

namespace Microsoft.OneFuzz.Service;

public interface ISecretsOperations
{
public (Uri, string) ParseSecretUrl(Uri secretsUrl);
public Task<SecretData<SecretAddress>?> SaveToKeyvault<T>(SecretData<T> secretData);
public Task<string?> GetSecretStringValue<T>(SecretData<T> data);

public Task<KeyVaultSecret> StoreInKeyvault(Uri keyvaultUrl, string secretName, string secretValue);
public Task<KeyVaultSecret> GetSecret(Uri secretUrl);
public Task<T?> GetSecretObj<T>(Uri secretUrl);
public Task<DeleteSecretOperation> DeleteSecret(Uri secretUrl);
public Task<DeleteSecretOperation?> DeleteRemoteSecretData<T>(SecretData<T> data);
public Uri GetKeyvaultAddress();

}

public class SecretsOperations : ISecretsOperations
{
private readonly ICreds _creds;
private readonly IServiceConfig _config;
public SecretsOperations(ICreds creds, IServiceConfig config)
{
_creds = creds;
_config = config;
}

public (Uri, string) ParseSecretUrl(Uri secretsUrl)
{
// format: https://{vault-name}.vault.azure.net/secrets/{secret-name}/{version}
var vaultUrl = $"{secretsUrl.Scheme}://{secretsUrl.Host}";
var secretName = secretsUrl.Segments[secretsUrl.Segments.Length - 2].Trim('/');
return (new Uri(vaultUrl), secretName);
}

public async Task<SecretData<SecretAddress>?> SaveToKeyvault<T>(SecretData<T> secretData)
{
if (secretData == null || secretData.Secret is null)
return null;

if (secretData.Secret is SecretAddress)
{
return secretData as SecretData<SecretAddress>;
}
else
{
var secretName = Guid.NewGuid();
string secretValue;
if (secretData.Secret is string)
{
secretValue = (secretData.Secret as string)!.Trim();
}
else
{
secretValue = JsonSerializer.Serialize(secretData.Secret, EntityConverter.GetJsonSerializerOptions());
}

var kv = await StoreInKeyvault(GetKeyvaultAddress(), secretName.ToString(), secretValue);
return new SecretData<SecretAddress>(new SecretAddress(kv.Id));
}
}

public async Task<string?> GetSecretStringValue<T>(SecretData<T> data)
{
if (data.Secret is null)
{
return null;
}

if (data.Secret is SecretAddress)
{
var secret = await GetSecret((data.Secret as SecretAddress)!.Url);
return secret.Value;
}
else
{
return data.Secret.ToString();
}
}

public Uri GetKeyvaultAddress()
{
// https://docs.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#vault-name-and-object-name
var keyvaultName = _config!.OneFuzzKeyvault;
return new Uri($"https://{keyvaultName}.vault.azure.net");
}


public async Task<KeyVaultSecret> StoreInKeyvault(Uri keyvaultUrl, string secretName, string secretValue)
{
var keyvaultClient = new SecretClient(keyvaultUrl, _creds.GetIdentity());
var r = await keyvaultClient.SetSecretAsync(secretName, secretValue);
return r.Value;
}

public async Task<KeyVaultSecret> GetSecret(Uri secretUrl)
{
var (vaultUrl, secretName) = ParseSecretUrl(secretUrl);
var keyvaultClient = new SecretClient(vaultUrl, _creds.GetIdentity());
return await keyvaultClient.GetSecretAsync(secretName);
}

public async Task<T?> GetSecretObj<T>(Uri secretUrl)
{
var secret = await GetSecret(secretUrl);
if (secret is null)
return default(T);
else
return JsonSerializer.Deserialize<T>(secret.Value, EntityConverter.GetJsonSerializerOptions());
}

public async Task<DeleteSecretOperation> DeleteSecret(Uri secretUrl)
{
var (vaultUrl, secretName) = ParseSecretUrl(secretUrl);
var keyvaultClient = new SecretClient(vaultUrl, _creds.GetIdentity());
return await keyvaultClient.StartDeleteSecretAsync(secretName);
}

public async Task<DeleteSecretOperation?> DeleteRemoteSecretData<T>(SecretData<T> data)
{
if (data.Secret is SecretAddress)
{
if (data.Secret is not null)
return await DeleteSecret((data.Secret as SecretAddress)!.Url);
else
return null;
}
else
{
return null;
}
}

}
12 changes: 12 additions & 0 deletions src/ApiService/ApiService/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@
"System.Text.Json": "4.7.2"
}
},
"Azure.Security.KeyVault.Secrets": {
"type": "Direct",
"requested": "[4.3.0, )",
"resolved": "4.3.0",
"contentHash": "GRnmQzTXDVABry1rC8PwuVOHSDCUGn4Om1ABTCzWfHdDSOwRydtQ13ucJ1Z0YtdajklNwxEL6lhHGhFCI0diAw==",
"dependencies": {
"Azure.Core": "1.23.0",
"System.Memory": "4.5.4",
"System.Text.Json": "4.7.2",
"System.Threading.Tasks.Extensions": "4.5.4"
}
},
"Azure.Storage.Blobs": {
"type": "Direct",
"requested": "[12.11.0, )",
Expand Down

0 comments on commit 7c39a84

Please sign in to comment.