Skip to content

Commit

Permalink
Use primary constructors!
Browse files Browse the repository at this point in the history
  • Loading branch information
davidfowl committed Jul 14, 2023
1 parent 156eeb2 commit 88d236b
Show file tree
Hide file tree
Showing 9 changed files with 23 additions and 62 deletions.
20 changes: 7 additions & 13 deletions Todo.Web/Client/TodoClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@

namespace Todo.Web.Client;

public class TodoClient
public class TodoClient(HttpClient client)
{
private readonly HttpClient _client;
public TodoClient(HttpClient client)
{
_client = client;
}

public async Task<TodoItem?> AddTodoAsync(string? title)
{
if (string.IsNullOrEmpty(title))
Expand All @@ -20,7 +14,7 @@ public TodoClient(HttpClient client)

TodoItem? createdTodo = null;

var response = await _client.PostAsJsonAsync("todos", new TodoItem { Title = title });
var response = await client.PostAsJsonAsync("todos", new TodoItem { Title = title });

if (response.IsSuccessStatusCode)
{
Expand All @@ -32,13 +26,13 @@ public TodoClient(HttpClient client)

public async Task<bool> DeleteTodoAsync(int id)
{
var response = await _client.DeleteAsync($"todos/{id}");
var response = await client.DeleteAsync($"todos/{id}");
return response.IsSuccessStatusCode;
}

public async Task<(HttpStatusCode, List<TodoItem>?)> GetTodosAsync()
{
var response = await _client.GetAsync("todos");
var response = await client.GetAsync("todos");
var statusCode = response.StatusCode;
List<TodoItem>? todos = null;

Expand All @@ -57,7 +51,7 @@ public async Task<bool> LoginAsync(string? username, string? password)
return false;
}

var response = await _client.PostAsJsonAsync("auth/login", new UserInfo { Username = username, Password = password });
var response = await client.PostAsJsonAsync("auth/login", new UserInfo { Username = username, Password = password });
return response.IsSuccessStatusCode;
}

Expand All @@ -68,13 +62,13 @@ public async Task<bool> CreateUserAsync(string? username, string? password)
return false;
}

var response = await _client.PostAsJsonAsync("auth/register", new UserInfo { Username = username, Password = password });
var response = await client.PostAsJsonAsync("auth/register", new UserInfo { Username = username, Password = password });
return response.IsSuccessStatusCode;
}

public async Task<bool> LogoutAsync()
{
var response = await _client.PostAsync("auth/logout", content: null);
var response = await client.PostAsync("auth/logout", content: null);
return response.IsSuccessStatusCode;
}
}
15 changes: 4 additions & 11 deletions Todo.Web/Server/AuthClient.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
namespace Todo.Web.Server;

public class AuthClient
public class AuthClient(HttpClient client)
{
private readonly HttpClient _client;

public AuthClient(HttpClient client)
{
_client = client;
}

public async Task<string?> GetTokenAsync(UserInfo userInfo)
{
var response = await _client.PostAsJsonAsync("users/login", userInfo);
var response = await client.PostAsJsonAsync("users/login", userInfo);

if (!response.IsSuccessStatusCode)
{
Expand All @@ -25,7 +18,7 @@ public AuthClient(HttpClient client)

public async Task<string?> CreateUserAsync(UserInfo userInfo)
{
var response = await _client.PostAsJsonAsync("users/register", userInfo);
var response = await client.PostAsJsonAsync("users/register", userInfo);

if (!response.IsSuccessStatusCode)
{
Expand All @@ -37,7 +30,7 @@ public AuthClient(HttpClient client)

public async Task<string?> GetOrCreateUserAsync(string provider, ExternalUserInfo userInfo)
{
var response = await _client.PostAsJsonAsync($"users/token/{provider}", userInfo);
var response = await client.PostAsJsonAsync($"users/token/{provider}", userInfo);

if (!response.IsSuccessStatusCode)
{
Expand Down
10 changes: 2 additions & 8 deletions Todo.Web/Server/Authentication/ExternalProviders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@

namespace Todo.Web.Server;

public class ExternalProviders
public class ExternalProviders(IAuthenticationSchemeProvider schemeProvider)
{
private readonly IAuthenticationSchemeProvider _schemeProvider;
private Task<string[]>? _providerNames;

public ExternalProviders(IAuthenticationSchemeProvider schemeProvider)
{
_schemeProvider = schemeProvider;
}

public Task<string[]> GetProviderNamesAsync()
{
return _providerNames ??= GetProviderNamesAsyncCore();
Expand All @@ -22,7 +16,7 @@ private async Task<string[]> GetProviderNamesAsyncCore()
{
List<string>? providerNames = null;

var schemes = await _schemeProvider.GetAllSchemesAsync();
var schemes = await schemeProvider.GetAllSchemesAsync();

foreach (var s in schemes)
{
Expand Down
1 change: 1 addition & 0 deletions Todo.Web/Server/TodoApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static RouteGroupBuilder MapTodos(this IEndpointRouteBuilder routes, stri

group.RequireAuthorization();

// TODO: Remove when new YARP is released, see https://github.com/microsoft/reverse-proxy/issues/2131
var transformBuilder = routes.ServiceProvider.GetRequiredService<ITransformBuilder>();
var transform = transformBuilder.Create(b =>
{
Expand Down
2 changes: 1 addition & 1 deletion TodoApi.Tests/TodoApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private async Task<string> CreateTokenAsync(string id, bool isAdmin = false)

// Read the user JWTs configuration for testing so unit tests can generate
// JWT tokens.
var tokenService = scope.ServiceProvider.GetRequiredService<ITokenService>();
var tokenService = scope.ServiceProvider.GetRequiredService<TokenService>();

return await tokenService.GenerateTokenAsync(id, isAdmin);
}
Expand Down
11 changes: 2 additions & 9 deletions TodoApi.Tests/TokenService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,11 @@ public static class AuthenticationServiceExtensions
public static IServiceCollection AddTokenService(this IServiceCollection services)
{
// Wire up the token service
return services.AddScoped<ITokenService, TokenService>();
return services.AddScoped<TokenService>();
}
}

public interface ITokenService
{
// Generate a token for the specified user name and admin role
Task<string> GenerateTokenAsync(string username, bool isAdmin = false);
}

public sealed class TokenService(SignInManager<TodoUser> signInManager,
IOptionsMonitor<BearerTokenOptions> options) : ITokenService
public sealed class TokenService(SignInManager<TodoUser> signInManager, IOptionsMonitor<BearerTokenOptions> options)
{
private readonly BearerTokenOptions _options = options.Get(IdentityConstants.BearerScheme);

Expand Down
7 changes: 2 additions & 5 deletions TodoApi/Authorization/CheckCurrentUserAuthHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,12 @@ private class CheckCurrentUserRequirement : IAuthorizationRequirement { }

// This authorization handler verifies that the user exists even if there's
// a valid token
private class CheckCurrentUserAuthHandler : AuthorizationHandler<CheckCurrentUserRequirement>
private class CheckCurrentUserAuthHandler(CurrentUser currentUser) : AuthorizationHandler<CheckCurrentUserRequirement>
{
private readonly CurrentUser _currentUser;
public CheckCurrentUserAuthHandler(CurrentUser currentUser) => _currentUser = currentUser;

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CheckCurrentUserRequirement requirement)
{
// TODO: Check user if the user is locked out as well
if (_currentUser.User is not null)
if (currentUser.User is not null)
{
context.Succeed(requirement);
}
Expand Down
15 changes: 3 additions & 12 deletions TodoApi/Authorization/CurrentUserExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,19 @@ public static IServiceCollection AddCurrentUser(this IServiceCollection services
return services;
}

private sealed class ClaimsTransformation : IClaimsTransformation
private sealed class ClaimsTransformation(CurrentUser currentUser, UserManager<TodoUser> userManager) : IClaimsTransformation
{
private readonly CurrentUser _currentUser;
private readonly UserManager<TodoUser> _userManager;

public ClaimsTransformation(CurrentUser currentUser, UserManager<TodoUser> userManager)
{
_currentUser = currentUser;
_userManager = userManager;
}

public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
// We're not going to transform anything. We're using this as a hook into authorization
// to set the current user without adding custom middleware.
_currentUser.Principal = principal;
currentUser.Principal = principal;

if (principal.FindFirstValue(ClaimTypes.NameIdentifier) is { Length: > 0 } id)
{
// Resolve the user manager and see if the current user is a valid user in the database
// we do this once and store it on the current user.
_currentUser.User = await _userManager.FindByIdAsync(id);
currentUser.User = await userManager.FindByIdAsync(id);
}

return principal;
Expand Down
4 changes: 1 addition & 3 deletions TodoApi/TodoDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@

namespace TodoApi;

public class TodoDbContext : IdentityDbContext<TodoUser>
public class TodoDbContext(DbContextOptions<TodoDbContext> options) : IdentityDbContext<TodoUser>(options)
{
public TodoDbContext(DbContextOptions<TodoDbContext> options) : base(options) { }

public DbSet<Todo> Todos => Set<Todo>();

protected override void OnModelCreating(ModelBuilder builder)
Expand Down

0 comments on commit 88d236b

Please sign in to comment.