Skip to content

Commit

Permalink
Merge
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Mar 25, 2024
1 parent bc132bd commit 8badf94
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Text.Encodings.Web;
using Aspire.Dashboard.Authentication.OtlpApiKey;
using Aspire.Dashboard.Authentication.OtlpConnection;
using Aspire.Dashboard.Configuration;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.Extensions.Options;
Expand All @@ -20,6 +21,8 @@ public sealed class OtlpCompositeAuthenticationHandler(
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var options = dashboardOptions.CurrentValue;

foreach (var scheme in GetRelevantAuthenticationSchemes())
{
var result = await Context.AuthenticateAsync(scheme).ConfigureAwait(false);
Expand All @@ -38,11 +41,11 @@ IEnumerable<string> GetRelevantAuthenticationSchemes()
{
yield return OtlpConnectionAuthenticationDefaults.AuthenticationScheme;

if (Options.OtlpAuthMode is OtlpAuthMode.ApiKey)
if (options.Otlp.AuthMode is OtlpAuthMode.ApiKey)
{
yield return OtlpApiKeyAuthenticationDefaults.AuthenticationScheme;
}
else if (Options.OtlpAuthMode is OtlpAuthMode.ClientCertificate)
else if (options.Otlp.AuthMode is OtlpAuthMode.ClientCertificate)
{
yield return CertificateAuthenticationDefaults.AuthenticationScheme;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Aspire.Dashboard.Configuration;

public enum DashboardClientCertificateSource
{
File,
KeyStore
}
37 changes: 37 additions & 0 deletions src/Aspire.Dashboard/Configuration/DashboardOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,44 @@ namespace Aspire.Dashboard.Configuration;

public sealed class DashboardOptions
{
public string? ApplicationName { get; set; }
public OtlpOptions Otlp { get; set; } = new OtlpOptions();
public FrontendOptions Frontend { get; set; } = new FrontendOptions();
public ResourceServiceClientOptions ResourceServiceClient { get; set; } = new ResourceServiceClientOptions();
}

public sealed class ResourceServiceClientOptions
{
private Uri? _parsedUrl;

public string? Url { get; set; }
public ResourceClientAuthMode? AuthMode { get; set; }
public ResourceServiceClientCertificateOptions ClientCertificates { get; set; } = new ResourceServiceClientCertificateOptions();

public Uri? GetUri() => _parsedUrl;

internal bool TryParseOptions([NotNullWhen(false)] out string? errorMessage)
{
if (!string.IsNullOrEmpty(Url))
{
if (!Uri.TryCreate(Url, UriKind.Absolute, out _parsedUrl))
{
errorMessage = $"Failed to parse resource service client endpoint URL '{Url}'.";
return false;
}
}

errorMessage = null;
return true;
}
}

public sealed class ResourceServiceClientCertificateOptions
{
public DashboardClientCertificateSource? Source { get; set; }
public string? FilePath { get; set; }
public string? Password { get; set; }
public string? Subject { get; set; }
}

public sealed class OtlpOptions
Expand Down Expand Up @@ -53,6 +89,7 @@ public sealed class FrontendOptions
private List<Uri>? _parsedEndpointUrls;

public string? EndpointUrls { get; set; }
public FrontendAuthMode? AuthMode { get; set; }

public IReadOnlyList<Uri> GetEndpointUris()
{
Expand Down
10 changes: 10 additions & 0 deletions src/Aspire.Dashboard/Configuration/FrontendAuthMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Aspire.Dashboard.Configuration;

public enum FrontendAuthMode
{
Unsecured,
OpenIdConnect
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public void PostConfigure(string? name, DashboardOptions options)
}
if (_configuration.GetBool(DashboardConfigNames.DashboardInsecureAllowAnonymousName.ConfigKey) ?? false)
{
options.Otlp.AuthMode = OtlpAuthMode.None;
options.Frontend.AuthMode = FrontendAuthMode.Unsecured;
options.Otlp.AuthMode = OtlpAuthMode.Unsecured;
}
}
}
10 changes: 10 additions & 0 deletions src/Aspire.Dashboard/Configuration/ResourceClientAuthMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Aspire.Dashboard.Configuration;

public enum ResourceClientAuthMode
{
Unsecured,
Certificate
}
40 changes: 39 additions & 1 deletion src/Aspire.Dashboard/Configuration/ValidateDashboardOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ public ValidateOptionsResult Validate(string? name, DashboardOptions options)
errorMessages.Add(otlpParseErrorMessage);
}

if (!options.ResourceServiceClient.TryParseOptions(out var resourceServiceClientParseErrorMessage))
{
errorMessages.Add(resourceServiceClientParseErrorMessage);
}

switch (options.Otlp.AuthMode)
{
case OtlpAuthMode.None:
case OtlpAuthMode.Unsecured:
break;
case OtlpAuthMode.ApiKey:
if (string.IsNullOrEmpty(options.Otlp.PrimaryApiKey))
Expand All @@ -41,6 +46,39 @@ public ValidateOptionsResult Validate(string? name, DashboardOptions options)
break;
}

if (options.ResourceServiceClient.GetUri() != null)
{
switch (options.ResourceServiceClient.AuthMode)
{
case ResourceClientAuthMode.Unsecured:
break;
case ResourceClientAuthMode.Certificate:

switch (options.ResourceServiceClient.ClientCertificates.Source)
{
case DashboardClientCertificateSource.File:
if (string.IsNullOrEmpty(options.ResourceServiceClient.ClientCertificates.FilePath))
{
errorMessages.Add("Dashboard:ResourceServiceClient:ClientCertificate:Source is \"File\", but no Dashboard:ResourceServiceClient:ClientCertificate:FilePath is configured.");
}
break;
case DashboardClientCertificateSource.KeyStore:
if (string.IsNullOrEmpty(options.ResourceServiceClient.ClientCertificates.Subject))
{
errorMessages.Add("Dashboard:ResourceServiceClient:ClientCertificate:Source is \"KeyStore\", but no Dashboard:ResourceServiceClient:ClientCertificate:Subject is configured.");
}
break;
default:
errorMessages.Add($"Unexpected resource service client certificate source: {options.Otlp.AuthMode}");
break;
}
break;
default:
errorMessages.Add($"Unexpected resource service client authentication mode: {options.Otlp.AuthMode}");
break;
}
}

return errorMessages.Count > 0
? ValidateOptionsResult.Fail(errorMessages)
: ValidateOptionsResult.Success;
Expand Down
22 changes: 6 additions & 16 deletions src/Aspire.Dashboard/DashboardWebApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public DashboardWebApplication(Action<WebApplicationBuilder>? configureBuilder =
builder.Services.Configure<HttpsRedirectionOptions>(options => options.HttpsPort = browserHttpsPort);
}

ConfigureAuthentication(builder);
ConfigureAuthentication(builder, dashboardOptions);

// Add services to the container.
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
Expand Down Expand Up @@ -359,7 +359,7 @@ static Func<EndpointInfo> CreateEndPointAccessor(ListenOptions options, bool isH
}
}

private static void ConfigureAuthentication(WebApplicationBuilder builder)
private static void ConfigureAuthentication(WebApplicationBuilder builder, DashboardOptions dashboardOptions)
{
var authentication = builder.Services
.AddAuthentication()
Expand All @@ -369,7 +369,7 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder)
.AddCertificate(options =>
{
// Bind options to configuration so they can be overridden by environment variables.
builder.Configuration.Bind("CertificateAuthentication", options);
builder.Configuration.Bind("Dashboard:Otlp:CertificateAuthOptions", options);
options.Events = new CertificateAuthenticationEvents
{
Expand All @@ -393,7 +393,7 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder)
};
});

if (dashboardStartupConfig.FrontendAuthMode == FrontendAuthMode.OpenIdConnect)
if (dashboardOptions.Frontend.AuthMode == FrontendAuthMode.OpenIdConnect)
{
authentication.AddPolicyScheme(FrontendAuthenticationDefaults.AuthenticationScheme, displayName: FrontendAuthenticationDefaults.AuthenticationScheme, o =>
{
Expand Down Expand Up @@ -440,7 +440,7 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder)
.RequireClaim(OtlpAuthorization.OtlpClaimName)
.Build());
if (dashboardStartupConfig.FrontendAuthMode == FrontendAuthMode.OpenIdConnect)
if (dashboardOptions.Frontend.AuthMode == FrontendAuthMode.OpenIdConnect)
{
// Frontend is secured with OIDC, so delegate to that authentication scheme.
options.AddPolicy(
Expand All @@ -450,7 +450,7 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder)
.RequireAuthenticatedUser()
.Build());
}
else if (dashboardStartupConfig.FrontendAuthMode == FrontendAuthMode.Unsecured)
else
{
// Frontend is unsecured so our policy doesn't need any special handling.
options.AddPolicy(
Expand All @@ -459,10 +459,6 @@ private static void ConfigureAuthentication(WebApplicationBuilder builder)
.RequireAssertion(_ => true)
.Build());
}
else
{
throw new NotSupportedException($"Unexpected {nameof(FrontendAuthMode)} enum member.");
}
});
}

Expand All @@ -479,12 +475,6 @@ public ValueTask DisposeAsync()

private static bool IsHttps(Uri uri) => string.Equals(uri.Scheme, "https", StringComparison.Ordinal);

private enum FrontendAuthMode
{
Unsecured,
OpenIdConnect
}

public static class FrontendAuthenticationDefaults
{
public const string AuthenticationScheme = "Frontend";
Expand Down
Loading

0 comments on commit 8badf94

Please sign in to comment.