public async Task ConfigureSecretStore_WithDuplicateNames_MakesSubsetOfDuplicateSecretProviderNames() { // Arrange var name = $"duplicate-name-{Guid.NewGuid()}"; string secretName1 = "MySecret-1", secretName2 = "My-Secret2", secretName3 = "My-Secret3", secretName4 = $"My-Secret4"; string secretValue1 = $"secret-{Guid.NewGuid()}", secretValue2 = $"secret-{Guid.NewGuid()}", secretValue3 = $"secret-{Guid.NewGuid()}", secretValue4 = $"secret-{Guid.NewGuid()}"; var builder = new HostBuilder(); // Act builder.ConfigureSecretStore((config, stores) => { stores.AddProvider(new InMemorySecretProvider((secretName1, secretValue1)), options => options.Name = name) .AddProvider(new InMemorySecretProvider((secretName3, secretValue3)), options => options.Name = "some other name") .AddProvider(new InMemoryCachedSecretProvider((secretName2, secretValue2)), options => options.Name = name) .AddProvider(new InMemorySecretProvider((secretName4, secretValue4))); }); // Assert using (IHost host = builder.Build()) { var store = host.Services.GetRequiredService <ISecretStore>(); ISecretProvider provider = store.GetProvider(name); Assert.IsNotType <InMemoryCachedSecretProvider>(provider); Assert.Equal(secretValue1, await provider.GetRawSecretAsync(secretName1)); Assert.Equal(secretValue2, await provider.GetRawSecretAsync(secretName2)); await Assert.ThrowsAsync <SecretNotFoundException>(() => provider.GetRawSecretAsync(secretName3)); await Assert.ThrowsAsync <SecretNotFoundException>(() => provider.GetRawSecretAsync(secretName4)); } }
private async Task <string> GetAuthorizationSecretAsync(AuthorizationFilterContext context) { ISecretProvider userDefinedSecretProvider = context.HttpContext.RequestServices.GetService <ICachedSecretProvider>() ?? context.HttpContext.RequestServices.GetService <ISecretProvider>(); if (userDefinedSecretProvider is null) { throw new KeyNotFoundException( $"No configured {nameof(ICachedSecretProvider)} or {nameof(ISecretProvider)} implementation found in the request service container. " + "Please configure such an implementation (ex. in the Startup) of your application"); } Task <string> rawSecretAsync = userDefinedSecretProvider.GetRawSecretAsync(_secretName); if (rawSecretAsync is null) { throw new InvalidOperationException( $"Configured {nameof(ISecretProvider)} is not implemented correctly as it returns 'null' for a {nameof(Task)} value when calling {nameof(ISecretProvider.GetRawSecretAsync)}"); } string foundSecret = await rawSecretAsync; if (foundSecret is null) { throw new SecretNotFoundException(_secretName); } return(foundSecret); }
/// <summary> /// Authenticates with to the previously specified Azure Service Bus resource. /// </summary> /// <returns> /// An <see cref="IServiceBusManagementClient"/> instance that manages the previously specified Azure Service Bus resource. /// </returns> /// <exception cref="AuthenticationException">Thrown when the previously configured <see cref="ISecretProvider"/> isn't returning the client secret.</exception> public async Task <IServiceBusManagementClient> AuthenticateAsync() { Task <string> rawSecretAsync = _secretProvider.GetRawSecretAsync(_clientSecretKey); if (rawSecretAsync is null) { throw new AuthenticationException( $"Could not authenticate with the Azure Service Bus because the '{nameof(ISecretProvider)}' that should have returned the client secret, returned 'null'"); } string clientSecret = await rawSecretAsync; var context = new AuthenticationContext($"https://login.microsoftonline.com/{_tenantId}"); var clientCredentials = new ClientCredential(_clientId, clientSecret); AuthenticationResult result = await context.AcquireTokenAsync( "https://management.azure.com/", clientCredentials); var tokenCredentials = new TokenCredentials(result.AccessToken); var client = new ServiceBusManagementClient(tokenCredentials) { SubscriptionId = _subscriptionId }; return(client); }
public async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest request, ILogger log) { string secretValue = await _secretProvider.GetRawSecretAsync("ArcusTestSecret"); return(new OkObjectResult(secretValue)); }
private async Task <CloudStorageAccount> GetAccount() { if (_storageAccount == null) { var storageConnectionString = await _secretProvider.GetRawSecretAsync("HTC-Storage-Connectionstring"); _storageAccount = CloudStorageAccount.Parse(storageConnectionString); } return(_storageAccount); }
/// <summary> /// Retrieves the secret value, based on the given name /// </summary> /// <param name="secretName">The name of the secret key</param> /// <returns>Returns the secret key.</returns> /// <exception cref="ArgumentException">The <paramref name="secretName"/> must not be empty</exception> /// <exception cref="ArgumentNullException">The <paramref name="secretName"/> must not be null</exception> /// <exception cref="SecretNotFoundException">The secret was not found, using the given name</exception> public async Task <string> GetRawSecretAsync(string secretName) { Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name when mutating secret names"); string secretValue = await SafeguardMutateSecretAsync(secretName, mutatedSecretName => { return(_implementation.GetRawSecretAsync(mutatedSecretName)); }); return(secretValue); }
private async Task <BlobServiceClient> GetStorageClient() { if (_blobServiceClient == null) { var storageConnectionString = await _secretProvider.GetRawSecretAsync("HTC-Storage-Connectionstring"); _blobServiceClient = new BlobServiceClient(storageConnectionString); } return(_blobServiceClient); }
public async Task <IEnumerable <WeatherForecast> > Get() { string secret = await _secretProvider.GetRawSecretAsync("Weather-API-Key"); var rng = new Random(); return(Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray()); }
/// <summary> /// Uses an access token to authenticate with Azure. /// </summary> /// <param name="subscriptionId">The ID that identifies the subscription on Azure.</param> /// <param name="accessTokenSecretName">The secret key to use to fetch access token from the secret provider. This will be used to call the Azure management API.</param> /// <param name="secretProvider">The provider to get the client secret; using the <paramref name="accessTokenSecretName"/>.</param> /// <exception cref="ArgumentException">Thrown when the <paramref name="subscriptionId"/> or <paramref name="accessTokenSecretName"/> is blank.</exception> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="secretProvider"/> is blank.</exception> public static LogicAppAuthentication UsingAccessToken(string subscriptionId, string accessTokenSecretName, ISecretProvider secretProvider) { Guard.NotNullOrWhitespace(subscriptionId, nameof(subscriptionId), "Requires an ID that identifies the Azure subscription that has access to the Azure resources"); Guard.NotNullOrWhitespace(accessTokenSecretName, nameof(accessTokenSecretName), "Requires an access token secret key that points to the access token of the client or application that is authorized to interact with the Logic Apps running on Azure"); Guard.NotNull(secretProvider, nameof(secretProvider), "Requires an secret provider instance to retrieve the access token of the client or application that is authorized to interact with the Logic Apps running on Azure"); return(new LogicAppAuthentication(async() => { string accessToken = await secretProvider.GetRawSecretAsync(accessTokenSecretName); LogicManagementClient managementClient = AuthenticateLogicAppsManagement(subscriptionId, accessToken); return managementClient; })); }
/// <summary> /// Attempts to find a value with the given key, returns true if one is found, false otherwise. /// </summary> /// <param name="key">The key to lookup.</param> /// <param name="value">The value found at key if one is found.</param> /// <returns>True if key has a value, false otherwise.</returns> public override bool TryGet(string key, out string value) { Task <string> getSecretValueAsync = _secretProvider.GetRawSecretAsync(key); if (getSecretValueAsync != null) { string secretValue = getSecretValueAsync.ConfigureAwait(false).GetAwaiter().GetResult(); value = secretValue; return(secretValue != null); } value = null; return(false); }
public async Task Run([TimerTrigger("0 */1 * * * *")] TimerInfo timer, ILogger logger) { logger.LogInformation($"C# Timer trigger function executed at: {DateTime.UtcNow}"); var metricName = _configuration.GetValue <string>("Arcus:ApplicationInsights:MetricName"); var baseUrl = _configuration.GetValue <string>("Arcus:Databricks:Url"); string secretToken = await _secretProvider.GetRawSecretAsync("Arcus.Databricks.SecretToken"); var startOfWindow = timer.ScheduleStatus.Last; var endOfWindow = timer.ScheduleStatus.Next; using var client = DatabricksClient.CreateClient(baseUrl, secretToken); using (var provider = new DatabricksInfoProvider(client, logger)) { await provider.MeasureJobOutcomesAsync(metricName, startOfWindow, endOfWindow); } }
/// <summary> /// Uses the service principal to authenticate with Azure. /// </summary> /// <param name="tenantId">The ID where the resources are located on Azure.</param> /// <param name="subscriptionId">The ID that identifies the subscription on Azure.</param> /// <param name="clientId">The ID of the client or application that has access to the logic apps running on Azure.</param> /// <param name="clientSecretName">The secret of the client or application that has access to the logic apps running on Azure.</param> /// <param name="secretProvider">The provider to get the client secret; using the <paramref name="clientSecretName"/>.</param> /// <param name="cloud">The Azure cloud environment to use during authenticating and interacting with the Azure Logic Apps resources.</param> /// <exception cref="ArgumentException">Thrown when the <paramref name="tenantId"/>, <paramref name="subscriptionId"/>, <paramref name="clientId"/>, or <paramref name="clientSecretName"/> is blank.</exception> /// <exception cref="ArgumentNullException">Thrown when the <paramref name="secretProvider"/> is <c>null</c>.</exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="cloud"/> is outside the bounds of the enumeration.</exception> public static LogicAppAuthentication UsingServicePrincipal(string tenantId, string subscriptionId, string clientId, string clientSecretName, ISecretProvider secretProvider, AzureCloud cloud = AzureCloud.Global) { Guard.NotNullOrWhitespace(tenantId, nameof(tenantId), "Requires an tenant ID where the Azure resources are located"); Guard.NotNullOrWhitespace(subscriptionId, nameof(subscriptionId), "Requires an ID that identifies the Azure subscription that has access to the Azure resources"); Guard.NotNullOrWhitespace(clientId, nameof(clientId), "Requires an client or application ID that is authorized to interact with the Logic Apps running on Azure"); Guard.NotNullOrWhitespace(clientSecretName, nameof(clientSecretName), "Requires an client or application secret key that points to the secret of the client or application that is authorized to interact with the Logic Apps running on Azure"); Guard.NotNull(secretProvider, nameof(secretProvider), "Requires an secret provider instance to retrieve the secret of the client or application that is authorized to interact with the Logic Apps running on Azure"); Guard.For(() => !Enum.IsDefined(typeof(AzureCloud), cloud), new ArgumentOutOfRangeException(nameof(cloud), cloud, "Requires the Azure cloud environment to be within the bounds of the enumeration")); return(new LogicAppAuthentication(async() => { string clientSecret = await secretProvider.GetRawSecretAsync(clientSecretName); LogicManagementClient managementClient = await AuthenticateLogicAppsManagementAsync(subscriptionId, tenantId, clientId, clientSecret, cloud); return managementClient; })); }
/// <summary> /// Gets the expected value in a <see cref="X509Certificate2"/> for an <paramref name="configurationKey"/> using the specified <paramref name="services"/>. /// </summary> /// <param name="configurationKey">The configured key for which the expected certificate value is registered.</param> /// <param name="services">The services collections of the HTTP request pipeline to retrieve registered instances.</param> public async Task <string> GetExpectedCertificateValueForConfiguredKeyAsync(string configurationKey, IServiceProvider services) { Guard.NotNullOrWhitespace(configurationKey, nameof(configurationKey), "Configured key cannot be blank"); Guard.NotNull(services, nameof(services), "Registered services cannot be 'null'"); ISecretProvider userDefinedSecretProvider = services.GetService <ICachedSecretProvider>() ?? services.GetService <ISecretProvider>(); if (userDefinedSecretProvider == null) { throw new KeyNotFoundException( $"No configured {nameof(ICachedSecretProvider)} or {nameof(ISecretProvider)} implementation found in the request service container. " + "Please configure such an implementation (ex. in the Startup) of your application"); } Task <string> getValueAsync = userDefinedSecretProvider.GetRawSecretAsync(configurationKey); return(getValueAsync == null ? null : await getValueAsync); }
private async Task <string> GetAuthorizationSecretAsync(AuthorizationFilterContext context) { ISecretProvider userDefinedSecretProvider = context.HttpContext.RequestServices.GetService <ICachedSecretProvider>() ?? context.HttpContext.RequestServices.GetService <ISecretProvider>(); if (userDefinedSecretProvider == null) { throw new KeyNotFoundException( $"No configured {nameof(ICachedSecretProvider)} or {nameof(ISecretProvider)} implementation found in the request service container. " + "Please configure such an implementation (ex. in the Startup) of your application"); } string foundSecret = await userDefinedSecretProvider.GetRawSecretAsync(_secretName); if (foundSecret == null) { throw new SecretNotFoundException(_secretName); } return(foundSecret); }
private async Task <DeviceClient> CreateIoTHubDeviceClient() { var connectionString = await _secretProvider.GetRawSecretAsync($"IOTHUB_CONNECTIONSTRING_DEVICE_{DeviceId}"); return(DeviceClient.CreateFromConnectionString(connectionString, TransportType.Amqp)); }
/// <summary> /// Retrieves the secret value, based on the given name. /// </summary> /// <param name="secretName">The name of the secret key</param> /// <returns>Returns the secret key.</returns> /// <exception cref="T:System.ArgumentException">The <paramref name="secretName" /> must not be empty</exception> /// <exception cref="T:System.ArgumentNullException">The <paramref name="secretName" /> must not be null</exception> /// <exception cref="T:Arcus.Security.Core.SecretNotFoundException">The secret was not found, using the given name</exception> public async Task <string> GetRawSecretAsync(string secretName) { Guard.NotNullOrWhitespace(secretName, nameof(secretName), "Requires a non-blank secret name to access the secret"); return(await WhenAuthorized(() => _secretProvider.GetRawSecretAsync(secretName))); }
private static void ConfigureServices(WebApplicationBuilder builder, IConfiguration configuration) { #if CertificateAuth var certificateAuthenticationConfig = new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.Configuration, "CertificateSubject") .Build(); builder.Services.AddScoped(serviceProvider => new CertificateAuthenticationValidator(certificateAuthenticationConfig)); #endif builder.Services.AddRouting(options => { options.LowercaseUrls = true; options.LowercaseQueryStrings = true; }); builder.Services.AddControllers(options => { options.ReturnHttpNotAcceptable = true; options.RespectBrowserAcceptHeader = true; RestrictToJsonContentType(options); ConfigureJsonFormatters(options); #if SharedAccessKeyAuth #warning Please provide a valid request header name and secret name to the shared access filter options.Filters.Add(new SharedAccessKeyAuthenticationFilter(headerName: SharedAccessKeyHeaderName, queryParameterName: null, secretName: "<your-secret-name>")); #endif #if CertificateAuth options.Filters.Add(new CertificateAuthenticationFilter()); #endif #if JwtAuth AuthorizationPolicy policy = new AuthorizationPolicyBuilder() .RequireRole("Admin") .RequireAuthenticatedUser() .Build(); options.Filters.Add(new AuthorizeFilter(policy)); #endif }); #if JwtAuth #error Use previously registered secret provider, for example Azure Key Vault: https://security.arcus-azure.net/features/secrets/consume-from-key-vault ISecretProvider secretProvider = null; builder.Services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(x => { string key = secretProvider.GetRawSecretAsync("JwtSigningKey").GetAwaiter().GetResult(); x.SaveToken = true; x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)), ValidateIssuer = true, ValidIssuer = configuration.GetValue <string>("Jwt:Issuer"), ValidateAudience = true, ValidAudience = configuration.GetValue <string>("Jwt:Audience") }; }); #endif builder.Services.AddHealthChecks(); #if (ExcludeCorrelation == false) builder.Services.AddHttpCorrelation(); #endif #if (ExcludeOpenApi == false) #warning Be careful of exposing sensitive information with the OpenAPI document, only expose what's necessary and hide everything else. var openApiInformation = new OpenApiInfo { Title = "Arcus.Templates.WebApi", Version = "v1" }; builder.Services.AddSwaggerGen(swaggerGenerationOptions => { swaggerGenerationOptions.SwaggerDoc("v1", openApiInformation); swaggerGenerationOptions.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Arcus.Templates.WebApi.Open-Api.xml")); swaggerGenerationOptions.ExampleFilters(); #if (ExcludeCorrelation == false) swaggerGenerationOptions.OperationFilter <AddHeaderOperationFilter>("X-Transaction-Id", "Transaction ID is used to correlate multiple operation calls. A new transaction ID will be generated if not specified.", false); swaggerGenerationOptions.OperationFilter <AddResponseHeadersFilter>(); #endif #if SharedAccessKeyAuth swaggerGenerationOptions.AddSecurityDefinition("shared-access-key", new OpenApiSecurityScheme { Type = SecuritySchemeType.ApiKey, In = ParameterLocation.Header, Name = SharedAccessKeyHeaderName, Description = "Authentication scheme based on shared access key" }); swaggerGenerationOptions.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Description = "Globally authentication scheme based on shared access key", Reference = new OpenApiReference { Id = "shared-access-key", Type = ReferenceType.SecurityScheme } }, new List <string>() } }); #endif #if CertificateAuth swaggerGenerationOptions.AddSecurityDefinition("certificate", new OpenApiSecurityScheme { Type = SecuritySchemeType.ApiKey, In = ParameterLocation.Header, Name = "X-ARR-ClientCert", Description = "Authentication scheme based on client certificate" }); swaggerGenerationOptions.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Description = "Globally authentication scheme based on client certificate", Reference = new OpenApiReference { Id = "certificate", Type = ReferenceType.SecurityScheme } }, new List <string>() } }); #endif #if JwtAuth swaggerGenerationOptions.AddSecurityDefinition("jwt", new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Description = "Authentication scheme based on JWT" }); swaggerGenerationOptions.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Description = "Globally authentication scheme based on JWT", Reference = new OpenApiReference { Id = "jwt", Type = ReferenceType.SecurityScheme } }, new List <string>() } }); #endif }); builder.Services.AddSwaggerExamplesFromAssemblyOf <HealthReportResponseExampleProvider>(); #endif }