diff --git a/Directory.Packages.props b/Directory.Packages.props
index 806b335..29f3f19 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,7 +7,7 @@
-
+
diff --git a/src/WebApp/Pages/Account/ExternalLogin.cshtml.cs b/src/WebApp/Pages/Account/ExternalLogin.cshtml.cs
index 8b33c94..77d73f5 100644
--- a/src/WebApp/Pages/Account/ExternalLogin.cshtml.cs
+++ b/src/WebApp/Pages/Account/ExternalLogin.cshtml.cs
@@ -8,7 +8,6 @@
using MyApp.AppServices.Staff.Dto;
using MyApp.Domain.Identity;
using MyApp.WebApp.Models;
-using MyApp.WebApp.Platform.Logging;
using MyApp.WebApp.Platform.PageModelHelpers;
using MyApp.WebApp.Platform.Settings;
@@ -67,7 +66,9 @@ private async Task SignInAsLocalUser()
var user = await userManager.FindByIdAsync(staffId);
logger.LogInformation("Local user with ID {StaffId} signed in", staffId);
- await signInManager.SignInAsync(user!, false);
+ user!.MostRecentLogin = DateTimeOffset.Now;
+ await userManager.UpdateAsync(user);
+ await signInManager.SignInAsync(user, false);
return LocalRedirectOrHome();
}
@@ -92,19 +93,19 @@ public async Task OnGetCallbackAsync(string? returnUrl = null, st
if (!userEmail.IsValidEmailDomain())
{
- logger.LogWarning("User {UserName} with invalid email domain attempted signin", userEmail.MaskEmail());
+ logger.LogWarning("User with invalid email domain attempted signin");
return RedirectToPage("./Unavailable");
}
- logger.LogInformation("User {UserName} in tenant {TenantID} successfully authenticated", userEmail.MaskEmail(),
- userTenant);
+ logger.LogInformation("User with object ID {ObjectId} in tenant {TenantID} successfully authenticated",
+ externalLoginInfo.Principal.GetObjectId(), userTenant);
// Determine if a user account already exists with the Object ID.
// If not, then determine if a user account already exists with the given username.
var user = AppSettings.DevSettings.UseInMemoryData
? await userManager.FindByNameAsync(userEmail)
- : await userManager.Users.SingleOrDefaultAsync(u =>
- u.ObjectIdentifier == externalLoginInfo.Principal.GetObjectId()) ??
+ : await userManager.Users.SingleOrDefaultAsync(au =>
+ au.ObjectIdentifier == externalLoginInfo.Principal.GetObjectId()) ??
await userManager.FindByNameAsync(userEmail);
// If the user does not have a local account yet, then create one and sign in.
@@ -114,7 +115,7 @@ public async Task OnGetCallbackAsync(string? returnUrl = null, st
// If user has been marked as inactive, don't sign in.
if (!user.Active)
{
- logger.LogWarning("Inactive user {Email} attempted signin", userEmail.MaskEmail());
+ logger.LogWarning("Inactive user with object ID {ObjectId} attempted signin", user.ObjectIdentifier);
return RedirectToPage("./Unavailable");
}
@@ -163,25 +164,24 @@ private async Task CreateUserAndSignInAsync(ExternalLoginInfo inf
var createUserResult = await userManager.CreateAsync(user);
if (!createUserResult.Succeeded)
{
- logger.LogWarning("Failed to create new user {UserName}", user.Email.MaskEmail());
+ logger.LogWarning("Failed to create new user with object ID {ObjectId}", user.ObjectIdentifier);
return await FailedLoginAsync(createUserResult, user);
}
- logger.LogInformation("Created new user {Email} with object ID {ObjectId}", user.Email.MaskEmail(),
- user.ObjectIdentifier);
+ logger.LogInformation("Created new user with object ID {ObjectId}", user.ObjectIdentifier);
// Add new user to application Roles if seeded in app settings or local admin user setting is enabled.
var seedAdminUsers = configuration.GetSection("SeedAdminUsers").Get();
if (AppSettings.DevSettings.LocalUserIsStaff)
{
- logger.LogInformation("Seeding staff role for new user {Email}", user.Email.MaskEmail());
+ logger.LogInformation("Seeding staff role for new user with object ID {ObjectId}", user.ObjectIdentifier);
await userManager.AddToRoleAsync(user, RoleName.Staff);
}
if (AppSettings.DevSettings.LocalUserIsAdmin ||
(seedAdminUsers != null && seedAdminUsers.Contains(user.Email, StringComparer.InvariantCultureIgnoreCase)))
{
- logger.LogInformation("Seeding all roles for new user {Email}", user.Email.MaskEmail());
+ logger.LogInformation("Seeding all roles for new user with object ID {ObjectId}", user.ObjectIdentifier);
foreach (var role in AppRole.AllRoles) await userManager.AddToRoleAsync(user, role.Key);
}
@@ -194,8 +194,8 @@ private async Task CreateUserAndSignInAsync(ExternalLoginInfo inf
// Update local store with from external provider.
private async Task RefreshUserInfoAndSignInAsync(ApplicationUser user, ExternalLoginInfo info)
{
- logger.LogInformation("Existing user {Email} logged in with {LoginProvider} provider",
- user.Email.MaskEmail(), info.LoginProvider);
+ logger.LogInformation("Existing user with object ID {ObjectId} logged in with {LoginProvider} provider",
+ user.ObjectIdentifier, info.LoginProvider);
var previousValues = new ApplicationUser
{
@@ -218,7 +218,6 @@ private async Task RefreshUserInfoAndSignInAsync(ApplicationUser
}
await userManager.UpdateAsync(user);
-
await signInManager.RefreshSignInAsync(user);
return LocalRedirectOrHome();
}
@@ -231,8 +230,8 @@ private async Task AddLoginProviderAndSignInAsync(
if (!addLoginResult.Succeeded)
{
- logger.LogWarning("Failed to add login provider {LoginProvider} for user {Email}",
- info.LoginProvider, user.Email.MaskEmail());
+ logger.LogWarning("Failed to add login provider {LoginProvider} for user with object ID {ObjectId}",
+ info.LoginProvider, user.ObjectIdentifier);
return await FailedLoginAsync(addLoginResult, user);
}
@@ -240,8 +239,8 @@ private async Task AddLoginProviderAndSignInAsync(
user.MostRecentLogin = DateTimeOffset.Now;
await userManager.UpdateAsync(user);
- logger.LogInformation("Login provider {LoginProvider} added for user {Email} with object ID {ObjectId}",
- info.LoginProvider, user.Email.MaskEmail(), user.ObjectIdentifier);
+ logger.LogInformation("Login provider {LoginProvider} added for user with object ID {ObjectId}",
+ info.LoginProvider, user.ObjectIdentifier);
// Include the access token in the properties.
var props = new AuthenticationProperties();
@@ -262,9 +261,6 @@ private async Task FailedLoginAsync(IdentityResult result, Applicati
return Page();
}
- private IActionResult LocalRedirectOrHome()
- {
- if (ReturnUrl is null) return RedirectToPage("/Staff/Index");
- return LocalRedirect(ReturnUrl);
- }
+ private IActionResult LocalRedirectOrHome() =>
+ ReturnUrl is null ? RedirectToPage("/Staff/Index") : LocalRedirect(ReturnUrl);
}
diff --git a/src/WebApp/Platform/AppConfiguration/MigratorHostedService.cs b/src/WebApp/Platform/AppConfiguration/MigratorHostedService.cs
index 7b4a200..1c6f8be 100644
--- a/src/WebApp/Platform/AppConfiguration/MigratorHostedService.cs
+++ b/src/WebApp/Platform/AppConfiguration/MigratorHostedService.cs
@@ -17,10 +17,16 @@ public async Task StartAsync(CancellationToken cancellationToken)
if (AppSettings.DevSettings.UseInMemoryData) return;
var migrationConnectionString = configuration.GetConnectionString("MigrationConnection");
- var migrationOptions = new DbContextOptionsBuilder()
- .UseSqlServer(migrationConnectionString, builder => builder.MigrationsAssembly("EfRepository")).Options;
+ var dbContextOptionsBuilder = new DbContextOptionsBuilder()
+ .UseSqlServer(migrationConnectionString, builder => builder.MigrationsAssembly("EfRepository"));
- await using var migrationContext = new AppDbContext(migrationOptions);
+ if (AppSettings.DevSettings.UseDevSettings)
+ {
+ dbContextOptionsBuilder
+ .LogTo(Console.WriteLine, [DbLoggerCategory.Database.Command.Name], LogLevel.Information);
+ }
+
+ await using var migrationContext = new AppDbContext(dbContextOptionsBuilder.Options);
if (AppSettings.DevSettings.UseEfMigrations)
{
@@ -31,8 +37,7 @@ public async Task StartAsync(CancellationToken cancellationToken)
var roleManager = scope.ServiceProvider.GetRequiredService>();
foreach (var role in AppRole.AllRoles.Keys)
{
- if (!await migrationContext.Roles.AnyAsync(identityRole => identityRole.Name == role,
- cancellationToken))
+ if (!await migrationContext.Roles.AnyAsync(idRole => idRole.Name == role, cancellationToken))
{
await roleManager.CreateAsync(new IdentityRole(role));
}
diff --git a/src/WebApp/Platform/Logging/Redaction.cs b/src/WebApp/Platform/Logging/Redaction.cs
deleted file mode 100644
index 567e83f..0000000
--- a/src/WebApp/Platform/Logging/Redaction.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Text.RegularExpressions;
-
-namespace MyApp.WebApp.Platform.Logging;
-
-public static partial class Redaction
-{
- public static string MaskEmail(this string? email)
- {
- if (string.IsNullOrEmpty(email)) return string.Empty;
-
- var atIndex = email.IndexOf('@');
- if (atIndex <= 1) return email;
-
- var maskedEmail = MyRegex().Replace(email[..atIndex], "*");
- return string.Concat(maskedEmail, email.AsSpan(atIndex));
- }
-
- [GeneratedRegex(".(?=.{2})")]
- private static partial Regex MyRegex();
-}
diff --git a/tests/EfRepositoryTests/RepositoryHelper.cs b/tests/EfRepositoryTests/RepositoryHelper.cs
index 5676dd3..e5d3ada 100644
--- a/tests/EfRepositoryTests/RepositoryHelper.cs
+++ b/tests/EfRepositoryTests/RepositoryHelper.cs
@@ -1,5 +1,6 @@
using GaEpd.AppLibrary.Domain.Entities;
using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using MyApp.Domain.Entities.EntryTypes;
using MyApp.Domain.Entities.Offices;
using MyApp.Domain.Entities.WorkEntries;
@@ -26,7 +27,7 @@ namespace EfRepositoryTests;
///
public sealed class RepositoryHelper : IDisposable, IAsyncDisposable
{
- private AppDbContext Context { get; set; } = default!;
+ private AppDbContext Context { get; set; } = null!;
private readonly DbContextOptions _options;
private readonly AppDbContext _context;
@@ -36,7 +37,8 @@ public sealed class RepositoryHelper : IDisposable, IAsyncDisposable
///
private RepositoryHelper()
{
- _options = SqliteInMemory.CreateOptions();
+ _options = SqliteInMemory.CreateOptions(builder =>
+ builder.LogTo(Console.WriteLine, events: [RelationalEventId.CommandExecuted]));
_context = new AppDbContext(_options);
_context.Database.EnsureCreated();
}
@@ -49,7 +51,8 @@ private RepositoryHelper()
private RepositoryHelper(object callingClass, string callingMember)
{
_options = callingClass.CreateUniqueMethodOptions(callingMember: callingMember,
- builder: opts => opts.UseSqlServer());
+ builder: builder => builder.UseSqlServer()
+ .LogTo(Console.WriteLine, events: [RelationalEventId.CommandExecuted]));
_context = new AppDbContext(_options);
_context.Database.EnsureClean();
}