From 3ce25305a9f2b96b4e8f82e01c537a95388c704a Mon Sep 17 00:00:00 2001 From: Mia Winter Date: Thu, 11 Jan 2024 15:15:30 +0100 Subject: [PATCH] chore: applied code style --- ...omponentsEndpointRouteBuilderExtensions.cs | 158 +++++++++--------- .../Account/IdentityNoOpEmailSender.cs | 29 ++-- .../Account/IdentityRedirectManager.cs | 92 +++++----- .../Account/IdentityUserAccessor.cs | 25 ++- Wave/Data/ApplicationDbContext.cs | 10 +- Wave/Data/ApplicationUser.cs | 10 +- 6 files changed, 152 insertions(+), 172 deletions(-) diff --git a/Wave/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs b/Wave/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs index aa300d4..8852eef 100644 --- a/Wave/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs +++ b/Wave/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs @@ -10,104 +10,96 @@ using Wave.Components.Account.Pages.Manage; using Wave.Data; -namespace Microsoft.AspNetCore.Routing -{ - internal static class IdentityComponentsEndpointRouteBuilderExtensions - { - // These endpoints are required by the Identity Razor components defined in the /Components/Account/Pages directory of this project. - public static IEndpointConventionBuilder MapAdditionalIdentityEndpoints(this IEndpointRouteBuilder endpoints) - { - ArgumentNullException.ThrowIfNull(endpoints); +namespace Microsoft.AspNetCore.Routing; - var accountGroup = endpoints.MapGroup("/Account"); +internal static class IdentityComponentsEndpointRouteBuilderExtensions { + // These endpoints are required by the Identity Razor components defined in the /Components/Account/Pages directory of this project. + public static IEndpointConventionBuilder MapAdditionalIdentityEndpoints(this IEndpointRouteBuilder endpoints) { + ArgumentNullException.ThrowIfNull(endpoints); - accountGroup.MapPost("/PerformExternalLogin", ( - HttpContext context, - [FromServices] SignInManager signInManager, - [FromForm] string provider, - [FromForm] string returnUrl) => - { - IEnumerable> query = [ - new("ReturnUrl", returnUrl), - new("Action", ExternalLogin.LoginCallbackAction)]; + var accountGroup = endpoints.MapGroup("/Account"); - var redirectUrl = UriHelper.BuildRelative( - context.Request.PathBase, - "/Account/ExternalLogin", - QueryString.Create(query)); + accountGroup.MapPost("/PerformExternalLogin", ( + HttpContext context, + [FromServices] SignInManager signInManager, + [FromForm] string provider, + [FromForm] string returnUrl) => { + IEnumerable> query = [ + new("ReturnUrl", returnUrl), + new("Action", ExternalLogin.LoginCallbackAction) + ]; - var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); - return TypedResults.Challenge(properties, [provider]); - }); + var redirectUrl = UriHelper.BuildRelative( + context.Request.PathBase, + "/Account/ExternalLogin", + QueryString.Create(query)); - accountGroup.MapPost("/Logout", async ( - ClaimsPrincipal user, - SignInManager signInManager, - [FromForm] string returnUrl) => - { - await signInManager.SignOutAsync(); - return TypedResults.LocalRedirect($"~/{returnUrl}"); - }); + var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return TypedResults.Challenge(properties, [provider]); + }); - var manageGroup = accountGroup.MapGroup("/Manage").RequireAuthorization(); + accountGroup.MapPost("/Logout", async ( + ClaimsPrincipal user, + SignInManager signInManager, + [FromForm] string returnUrl) => { + await signInManager.SignOutAsync(); + return TypedResults.LocalRedirect($"~/{returnUrl}"); + }); - manageGroup.MapPost("/LinkExternalLogin", async ( - HttpContext context, - [FromServices] SignInManager signInManager, - [FromForm] string provider) => - { - // Clear the existing external cookie to ensure a clean login process - await context.SignOutAsync(IdentityConstants.ExternalScheme); + var manageGroup = accountGroup.MapGroup("/Manage").RequireAuthorization(); - var redirectUrl = UriHelper.BuildRelative( - context.Request.PathBase, - "/Account/Manage/ExternalLogins", - QueryString.Create("Action", ExternalLogins.LinkLoginCallbackAction)); + manageGroup.MapPost("/LinkExternalLogin", async ( + HttpContext context, + [FromServices] SignInManager signInManager, + [FromForm] string provider) => { + // Clear the existing external cookie to ensure a clean login process + await context.SignOutAsync(IdentityConstants.ExternalScheme); - var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, signInManager.UserManager.GetUserId(context.User)); - return TypedResults.Challenge(properties, [provider]); - }); + var redirectUrl = UriHelper.BuildRelative( + context.Request.PathBase, + "/Account/Manage/ExternalLogins", + QueryString.Create("Action", ExternalLogins.LinkLoginCallbackAction)); - var loggerFactory = endpoints.ServiceProvider.GetRequiredService(); - var downloadLogger = loggerFactory.CreateLogger("DownloadPersonalData"); + var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, + signInManager.UserManager.GetUserId(context.User)); + return TypedResults.Challenge(properties, [provider]); + }); - manageGroup.MapPost("/DownloadPersonalData", async ( - HttpContext context, - [FromServices] UserManager userManager, - [FromServices] AuthenticationStateProvider authenticationStateProvider) => - { - var user = await userManager.GetUserAsync(context.User); - if (user is null) - { - return Results.NotFound($"Unable to load user with ID '{userManager.GetUserId(context.User)}'."); - } + var loggerFactory = endpoints.ServiceProvider.GetRequiredService(); + var downloadLogger = loggerFactory.CreateLogger("DownloadPersonalData"); - var userId = await userManager.GetUserIdAsync(user); - downloadLogger.LogInformation("User with ID '{UserId}' asked for their personal data.", userId); + manageGroup.MapPost("/DownloadPersonalData", async ( + HttpContext context, + [FromServices] UserManager userManager, + [FromServices] AuthenticationStateProvider authenticationStateProvider) => { + var user = await userManager.GetUserAsync(context.User); + if (user is null) { + return Results.NotFound($"Unable to load user with ID '{userManager.GetUserId(context.User)}'."); + } - // Only include personal data for download - var personalData = new Dictionary(); - var personalDataProps = typeof(ApplicationUser).GetProperties().Where( - prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); - foreach (var p in personalDataProps) - { - personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); - } + var userId = await userManager.GetUserIdAsync(user); + downloadLogger.LogInformation("User with ID '{UserId}' asked for their personal data.", userId); - var logins = await userManager.GetLoginsAsync(user); - foreach (var l in logins) - { - personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); - } + // Only include personal data for download + var personalData = new Dictionary(); + var personalDataProps = typeof(ApplicationUser).GetProperties().Where( + prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); + foreach (var p in personalDataProps) { + personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); + } - personalData.Add("Authenticator Key", (await userManager.GetAuthenticatorKeyAsync(user))!); - var fileBytes = JsonSerializer.SerializeToUtf8Bytes(personalData); + var logins = await userManager.GetLoginsAsync(user); + foreach (var l in logins) { + personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); + } - context.Response.Headers.TryAdd("Content-Disposition", "attachment; filename=PersonalData.json"); - return TypedResults.File(fileBytes, contentType: "application/json", fileDownloadName: "PersonalData.json"); - }); + personalData.Add("Authenticator Key", (await userManager.GetAuthenticatorKeyAsync(user))!); + var fileBytes = JsonSerializer.SerializeToUtf8Bytes(personalData); - return accountGroup; - } + context.Response.Headers.TryAdd("Content-Disposition", "attachment; filename=PersonalData.json"); + return TypedResults.File(fileBytes, contentType: "application/json", fileDownloadName: "PersonalData.json"); + }); + + return accountGroup; } -} +} \ No newline at end of file diff --git a/Wave/Components/Account/IdentityNoOpEmailSender.cs b/Wave/Components/Account/IdentityNoOpEmailSender.cs index 6184224..0277733 100644 --- a/Wave/Components/Account/IdentityNoOpEmailSender.cs +++ b/Wave/Components/Account/IdentityNoOpEmailSender.cs @@ -2,20 +2,21 @@ using Microsoft.AspNetCore.Identity.UI.Services; using Wave.Data; -namespace Wave.Components.Account -{ - // Remove the "else if (EmailSender is IdentityNoOpEmailSender)" block from RegisterConfirmation.razor after updating with a real implementation. - internal sealed class IdentityNoOpEmailSender : IEmailSender - { - private readonly IEmailSender emailSender = new NoOpEmailSender(); +namespace Wave.Components.Account; - public Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) => - emailSender.SendEmailAsync(email, "Confirm your email", $"Please confirm your account by clicking here."); +// Remove the "else if (EmailSender is IdentityNoOpEmailSender)" block from RegisterConfirmation.razor after updating with a real implementation. +internal sealed class IdentityNoOpEmailSender : IEmailSender { + private readonly IEmailSender emailSender = new NoOpEmailSender(); - public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) => - emailSender.SendEmailAsync(email, "Reset your password", $"Please reset your password by clicking here."); + public Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) => + emailSender.SendEmailAsync(email, "Confirm your email", + $"Please confirm your account by clicking here."); - public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) => - emailSender.SendEmailAsync(email, "Reset your password", $"Please reset your password using the following code: {resetCode}"); - } -} + public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) => + emailSender.SendEmailAsync(email, "Reset your password", + $"Please reset your password by clicking here."); + + public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) => + emailSender.SendEmailAsync(email, "Reset your password", + $"Please reset your password using the following code: {resetCode}"); +} \ No newline at end of file diff --git a/Wave/Components/Account/IdentityRedirectManager.cs b/Wave/Components/Account/IdentityRedirectManager.cs index b5abc9e..315ff75 100644 --- a/Wave/Components/Account/IdentityRedirectManager.cs +++ b/Wave/Components/Account/IdentityRedirectManager.cs @@ -1,59 +1,53 @@ using Microsoft.AspNetCore.Components; using System.Diagnostics.CodeAnalysis; -namespace Wave.Components.Account -{ - internal sealed class IdentityRedirectManager(NavigationManager navigationManager) - { - public const string StatusCookieName = "Identity.StatusMessage"; +namespace Wave.Components.Account; - private static readonly CookieBuilder StatusCookieBuilder = new() - { - SameSite = SameSiteMode.Strict, - HttpOnly = true, - IsEssential = true, - MaxAge = TimeSpan.FromSeconds(5), - }; +internal sealed class IdentityRedirectManager(NavigationManager navigationManager) { + public const string StatusCookieName = "Identity.StatusMessage"; - [DoesNotReturn] - public void RedirectTo(string? uri) - { - uri ??= ""; + private static readonly CookieBuilder StatusCookieBuilder = new() { + SameSite = SameSiteMode.Strict, + HttpOnly = true, + IsEssential = true, + MaxAge = TimeSpan.FromSeconds(5), + }; - // Prevent open redirects. - if (!Uri.IsWellFormedUriString(uri, UriKind.Relative)) - { - uri = navigationManager.ToBaseRelativePath(uri); - } + [DoesNotReturn] + public void RedirectTo(string? uri) { + uri ??= ""; - // During static rendering, NavigateTo throws a NavigationException which is handled by the framework as a redirect. - // So as long as this is called from a statically rendered Identity component, the InvalidOperationException is never thrown. - navigationManager.NavigateTo(uri); - throw new InvalidOperationException($"{nameof(IdentityRedirectManager)} can only be used during static rendering."); + // Prevent open redirects. + if (!Uri.IsWellFormedUriString(uri, UriKind.Relative)) { + uri = navigationManager.ToBaseRelativePath(uri); } - [DoesNotReturn] - public void RedirectTo(string uri, Dictionary queryParameters) - { - var uriWithoutQuery = navigationManager.ToAbsoluteUri(uri).GetLeftPart(UriPartial.Path); - var newUri = navigationManager.GetUriWithQueryParameters(uriWithoutQuery, queryParameters); - RedirectTo(newUri); - } - - [DoesNotReturn] - public void RedirectToWithStatus(string uri, string message, HttpContext context) - { - context.Response.Cookies.Append(StatusCookieName, message, StatusCookieBuilder.Build(context)); - RedirectTo(uri); - } - - private string CurrentPath => navigationManager.ToAbsoluteUri(navigationManager.Uri).GetLeftPart(UriPartial.Path); - - [DoesNotReturn] - public void RedirectToCurrentPage() => RedirectTo(CurrentPath); - - [DoesNotReturn] - public void RedirectToCurrentPageWithStatus(string message, HttpContext context) - => RedirectToWithStatus(CurrentPath, message, context); + // During static rendering, NavigateTo throws a NavigationException which is handled by the framework as a redirect. + // So as long as this is called from a statically rendered Identity component, the InvalidOperationException is never thrown. + navigationManager.NavigateTo(uri); + throw new InvalidOperationException( + $"{nameof(IdentityRedirectManager)} can only be used during static rendering."); } -} + + [DoesNotReturn] + public void RedirectTo(string uri, Dictionary queryParameters) { + var uriWithoutQuery = navigationManager.ToAbsoluteUri(uri).GetLeftPart(UriPartial.Path); + var newUri = navigationManager.GetUriWithQueryParameters(uriWithoutQuery, queryParameters); + RedirectTo(newUri); + } + + [DoesNotReturn] + public void RedirectToWithStatus(string uri, string message, HttpContext context) { + context.Response.Cookies.Append(StatusCookieName, message, StatusCookieBuilder.Build(context)); + RedirectTo(uri); + } + + private string CurrentPath => navigationManager.ToAbsoluteUri(navigationManager.Uri).GetLeftPart(UriPartial.Path); + + [DoesNotReturn] + public void RedirectToCurrentPage() => RedirectTo(CurrentPath); + + [DoesNotReturn] + public void RedirectToCurrentPageWithStatus(string message, HttpContext context) + => RedirectToWithStatus(CurrentPath, message, context); +} \ No newline at end of file diff --git a/Wave/Components/Account/IdentityUserAccessor.cs b/Wave/Components/Account/IdentityUserAccessor.cs index 9f54db9..fcbc92f 100644 --- a/Wave/Components/Account/IdentityUserAccessor.cs +++ b/Wave/Components/Account/IdentityUserAccessor.cs @@ -1,20 +1,19 @@ using Microsoft.AspNetCore.Identity; using Wave.Data; -namespace Wave.Components.Account -{ - internal sealed class IdentityUserAccessor(UserManager userManager, IdentityRedirectManager redirectManager) - { - public async Task GetRequiredUserAsync(HttpContext context) - { - var user = await userManager.GetUserAsync(context.User); +namespace Wave.Components.Account; - if (user is null) - { - redirectManager.RedirectToWithStatus("Account/InvalidUser", $"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", context); - } +internal sealed class IdentityUserAccessor( + UserManager userManager, + IdentityRedirectManager redirectManager) { + public async Task GetRequiredUserAsync(HttpContext context) { + var user = await userManager.GetUserAsync(context.User); - return user; + if (user is null) { + redirectManager.RedirectToWithStatus("Account/InvalidUser", + $"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", context); } + + return user; } -} +} \ No newline at end of file diff --git a/Wave/Data/ApplicationDbContext.cs b/Wave/Data/ApplicationDbContext.cs index 2b5734d..377080c 100644 --- a/Wave/Data/ApplicationDbContext.cs +++ b/Wave/Data/ApplicationDbContext.cs @@ -1,9 +1,7 @@ using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; -namespace Wave.Data -{ - public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options) - { - } -} +namespace Wave.Data; + +public class ApplicationDbContext(DbContextOptions options) + : IdentityDbContext(options) { } \ No newline at end of file diff --git a/Wave/Data/ApplicationUser.cs b/Wave/Data/ApplicationUser.cs index 758eca5..1f4d5ee 100644 --- a/Wave/Data/ApplicationUser.cs +++ b/Wave/Data/ApplicationUser.cs @@ -1,10 +1,6 @@ using Microsoft.AspNetCore.Identity; -namespace Wave.Data -{ - // Add profile data for application users by adding properties to the ApplicationUser class - public class ApplicationUser : IdentityUser - { - } +namespace Wave.Data; -} +// Add profile data for application users by adding properties to the ApplicationUser class +public class ApplicationUser : IdentityUser { } \ No newline at end of file