Skip to content

Commit

Permalink
Validate token
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Mar 28, 2024
1 parent 4a6d32a commit 22161aa
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 8 deletions.
3 changes: 2 additions & 1 deletion src/Aspire.Dashboard/Components/Pages/Token.razor
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<PageTitle><ApplicationName ResourceName="@nameof(Dashboard.Resources.Token.PageTitle)" Loc="@Loc" /></PageTitle>

<div class="token-backdrop">
<EditForm EditContext="@EditContext" OnValidSubmit="@GetToken">
<EditForm EditContext="@EditContext" OnValidSubmit="@SubmitAsync">
<DataAnnotationsValidator />
<div class="token-form-container">
<div class="token-logo">
Expand Down Expand Up @@ -39,6 +39,7 @@

<div class="token-validation">
<ValidationMessage For="() => _formModel.Token" />
<div class="validation-message">@_validationFailedMessage</div>
</div>
</div>
</EditForm>
Expand Down
11 changes: 10 additions & 1 deletion src/Aspire.Dashboard/Components/Pages/Token.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public partial class Token : IAsyncDisposable
{
private IJSObjectReference? _jsModule;
private FluentTextField? _tokenTextField;
private string? _validationFailedMessage;

private TokenFormModel _formModel = default!;
public EditContext EditContext { get; private set; } = default!;
Expand Down Expand Up @@ -46,6 +47,10 @@ protected override async Task OnInitializedAsync()

_formModel = new TokenFormModel();
EditContext = new EditContext(_formModel);
EditContext.OnValidationStateChanged += (s, e) =>
{
_validationFailedMessage = null;
};
}

protected override async Task OnAfterRenderAsync(bool firstRender)
Expand All @@ -58,7 +63,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
}
}

private async Task GetToken()
private async Task SubmitAsync()
{
if (_jsModule is null)
{
Expand All @@ -72,6 +77,10 @@ private async Task GetToken()
NavigationManager.NavigateTo(ReturnUrl ?? "/", forceLoad: true);
return;
}
else
{
_validationFailedMessage = "Invalid token. Please try again.";
}
}

public async ValueTask DisposeAsync()
Expand Down
1 change: 1 addition & 0 deletions src/Aspire.Dashboard/Configuration/DashboardOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public sealed class FrontendOptions

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

public IReadOnlyList<Uri> GetEndpointUris()
{
Expand Down
30 changes: 25 additions & 5 deletions src/Aspire.Dashboard/Configuration/ValidateDashboardOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,29 @@ public ValidateOptionsResult Validate(string? name, DashboardOptions options)
errorMessages.Add(frontendParseErrorMessage);
}

if (!options.Otlp.TryParseOptions(out var otlpParseErrorMessage))
switch (options.Frontend.AuthMode)
{
errorMessages.Add(otlpParseErrorMessage);
case FrontendAuthMode.Unsecured:
break;
case FrontendAuthMode.OpenIdConnect:
break;
case FrontendAuthMode.BrowserToken:
if (string.IsNullOrEmpty(options.Frontend.BrowserToken))
{
errorMessages.Add($"BrowserToken is required when frontend authentication mode is browser token. Specify a {DashboardConfigNames.DashboardFrontendBrowserTokenName.ConfigKey} value.");
}
break;
case null:
errorMessages.Add($"Frontend endpoint authentication is not configured. Either specify {DashboardConfigNames.DashboardUnsecuredAllowAnonymousName.ConfigKey}=true, or specify {DashboardConfigNames.DashboardFrontendAuthModeName.ConfigKey}. Possible values: {string.Join(", ", typeof(FrontendAuthMode).GetEnumNames())}");
break;
default:
errorMessages.Add($"Unexpected frontend authentication mode: {options.Otlp.AuthMode}");
break;
}

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

switch (options.Otlp.AuthMode)
Expand All @@ -40,13 +55,18 @@ public ValidateOptionsResult Validate(string? name, DashboardOptions options)
case OtlpAuthMode.ClientCertificate:
break;
case null:
errorMessages.Add($"OTLP endpoint authentication is not configured. Either specify {DashboardConfigNames.DashboardUnsecuredAllowAnonymousName.ConfigKey} with a value of true, or specify ${DashboardConfigNames.DashboardOtlpAuthModeName.ConfigKey}. Possible values: {string.Join(", ", typeof(OtlpAuthMode).GetEnumNames())}");
errorMessages.Add($"OTLP endpoint authentication is not configured. Either specify {DashboardConfigNames.DashboardUnsecuredAllowAnonymousName.ConfigKey}=true, or specify {DashboardConfigNames.DashboardOtlpAuthModeName.ConfigKey}. Possible values: {string.Join(", ", typeof(OtlpAuthMode).GetEnumNames())}");
break;
default:
errorMessages.Add($"Unexpected OTLP authentication mode: {options.Otlp.AuthMode}");
break;
}

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

if (options.ResourceServiceClient.GetUri() != null)
{
switch (options.ResourceServiceClient.AuthMode)
Expand Down
7 changes: 6 additions & 1 deletion src/Aspire.Dashboard/DashboardWebApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,13 @@ public DashboardWebApplication(Action<WebApplicationBuilder>? configureBuilder =
_app.MapGrpcService<OtlpTraceService>();
_app.MapGrpcService<OtlpLogsService>();

_app.MapGet("/validate-token", async (string token, HttpContext httpContext) =>
_app.MapGet("/validate-token", async (string token, HttpContext httpContext, IOptionsMonitor<DashboardOptions> dashboardOptions) =>
{
if (string.IsNullOrEmpty(token) || token != dashboardOptions.CurrentValue.Frontend.BrowserToken)
{
return false;
}

var claimsIdentity = new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, "Local")
Expand Down
1 change: 1 addition & 0 deletions src/Aspire.Hosting/Dcp/ApplicationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,7 @@ private async Task<List<KeyValuePair<string, string>>> GetDashboardEnvironmentVa
KeyValuePair.Create(DashboardConfigNames.DashboardOtlpUrlName.EnvVarName, otlpEndpointUrl),
KeyValuePair.Create(DashboardConfigNames.ResourceServiceAuthModeName.EnvVarName, "Unsecured"),
KeyValuePair.Create(DashboardConfigNames.DashboardFrontendAuthModeName.EnvVarName, "BrowserToken"),
KeyValuePair.Create(DashboardConfigNames.DashboardFrontendBrowserTokenName.EnvVarName, "abc"),
};

if (configuration["AppHost:OtlpApiKey"] is { } otlpApiKey)
Expand Down
1 change: 1 addition & 0 deletions src/Shared/DashboardConfigNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal static class DashboardConfigNames
public static readonly ConfigName DashboardOtlpPrimaryApiKeyName = new("Dashboard:Otlp:PrimaryApiKey", "DASHBOARD__OTLP__PRIMARYAPIKEY");
public static readonly ConfigName DashboardOtlpSecondaryApiKeyName = new("Dashboard:Otlp:SecondaryApiKey", "DASHBOARD__OTLP__SECONDARYAPIKEY");
public static readonly ConfigName DashboardFrontendAuthModeName = new("Dashboard:Frontend:AuthMode", "DASHBOARD__FRONTEND__AUTHMODE");
public static readonly ConfigName DashboardFrontendBrowserTokenName = new("Dashboard:Frontend:BrowserToken", "DASHBOARD__FRONTEND__BROWSERTOKEN");
public static readonly ConfigName ResourceServiceAuthModeName = new("Dashboard:ResourceServiceClient:AuthMode", "DASHBOARD__RESOURCESERVICECLIENT__AUTHMODE");
}

Expand Down

0 comments on commit 22161aa

Please sign in to comment.