diff --git a/src/WebApp/Pages/Error.cshtml b/src/WebApp/Pages/Error.cshtml index c900a7f..cc13ef0 100644 --- a/src/WebApp/Pages/Error.cshtml +++ b/src/WebApp/Pages/Error.cshtml @@ -4,12 +4,5 @@ ViewData["Title"] = "Error"; } -

Error.

-

An error occurred while processing your request.

- -@if (Model.ShowRequestId) -{ -

- Request ID: @Model.RequestId -

-} +

@ViewData["Title"]

+

An error occurred while processing your request.

\ No newline at end of file diff --git a/src/WebApp/Pages/Error.cshtml.cs b/src/WebApp/Pages/Error.cshtml.cs index 8db85cc..b29e79c 100644 --- a/src/WebApp/Pages/Error.cshtml.cs +++ b/src/WebApp/Pages/Error.cshtml.cs @@ -1,14 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using System.Diagnostics; namespace WebApp.Pages; [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] -[IgnoreAntiforgeryToken] -public class ErrorModel : PageModel -{ - public string? RequestId { get; set; } - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - public void OnGet() => RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; -} +public class ErrorModel : PageModel; diff --git a/src/WebApp/Pages/Index.cshtml b/src/WebApp/Pages/Index.cshtml index 8909946..33f1936 100644 --- a/src/WebApp/Pages/Index.cshtml +++ b/src/WebApp/Pages/Index.cshtml @@ -122,13 +122,9 @@
Server Checkup - @if (Model.Version is not null) + @if (!string.IsNullOrEmpty(AppSettings.Version)) { - @: | Version @Model.Version - } - @if (Model.Build is not null) - { - @: (@Model.Build) + @:| Version: @AppSettings.Version }
diff --git a/src/WebApp/Pages/Index.cshtml.cs b/src/WebApp/Pages/Index.cshtml.cs index 73fa24a..2bbb41e 100644 --- a/src/WebApp/Pages/Index.cshtml.cs +++ b/src/WebApp/Pages/Index.cshtml.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using ServerCheckupLibrary; -using System.Reflection; using WebApp.Platform; namespace WebApp.Pages; @@ -14,9 +13,6 @@ public class IndexModel : PageModel public ResultMessage? ExternalServiceCheckMessage { get; private set; } public ResultMessage? DotnetVersionCheckMessage { get; private set; } - public string? Version { get; private set; } - public string? Build { get; private set; } - public IActionResult OnGet() { if (User.Identity is not { IsAuthenticated: true }) @@ -47,14 +43,6 @@ public IActionResult OnGet() DotnetVersionCheckMessage = new ResultMessage(Context.Info, ".NET version checks are disabled."); } - // Get app version. - var entryAssembly = Assembly.GetEntryAssembly(); - var segments = (entryAssembly?.GetCustomAttribute()? - .InformationalVersion ?? entryAssembly?.GetName().Version?.ToString() ?? "").Split('+'); - - Version = segments[0]; - if (segments.Length > 0) Build = segments[1][..Math.Min(7, segments[1].Length)]; - return Page(); } } diff --git a/src/WebApp/Pages/Shared/_Layout.cshtml b/src/WebApp/Pages/Shared/_Layout.cshtml index ac1dbae..fd9bc0a 100644 --- a/src/WebApp/Pages/Shared/_Layout.cshtml +++ b/src/WebApp/Pages/Shared/_Layout.cshtml @@ -7,6 +7,7 @@ +
diff --git a/src/WebApp/Pages/Shared/_MonitoringScriptsPartial.cshtml b/src/WebApp/Pages/Shared/_MonitoringScriptsPartial.cshtml new file mode 100644 index 0000000..cdbf840 --- /dev/null +++ b/src/WebApp/Pages/Shared/_MonitoringScriptsPartial.cshtml @@ -0,0 +1,39 @@ +@using WebApp.Platform +@{ + // DataDog + var ddClientToken = AppSettings.DataDogSettings.ClientToken; + var ddApplicationId = AppSettings.DataDogSettings.ApplicationId; + const string service = "checkup"; + + if (!string.IsNullOrEmpty(ddClientToken) && !string.IsNullOrEmpty(ddApplicationId)) + { + + } +} diff --git a/src/WebApp/Platform/AppSettings.cs b/src/WebApp/Platform/AppSettings.cs index 23b6cab..68781ee 100644 --- a/src/WebApp/Platform/AppSettings.cs +++ b/src/WebApp/Platform/AppSettings.cs @@ -1,9 +1,10 @@ using JetBrains.Annotations; using ServerCheckupLibrary.Checks; +using System.Reflection; namespace WebApp.Platform; -public static class AppSettings +internal static class AppSettings { public static CheckEmailOptions CheckEmailOptions { get; } = new(); public static CheckDatabaseOptions CheckDatabaseOptions { get; } = new(); @@ -12,6 +13,45 @@ public static class AppSettings public static CheckDotnetVersionOptions CheckDotnetVersionOptions { get; } = new(); public static string ServerName { get; set; } = string.Empty; public static DevOptions DevOptions { get; } = new(); + + public static string? Version { get; private set; } + public static string? SimpleVersion => Version?.Split('+')[0]; + + private static string Env { get; } = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "unknown"; + public static string ShortEnv => Env switch { "Production" => "prod", "Staging" => "uat", _ => "dev" }; + public static DataDog DataDogSettings { get; } = new(); + + public record DataDog + { + public string? ClientToken { get; [UsedImplicitly] init; } + public string? ApplicationId { get; [UsedImplicitly] init; } + } + + public static void BindAppSettings(this IHostApplicationBuilder builder) + { + // Set default timeout for regular expressions. + // https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices#use-time-out-values + AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromMilliseconds(100)); + + Version = GetVersion(); + ServerName = builder.Configuration.GetValue(nameof(ServerName)) ?? "Unknown"; + + builder.Configuration.GetSection(nameof(CheckEmailOptions)).Bind(CheckEmailOptions); + builder.Configuration.GetSection(nameof(CheckDatabaseOptions)).Bind(CheckDatabaseOptions); + builder.Configuration.GetSection(nameof(CheckDatabaseEmailOptions)).Bind(CheckDatabaseEmailOptions); + builder.Configuration.GetSection(nameof(CheckExternalServiceOptions)).Bind(CheckExternalServiceOptions); + builder.Configuration.GetSection(nameof(CheckDotnetVersionOptions)).Bind(CheckDotnetVersionOptions); + builder.Configuration.GetSection(nameof(DevOptions)).Bind(DevOptions); + builder.Configuration.GetSection(nameof(DataDogSettings)).Bind(DataDogSettings); + } + + private static string GetVersion() + { + var entryAssembly = Assembly.GetEntryAssembly(); + var segments = (entryAssembly?.GetCustomAttribute()? + .InformationalVersion ?? entryAssembly?.GetName().Version?.ToString() ?? "").Split('+'); + return segments[0] + (segments.Length > 0 ? $"+{segments[1][..Math.Min(7, segments[1].Length)]}" : ""); + } } [UsedImplicitly(ImplicitUseTargetFlags.Members)] diff --git a/src/WebApp/Platform/AuthenticationServices.cs b/src/WebApp/Platform/AuthenticationServices.cs new file mode 100644 index 0000000..7d5f058 --- /dev/null +++ b/src/WebApp/Platform/AuthenticationServices.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.Tokens; + +namespace WebApp.Platform; + +public static class AuthenticationServices +{ + public static void ConfigureAuthentication(this IHostApplicationBuilder builder) + { + if (AppSettings.DevOptions.UseLocalAuth) + { + // When running locally, use a built-in authenticated user. + builder.Services + .AddAuthentication(LocalAuthenticationHandler.BasicAuthenticationScheme) + .AddScheme( + LocalAuthenticationHandler.BasicAuthenticationScheme, null); + } + else + { + // When running on the server, require an OIDC login provider (configured in the app settings file). + builder.Services + .AddAuthentication(options => + { + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddOpenIdConnect(configureOptions: options => + { + var configSection = builder.Configuration.GetSection("OIDC"); + + options.Authority = configSection["Authority"]; + options.ClientId = configSection["ClientId"]; + options.ClientSecret = configSection["ClientSecret"]; + options.CallbackPath = configSection["CallbackPath"]; + + options.Scope.Add("email"); + options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.ResponseType = OpenIdConnectResponseType.Code; + options.MapInboundClaims = false; + options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "email" }; + }); + } + } +} diff --git a/src/WebApp/Program.cs b/src/WebApp/Program.cs index ec6a320..938348b 100644 --- a/src/WebApp/Program.cs +++ b/src/WebApp/Program.cs @@ -1,9 +1,3 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication.OpenIdConnect; -using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using Microsoft.IdentityModel.Tokens; -using ServerCheckupLibrary.Checks; using ServerCheckupLibrary.Hubs; using WebApp.Platform; using ZLogger; @@ -11,57 +5,17 @@ var builder = WebApplication.CreateBuilder(args); var isDevelopment = builder.Environment.IsDevelopment(); -// Configure logging +// Configure logging. builder.Logging.ClearProviders().AddZLoggerConsole(options => options.UseJsonFormatter()); -// Persist data protection keys +// Persist data protection keys. builder.Services.AddDataProtection(); // Bind application settings. -builder.Configuration.GetSection(nameof(CheckEmailOptions)).Bind(AppSettings.CheckEmailOptions); -builder.Configuration.GetSection(nameof(CheckDatabaseOptions)).Bind(AppSettings.CheckDatabaseOptions); -builder.Configuration.GetSection(nameof(CheckDatabaseEmailOptions)).Bind(AppSettings.CheckDatabaseEmailOptions); -builder.Configuration.GetSection(nameof(CheckExternalServiceOptions)).Bind(AppSettings.CheckExternalServiceOptions); -builder.Configuration.GetSection(nameof(CheckDotnetVersionOptions)).Bind(AppSettings.CheckDotnetVersionOptions); -builder.Configuration.GetSection(nameof(DevOptions)).Bind(AppSettings.DevOptions); -AppSettings.ServerName = builder.Configuration.GetValue(nameof(AppSettings.ServerName)) ?? "Unknown"; - -// Configure authentication. -if (AppSettings.DevOptions.UseLocalAuth) -{ - // When running locally, use a built-in authenticated user. - builder.Services - .AddAuthentication(LocalAuthenticationHandler.BasicAuthenticationScheme) - .AddScheme( - LocalAuthenticationHandler.BasicAuthenticationScheme, null); -} -else -{ - // When running on the server, require an OIDC login provider (configured in the appsettings file). - builder.Services - .AddAuthentication(options => - { - options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; - }) - .AddCookie() - .AddOpenIdConnect(configureOptions: options => - { - var configSection = builder.Configuration.GetSection("OIDC"); - - options.Authority = configSection["Authority"]; - options.ClientId = configSection["ClientId"]; - options.ClientSecret = configSection["ClientSecret"]; - options.CallbackPath = configSection["CallbackPath"]; - - options.Scope.Add("email"); - options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; - options.ResponseType = OpenIdConnectResponseType.Code; - options.MapInboundClaims = false; - options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "email" }; - }); -} +builder.BindAppSettings(); +// Configure authentication and authorization. +builder.ConfigureAuthentication(); builder.Services.AddAuthorization(); // Add SignalR diff --git a/src/WebApp/appsettings-example.json b/src/WebApp/appsettings-example.json index a3c51d8..2818122 100644 --- a/src/WebApp/appsettings-example.json +++ b/src/WebApp/appsettings-example.json @@ -68,8 +68,12 @@ "Instance": "https://login.microsoftonline.com/", "Domain": "qualified.domain.name", "TenantId": "00000000-0000-0000-0000-000000000000", - "ClientId": "00000000-0000-0000-00000000000000000", + "ClientId": "00000000-0000-0000-0000-000000000000", "CallbackPath": "/signin-oidc" }, + "DataDogSettings": { + "ClientToken": "pub0000000000000000000", + "ApplicationId": "00000000-0000-0000-0000-000000000000" + }, "AllowedHosts": "*" } diff --git a/src/WebApp/appsettings.json b/src/WebApp/appsettings.json index 6874e56..d5cc9cd 100644 --- a/src/WebApp/appsettings.json +++ b/src/WebApp/appsettings.json @@ -32,6 +32,10 @@ "ClientId": "", "CallbackPath": "/signin-oidc" }, + "DataDogSettings": { + "ClientToken": "", + "ApplicationId": "" + }, "Logging": { "LogLevel": { "Default": "Information",