зеркало из https://github.com/Azure/Sia-Gateway.git
Merge remote-tracking branch 'origin/master' into pdimit/dry
This commit is contained in:
Коммит
c357195249
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<PackageId>Microsoft.Sia.Connectors.Tickets</PackageId>
|
||||
<Version>1.0.5-alpha</Version>
|
||||
<Version>1.0.6-alpha</Version>
|
||||
<Authors>pdimit, magpint, jache, chtownes</Authors>
|
||||
<Company>Microsoft</Company>
|
||||
<Product>SRE Incident Assistant</Product>
|
||||
|
@ -15,13 +15,13 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="6.1.1" />
|
||||
<PackageReference Include="Microsoft.Sia.Shared" Version="1.0.7-alpha" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Sia.Data.Incident\Sia.Data.Incidents.csproj" />
|
||||
<ProjectReference Include="..\Sia.Domain.ApiModels\Sia.Domain.ApiModels.csproj" />
|
||||
<ProjectReference Include="..\Sia.Domain\Sia.Domain.csproj" />
|
||||
<ProjectReference Include="..\Sia.Shared\Sia.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -3,6 +3,7 @@
|
|||
public enum AuthenticationType
|
||||
{
|
||||
None,
|
||||
Certificate
|
||||
Certificate,
|
||||
CertificateFromKeyVault
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Sia.Connectors.Tickets;
|
||||
using Sia.Connectors.Tickets.TicketProxy;
|
||||
using Sia.Shared.Authentication;
|
||||
|
||||
namespace Sia.Gateway.Initialization
|
||||
{
|
||||
|
@ -12,7 +14,10 @@ namespace Sia.Gateway.Initialization
|
|||
|
||||
public static IServiceCollection AddProxyWithCert(this IServiceCollection services, string endpoint, string certThumbprint)
|
||||
=> services.AddProxy(new ProxyConnectionInfo(endpoint, certThumbprint));
|
||||
|
||||
|
||||
|
||||
public static IServiceCollection AddProxyWithCertFromKeyVault(this IServiceCollection services, string endpoint, KeyVaultConfiguration config, string certName)
|
||||
=> services.AddProxy(new ProxyConnectionInfo(endpoint, config, certName));
|
||||
|
||||
private static IServiceCollection AddProxy(this IServiceCollection services, ProxyConnectionInfo proxyConnection)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Sia.Shared.Authentication;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Sia.Shared.Authentication;
|
||||
using Sia.Shared.Validation;
|
||||
using System;
|
||||
|
||||
|
@ -6,14 +7,38 @@ namespace Sia.Connectors.Tickets.TicketProxy
|
|||
{
|
||||
public class ProxyConnectionInfo
|
||||
{
|
||||
private AzureSecretVault _keyVault;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates ProxyConnectionInfo with no Authentication
|
||||
/// </summary>
|
||||
/// <param name="endpoint">Proxy Endpoint</param>
|
||||
public ProxyConnectionInfo(string endpoint)
|
||||
: this(endpoint, AuthenticationType.None)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates ProxyConnectionInfo with certificate authentication from a local cert
|
||||
/// </summary>
|
||||
/// <param name="endpoint">Proxy Endpoint</param>
|
||||
/// <param name="certThumbprint">Thumbprint for searching local certificate store</param>
|
||||
public ProxyConnectionInfo(string endpoint, string certThumbprint)
|
||||
: this(endpoint, AuthenticationType.Certificate)
|
||||
{
|
||||
CertThumbprint = ThrowIf.NullOrWhiteSpace(certThumbprint, nameof(certThumbprint));
|
||||
CertIdentifier = ThrowIf.NullOrWhiteSpace(certThumbprint, nameof(certThumbprint));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates ProxyConnectionInfo with certificate authentication using a certificate retrieved from keyvault
|
||||
/// </summary>
|
||||
/// <param name="endpoint">Proxy Endpoint</param>
|
||||
/// <param name="config">Configuration root for initialization</param>
|
||||
/// <param name="vaultName">Key vault name</param>
|
||||
public ProxyConnectionInfo(string endpoint, KeyVaultConfiguration config, string vaultName)
|
||||
: this(endpoint, AuthenticationType.CertificateFromKeyVault)
|
||||
{
|
||||
_keyVault = new AzureSecretVault(config);
|
||||
CertIdentifier = ThrowIf.NullOrWhiteSpace(vaultName, nameof(vaultName));
|
||||
}
|
||||
|
||||
protected ProxyConnectionInfo(string endpoint, AuthenticationType authType)
|
||||
|
@ -23,9 +48,9 @@ namespace Sia.Connectors.Tickets.TicketProxy
|
|||
}
|
||||
|
||||
public ProxyClient GetClient() => new ProxyClient(ClientFactory.GetClient(), Endpoint);
|
||||
public AuthenticationType AuthenticationType { get; set; }
|
||||
public string Endpoint { get; set; }
|
||||
public string CertThumbprint { get; set; }
|
||||
public AuthenticationType AuthenticationType { get; protected set; }
|
||||
public string Endpoint { get; protected set; }
|
||||
public string CertIdentifier { get; protected set; }
|
||||
|
||||
protected IHttpClientFactory ClientFactory
|
||||
{
|
||||
|
@ -34,7 +59,9 @@ namespace Sia.Connectors.Tickets.TicketProxy
|
|||
switch (AuthenticationType)
|
||||
{
|
||||
case AuthenticationType.Certificate:
|
||||
return new LocalCertificateRetriever(CertThumbprint);
|
||||
return new LocalCertificateRetriever(CertIdentifier);
|
||||
case AuthenticationType.CertificateFromKeyVault:
|
||||
return new KeyVaultCertificateRetriever(_keyVault, CertIdentifier);
|
||||
case AuthenticationType.None:
|
||||
return new UnauthenticatedClientFactory();
|
||||
default:
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.DataAnnotations" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Sia.Shared" Version="1.0.7-alpha" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Sia.Domain\Sia.Domain.csproj" />
|
||||
<ProjectReference Include="..\Sia.Shared\Sia.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,8 +1,10 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Sia.Domain.ApiModels;
|
||||
using Sia.Gateway.Authentication;
|
||||
using Sia.Gateway.Protocol;
|
||||
using Sia.Gateway.Hubs;
|
||||
using Sia.Gateway.Requests;
|
||||
using Sia.Gateway.Requests.Events;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -13,10 +15,15 @@ namespace Sia.Gateway.Controllers
|
|||
public class EventsController : BaseController
|
||||
{
|
||||
private const string notFoundMessage = "Incident or event not found";
|
||||
private readonly HubConnectionBuilder _hubConnectionBuilder;
|
||||
|
||||
public EventsController(IMediator mediator, AzureActiveDirectoryAuthenticationInfo authConfig, IUrlHelper urlHelper)
|
||||
public EventsController(IMediator mediator,
|
||||
AzureActiveDirectoryAuthenticationInfo authConfig,
|
||||
HubConnectionBuilder hubConnectionBuilder,
|
||||
IUrlHelper urlHelper)
|
||||
: base(mediator, authConfig, urlHelper)
|
||||
{
|
||||
_hubConnectionBuilder = hubConnectionBuilder;
|
||||
}
|
||||
|
||||
[HttpGet(Name = nameof(GetEvents))]
|
||||
|
@ -47,7 +54,18 @@ namespace Sia.Gateway.Controllers
|
|||
{
|
||||
return NotFound(notFoundMessage);
|
||||
}
|
||||
await SendEventToSubscribers(result);
|
||||
return Created($"api/incidents/{result.IncidentId}/events/{result.Id}", result);
|
||||
}
|
||||
|
||||
private async Task SendEventToSubscribers(Domain.Event result)
|
||||
{
|
||||
var eventHubConnection = _hubConnectionBuilder
|
||||
.WithUrl($"{Request.Scheme}://{Request.Host}/{EventsHub.HubPath}")
|
||||
.Build();
|
||||
await eventHubConnection.StartAsync();
|
||||
await eventHubConnection.SendAsync("Send", result);
|
||||
eventHubConnection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using Microsoft.AspNetCore.SignalR;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Sia.Domain;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sia.Gateway.Hubs
|
||||
{
|
||||
public class EventsHub : Hub
|
||||
{
|
||||
public const string HubPath = "events/live";
|
||||
public EventsHub() : base()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Task Send(Event ev)
|
||||
{
|
||||
return Clients.All.InvokeAsync("Send", Json(ev));
|
||||
}
|
||||
|
||||
private string Json<T>(T toSerialize) => JsonConvert.SerializeObject(toSerialize, new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
});
|
||||
}
|
||||
}
|
|
@ -4,13 +4,19 @@ using Sia.Shared.Authentication;
|
|||
|
||||
namespace Sia.Gateway.Initialization
|
||||
{
|
||||
public static class SecretVaultStartup
|
||||
public static class ApplicationInsightsStartup
|
||||
{
|
||||
public static AzureSecretVault Initialize(IHostingEnvironment env, IConfigurationRoot configuration)
|
||||
public static AzureSecretVault InitializeApplicationInsights(this IHostingEnvironment env, IConfigurationRoot configuration)
|
||||
{
|
||||
//Needs to be done in the initial Startup.Startup() method because Application Insights registers itself prior
|
||||
//to ConfigureServices being run
|
||||
var secrets = new AzureSecretVault(configuration);
|
||||
var secrets = new AzureSecretVault(
|
||||
new KeyVaultConfiguration(
|
||||
configuration["ClientId"],
|
||||
configuration["ClientSecret"],
|
||||
configuration["KeyVault:VaultName"]
|
||||
)
|
||||
);
|
||||
|
||||
var vaultTask = secrets.Get(configuration.GetSection("KeyVault")["InstrumentationKeyName"]);
|
||||
vaultTask.Wait();
|
|
@ -2,6 +2,7 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Sia.Gateway.Hubs;
|
||||
using Sia.Gateway.Middleware;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -14,6 +15,9 @@ namespace Sia.Gateway.Initialization
|
|||
|
||||
public static void AddMiddleware(this IApplicationBuilder app, IHostingEnvironment env, IConfigurationRoot configuration)
|
||||
{
|
||||
app.UseAuthentication();
|
||||
app.UseSession();
|
||||
|
||||
app.UseCors(builder =>
|
||||
builder
|
||||
.WithOrigins(LoadAcceptableOriginsFromConfig(configuration))
|
||||
|
@ -21,8 +25,11 @@ namespace Sia.Gateway.Initialization
|
|||
.AllowAnyMethod()
|
||||
.AllowCredentials()
|
||||
);
|
||||
app.UseAuthentication();
|
||||
app.UseSession();
|
||||
|
||||
app.UseSignalR(routes =>
|
||||
{
|
||||
routes.MapHub<EventsHub>(EventsHub.HubPath);
|
||||
});
|
||||
|
||||
if (env.IsDevelopment() || env.IsStaging()) app.UseDeveloperExceptionPage();
|
||||
app.UseMiddleware<ExceptionHandler>();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using MediatR;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
|
@ -13,7 +14,12 @@ using Sia.Data.Incidents;
|
|||
using Sia.Gateway.Authentication;
|
||||
using Sia.Gateway.Requests;
|
||||
using Sia.Gateway.ServiceRepositories;
|
||||
using Sia.Shared.Authentication;
|
||||
using Sia.Shared.Validation;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
|
@ -30,34 +36,7 @@ namespace Sia.Gateway.Initialization
|
|||
if (env.IsDevelopment()) services.AddDbContext<IncidentContext>(options => options.UseInMemoryDatabase("Live"));
|
||||
if (env.IsStaging()) services.AddDbContext<IncidentContext>(options => options.UseSqlServer(config.GetConnectionString("incidentStaging")));
|
||||
|
||||
var ticketConnectorAssemblyPath = config["Connector:Ticket:Path"];
|
||||
|
||||
if (!string.IsNullOrEmpty(ticketConnectorAssemblyPath))
|
||||
{
|
||||
LoadConnectorFromAssembly(services, env, config, ticketConnectorAssemblyPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
var proxyEndpoint = config["Connector:Ticket:ProxyEndpoint"];
|
||||
if (!string.IsNullOrEmpty(proxyEndpoint))
|
||||
{
|
||||
services.AddIncidentClient(typeof(Ticket));
|
||||
var proxyAuthType = config["Connector:Ticket:ProxyAuthType"];
|
||||
if (proxyAuthType == "Certificate")
|
||||
{
|
||||
services.AddProxyWithCert(proxyEndpoint, config["Connector:Ticket:ProxyCertThumbprint"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddProxyWithoutAuth(proxyEndpoint);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddIncidentClient(typeof(EmptyTicket));
|
||||
services.AddNoTicketingSystem();
|
||||
}
|
||||
}
|
||||
AddTicketingConnector(services, env, config);
|
||||
|
||||
services.AddScoped<IEventRepository, EventRepository>();
|
||||
services.AddScoped<IEngagementRepository, EngagementRepository>();
|
||||
|
@ -66,6 +45,57 @@ namespace Sia.Gateway.Initialization
|
|||
services.AddSingleton<AzureActiveDirectoryAuthenticationInfo>(i => incidentAuthConfig);
|
||||
}
|
||||
|
||||
private static void AddTicketingConnector(IServiceCollection services, IHostingEnvironment env, IConfigurationRoot config)
|
||||
{
|
||||
if (TryGetConfigValue(config, "Connector:Ticket:Path", out var ticketConnectorAssemblyPath))
|
||||
{
|
||||
LoadConnectorFromAssembly(services, env, config, ticketConnectorAssemblyPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryGetConfigValue(config, "Connector:Ticket:ProxyEndpoint", out var proxyEndpoint))
|
||||
{
|
||||
AddProxyConnector(services, config, proxyEndpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
services.AddIncidentClient(typeof(EmptyTicket));
|
||||
services.AddNoTicketingSystem();
|
||||
}
|
||||
|
||||
private static bool TryGetConfigValue(IConfigurationRoot config, string configName, out string configValue)
|
||||
{
|
||||
ThrowIf.NullOrWhiteSpace(configName, nameof(configName));
|
||||
configValue = config[configName];
|
||||
return !string.IsNullOrEmpty(configValue);
|
||||
}
|
||||
|
||||
private static void AddProxyConnector(IServiceCollection services, IConfigurationRoot config, string proxyEndpoint)
|
||||
{
|
||||
services.AddIncidentClient(typeof(Ticket));
|
||||
var proxyAuthType = config["Connector:Ticket:ProxyAuthType"];
|
||||
switch(proxyAuthType)
|
||||
{
|
||||
case "Certificate":
|
||||
services.AddProxyWithCert(proxyEndpoint, config["Connector:Ticket:ProxyCertThumbprint"]);
|
||||
return;
|
||||
case "VaultCertificate":
|
||||
services.AddProxyWithCertFromKeyVault(
|
||||
proxyEndpoint,
|
||||
new KeyVaultConfiguration(
|
||||
config["ClientId"],
|
||||
config["ClientSecret"],
|
||||
config["Connector:Ticket:VaultName"]
|
||||
),
|
||||
config["Connector:Ticket:CertName"]
|
||||
);
|
||||
return;
|
||||
default:
|
||||
services.AddProxyWithoutAuth(proxyEndpoint);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadConnectorFromAssembly(IServiceCollection services, IHostingEnvironment env, IConfigurationRoot config, string ticketConnectorAssemblyPath)
|
||||
{
|
||||
var connectorAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(ticketConnectorAssemblyPath);
|
||||
|
@ -110,6 +140,14 @@ namespace Sia.Gateway.Initialization
|
|||
services.AddDistributedMemoryCache();
|
||||
services.AddSession();
|
||||
services.AddCors();
|
||||
services.AddSockets();
|
||||
services.AddSignalR().AddRedis(redisOptions =>
|
||||
{
|
||||
redisOptions.Options.EndPoints.Add(config["Redis:CacheEndpoint"]);
|
||||
redisOptions.Options.Ssl = true;
|
||||
redisOptions.Options.Password = config["Redis:Password"];
|
||||
});
|
||||
services.AddScoped<HubConnectionBuilder>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,13 +25,12 @@ namespace Sia.Gateway
|
|||
}
|
||||
_configuration = builder.Build();
|
||||
|
||||
_secrets = SecretVaultStartup.Initialize(env, _configuration);
|
||||
env.InitializeApplicationInsights(_configuration);
|
||||
_env = env;
|
||||
}
|
||||
|
||||
private IHostingEnvironment _env { get; }
|
||||
private IConfigurationRoot _configuration { get; }
|
||||
private ISecretVault _secrets { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Cors" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Session" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-alpha1-final" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="1.0.0-alpha1-final" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Redis" Version="1.0.0-alpha1-final" />
|
||||
<PackageReference Include="Microsoft.Azure.KeyVault" Version="2.3.2" />
|
||||
<PackageReference Include="Microsoft.Azure.KeyVault.Core" Version="2.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" />
|
||||
|
@ -21,13 +24,13 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="3.16.1" />
|
||||
<PackageReference Include="Microsoft.Sia.Connectors.Tickets" Version="1.0.5-alpha" />
|
||||
<PackageReference Include="System.Globalization.Extensions" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Sia.Connectors.Tickets\Sia.Connectors.Tickets.csproj" />
|
||||
<ProjectReference Include="..\Sia.Data.Incident\Sia.Data.Incidents.csproj" />
|
||||
<ProjectReference Include="..\Sia.Domain.ApiModels\Sia.Domain.ApiModels.csproj" />
|
||||
<ProjectReference Include="..\Sia.Domain\Sia.Domain.csproj" />
|
||||
|
|
|
@ -15,5 +15,26 @@
|
|||
"Your uri here" //Replace with your frontend uri
|
||||
]
|
||||
},
|
||||
"Redis": {
|
||||
"CacheEndpoint": "yourCache.redis.cache.windows.net:6380",
|
||||
"Password": "YOURPASSWORD"
|
||||
},
|
||||
"Connector": {
|
||||
"Ticket": {
|
||||
//If loading from assembly
|
||||
"Path": "Path to connector DLL",
|
||||
|
||||
//If using proxy
|
||||
"ProxyEndpoint": "endpoint URL",
|
||||
"ProxyAuthType": "Proxy authentication type (Certificate, VaultCertificate, or None)",
|
||||
|
||||
//If using 'Certificate' proxy auth type
|
||||
"ProxyCertThumbprint": "Certificate thumbprint",
|
||||
|
||||
//If using VaultCertificate auth with either assembly or proxy
|
||||
"CertName": "certificate name as stored in KeyVault",
|
||||
"VaultName": "name of KeyVault instance to use when retrieving certificate"
|
||||
}
|
||||
},
|
||||
"ClientSecret": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa" //Replace with your client secret
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
using Microsoft.Azure.KeyVault.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.IdentityModel.Clients.ActiveDirectory;
|
||||
using Sia.Shared.Validation;
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
|
@ -13,35 +15,48 @@ namespace Sia.Shared.Authentication
|
|||
}
|
||||
public class AzureSecretVault : ISecretVault
|
||||
{
|
||||
private readonly string _vault;
|
||||
private readonly string _clientId;
|
||||
private readonly string _secret;
|
||||
private readonly KeyVaultConfiguration _config;
|
||||
private const string _secretsEndpoint = "/secrets/";
|
||||
private const string _keysEndpoint = "/keys/";
|
||||
private const string _certificatesEndpoint = "/certificates/";
|
||||
|
||||
public AzureSecretVault(IConfigurationRoot configuration)
|
||||
public AzureSecretVault(KeyVaultConfiguration configuration)
|
||||
{
|
||||
_clientId = configuration["ClientId"];
|
||||
_secret = configuration["ClientSecret"];
|
||||
_vault = String.Format(secretUriBase, configuration.GetSection("KeyVault")["VaultName"]);
|
||||
_config = ThrowIf.Null(configuration, nameof(configuration));
|
||||
}
|
||||
|
||||
public async Task<string> Get(string secretName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var secret = await GetKeyVaultClient().GetSecretAsync(_vault + secretName).ConfigureAwait(false);
|
||||
var secret = await GetKeyVaultClient().GetSecretAsync(_config.Vault + _secretsEndpoint + secretName).ConfigureAwait(false);
|
||||
return secret.Value;
|
||||
}
|
||||
catch (KeyVaultErrorException)
|
||||
catch (KeyVaultErrorException ex)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<X509Certificate2> GetCertificate(string certificateName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cert = await GetKeyVaultClient()
|
||||
.GetCertificateAsync(_config.Vault, certificateName)
|
||||
.ConfigureAwait(false);
|
||||
return new X509Certificate2(cert.Cer);
|
||||
}
|
||||
catch (KeyVaultErrorException ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetToken(string authority, string resource, string scope)
|
||||
{
|
||||
var authContext = new AuthenticationContext(authority);
|
||||
ClientCredential clientCred = new ClientCredential(_clientId,
|
||||
_secret);
|
||||
ClientCredential clientCred = new ClientCredential(_config.ClientId, _config.ClientSecret);
|
||||
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
|
||||
|
||||
if (result == null)
|
||||
|
@ -50,8 +65,6 @@ namespace Sia.Shared.Authentication
|
|||
return result.AccessToken;
|
||||
}
|
||||
|
||||
private const string secretUriBase = "https://{0}.vault.azure.net/secrets/";
|
||||
|
||||
private KeyVaultClient GetKeyVaultClient() => new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
using Sia.Shared.Validation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public class KeyVaultCertificateRetriever
|
||||
: CertificateRetriever
|
||||
{
|
||||
private readonly X509Certificate2 _certificate;
|
||||
|
||||
public KeyVaultCertificateRetriever(AzureSecretVault certificateVault, string certificateName)
|
||||
{
|
||||
ThrowIf.NullOrWhiteSpace(certificateName, nameof(certificateName));
|
||||
|
||||
var certTask = certificateVault.GetCertificate(certificateName);
|
||||
Task.WaitAll(new Task[] { certTask });
|
||||
if (certTask.IsCompleted)
|
||||
{
|
||||
_certificate = certTask.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public override X509Certificate2 Certificate => _certificate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.Extensions.Configuration;
|
||||
using Sia.Shared.Validation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Sia.Shared.Authentication
|
||||
{
|
||||
public class KeyVaultConfiguration
|
||||
{
|
||||
public KeyVaultConfiguration(string clientId, string clientSecret, string vault)
|
||||
{
|
||||
ClientId = ThrowIf.NullOrWhiteSpace(clientId, nameof(clientId));
|
||||
ClientSecret = ThrowIf.NullOrWhiteSpace(clientSecret, nameof(clientSecret));
|
||||
Vault = String.Format(secretUriBase, ThrowIf.NullOrWhiteSpace(vault, nameof(vault)));
|
||||
}
|
||||
|
||||
private const string secretUriBase = "https://{0}.vault.azure.net";
|
||||
|
||||
public readonly string Vault;
|
||||
public readonly string ClientId;
|
||||
public readonly string ClientSecret;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>1.0.7-alpha</Version>
|
||||
<Version>1.0.8-alpha</Version>
|
||||
<PackageId>Microsoft.Sia.Shared</PackageId>
|
||||
<Authors>pdimit, magpint, jache, chtownes</Authors>
|
||||
<Company>Microsoft</Company>
|
||||
|
|
Загрузка…
Ссылка в новой задаче