Skip to content

Commit

Permalink
.NET 8 (#213)
Browse files Browse the repository at this point in the history
* Bump everything.

* Update Dockerfile.

* Update tools.

* Lengthen placeholder private key.

* Use new JWT classes.

* Update openapi.json.

* Bump test project framework and depends.

* Use new JWT classes.

* Dispose of the DB context before dropping.

* Don't create a new connection to migrate.

* Bump Swashbuckle.AspNetCore to 6.6.2.

* Update openapi.json.

* Update workflow.

* Open a connection to the db if necessary.

* Bump Swashbuckle.AspNetCore.Annotations.
  • Loading branch information
TheTedder committed Jul 2, 2024
1 parent 275d5d1 commit 8977552
Show file tree
Hide file tree
Showing 16 changed files with 127 additions and 120 deletions.
6 changes: 3 additions & 3 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.9",
"version": "8.0.6",
"commands": [
"dotnet-ef"
]
},
"swashbuckle.aspnetcore.cli": {
"version": "6.5.0",
"version": "6.6.2",
"commands": [
"swagger"
]
},
"husky": {
"version": "0.5.4",
"version": "0.7.0",
"commands": [
"husky"
]
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/test-backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v2
- name: Setup dotnet 7
uses: actions/setup-dotnet@v1
uses: actions/checkout@v4
- name: Setup dotnet 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0'
- name: Restore NuGet packages
run: dotnet restore
- name: Restore tools
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
ENV ASPNETCORE_URLS "http://+:80"
ENV ASPNETCORE_HTTP_PORTS "80"
EXPOSE 80

# Note: when debugging with Visual Studio, the other stages are not used

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG DISABLE_OPENAPI_FILE_GEN=true
ARG HUSKY=0

Expand Down
5 changes: 3 additions & 2 deletions LeaderboardBackend.Test/Features/Users/LoginTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ public async Task Login_ValidRequest_ReturnsLoginResponse()
using IServiceScope s = _factory.Services.CreateScope();
JwtConfig jwtConfig = s.ServiceProvider.GetRequiredService<IOptions<JwtConfig>>().Value;
TokenValidationParameters parameters = Jwt.ValidationParameters.GetInstance(jwtConfig);

Jwt.SecurityTokenHandler.ValidateToken(content!.Token, parameters, out _).Should().BeOfType<ClaimsPrincipal>();
TokenValidationResult validationResult = await Jwt.SecurityTokenHandler.ValidateTokenAsync(content!.Token, parameters);
validationResult.IsValid.Should().BeTrue();
validationResult.ClaimsIdentity.IsAuthenticated.Should().BeTrue();
}

[TestCase(null, null, "NotNullValidator", "NotEmptyValidator", Description = "Null email + password")]
Expand Down
26 changes: 13 additions & 13 deletions LeaderboardBackend.Test/LeaderboardBackend.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoBogus" Version="2.13.1" />
<PackageReference Include="Bogus" Version="34.0.2" />
<PackageReference Include="FluentAssertions" Version="6.11.0" />
<PackageReference Include="FluentAssertions.Analyzers" Version="0.17.2">
<PackageReference Include="Bogus" Version="35.5.1" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="FluentAssertions.Analyzers" Version="0.32.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="NodaTime.Testing" Version="3.1.9" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.6" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NodaTime.Testing" Version="3.1.11" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Testcontainers" Version="3.4.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.4.0" />
<PackageReference Include="Testcontainers" Version="3.9.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.9.0" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions LeaderboardBackend.Test/TestApi/TestApiFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private static void InitializeDatabase(ApplicationContext dbContext)
{
dbContext.MigrateDatabase();
Seed(dbContext);
dbContext.Dispose();
PostgresDatabaseFixture.CreateTemplateFromCurrentDb();
}
}
Expand Down
4 changes: 2 additions & 2 deletions LeaderboardBackend/Authorization/Jwt.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

namespace LeaderboardBackend.Authorization;
Expand All @@ -10,7 +10,7 @@ namespace LeaderboardBackend.Authorization;
/// </summary>
public static class Jwt
{
public static JwtSecurityTokenHandler SecurityTokenHandler { get; } = new();
public static JsonWebTokenHandler SecurityTokenHandler { get; } = new();

public static class ValidationParameters
{
Expand Down
27 changes: 9 additions & 18 deletions LeaderboardBackend/Authorization/UserTypeAuthorizationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,22 @@ IUserService userService
_userService = userService;
}

protected override Task HandleRequirementAsync(
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
UserTypeRequirement requirement
)
{
if (!TryGetJwtFromHttpContext(context, out string? token) || !ValidateJwt(token))
if (!TryGetJwtFromHttpContext(context, out string? token) || !await ValidateJwt(token))
{
return Task.CompletedTask;
return;
}

Guid? userId = _authService.GetUserIdFromClaims(context.User);

if (userId is null)
{
context.Fail();
return Task.CompletedTask;
return;
}

User? user = _userService.GetUserById(userId.Value).Result;
Expand All @@ -48,12 +48,12 @@ UserTypeRequirement requirement
{
// FIXME: Work out how to fail as a ForbiddenResult.
context.Fail();
return Task.CompletedTask;
return;
}

context.Succeed(requirement);

return Task.CompletedTask;
return;
}

private bool Handle(User user, UserTypeRequirement requirement)
Expand Down Expand Up @@ -95,18 +95,9 @@ private static bool TryGetJwtFromHttpContext(
return Jwt.SecurityTokenHandler.CanReadToken(token);
}

private bool ValidateJwt(string token)
private async Task<bool> ValidateJwt(string token)
{
try
{
Jwt.SecurityTokenHandler.ValidateToken(token, _jwtValidationParams, out _);

return true;
}
// FIXME: Trigger a redirect to login, possibly on SecurityTokenExpiredException
catch
{
return false;
}
TokenValidationResult result = await Jwt.SecurityTokenHandler.ValidateTokenAsync(token, _jwtValidationParams);
return result.IsValid;
}
}
38 changes: 20 additions & 18 deletions LeaderboardBackend/LeaderboardBackend.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
Expand All @@ -24,27 +24,29 @@

<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="DotNetEnv" Version="2.5.0" />
<PackageReference Include="EFCore.NamingConventions" Version="7.0.2" />
<PackageReference Include="FluentValidation" Version="11.5.1" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
<PackageReference Include="MailKit" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.9">
<PackageReference Include="DotNetEnv" Version="3.0.0" />
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="FluentValidation" Version="11.9.2" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="MailKit" Version="4.6.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.FeatureManagement.AspNetCore" Version="2.6.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.6" />
<PackageReference Include="Microsoft.FeatureManagement.AspNetCore" Version="3.4.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.6" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="7.0.4" />
<PackageReference Include="OneOf" Version="3.0.243" />
<PackageReference Include="OneOf.SourceGenerator" Version="3.0.243" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="8.0.4" />
<PackageReference Include="OneOf" Version="3.0.271" />
<PackageReference Include="OneOf.SourceGenerator" Version="3.0.271" />
<PackageReference Include="ReHackt.Extensions.Options.Validation" Version="8.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.6.2" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.6.2" />
</ItemGroup>

<ItemGroup>
Expand Down
18 changes: 15 additions & 3 deletions LeaderboardBackend/Models/Entities/ApplicationContext.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Data;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Npgsql;
Expand Down Expand Up @@ -31,11 +32,22 @@ public ApplicationContext(DbContextOptions<ApplicationContext> options)
public void MigrateDatabase()
{
Database.Migrate();
bool tempConnection = false;
NpgsqlConnection connection = (NpgsqlConnection)Database.GetDbConnection();

if (connection.State is ConnectionState.Closed)
{
tempConnection = true;
Database.OpenConnection();
}

// when new extensions have been enabled by migrations, Npgsql's type cache must be refreshed
Database.OpenConnection();
((NpgsqlConnection)Database.GetDbConnection()).ReloadTypes();
Database.CloseConnection();
connection.ReloadTypes();

if (tempConnection)
{
Database.CloseConnection();
}
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
Expand Down
4 changes: 2 additions & 2 deletions LeaderboardBackend/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Text;
using System.Text.Json;
Expand All @@ -21,6 +20,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.FeatureManagement;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using NodaTime;
Expand Down Expand Up @@ -218,7 +218,7 @@
}
);

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();

// Configure authorisation.
Expand Down
25 changes: 11 additions & 14 deletions LeaderboardBackend/Services/Impl/AuthService.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using LeaderboardBackend.Authorization;
using LeaderboardBackend.Models.Entities;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

namespace LeaderboardBackend.Services;
Expand All @@ -24,22 +24,19 @@ public AuthService(IOptions<JwtConfig> options)

public string GenerateJSONWebToken(User user)
{
Claim[] claims =
SecurityTokenDescriptor descriptor = new()
{
new(JwtRegisteredClaimNames.Email, user.Email),
new(JwtRegisteredClaimNames.Sub, user.Id.ToString())
Issuer = _issuer,
Audience = _issuer,
Claims = new Dictionary<string, object> {
{ JwtRegisteredClaimNames.Email, user.Email },
{ JwtRegisteredClaimNames.Sub, user.Id.ToString() }
},
Expires = DateTime.Now.AddMinutes(30),
SigningCredentials = _credentials
};

JwtSecurityToken token =
new(
issuer: _issuer,
audience: _issuer,
claims: claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: _credentials
);

return Jwt.SecurityTokenHandler.WriteToken(token);
return Jwt.SecurityTokenHandler.CreateToken(descriptor);
}

public string? GetEmailFromClaims(ClaimsPrincipal claims)
Expand Down
2 changes: 1 addition & 1 deletion LeaderboardBackend/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"EnvPath": "../.env",
"WebsiteUrl": "http://localhost:3000",
"Jwt": {
"Key": "coolsecretkeywow",
"Key": "coolsecretkeywowitssoincredibleiloveit",
"Issuer": "leaderboards.gg"
},
"Kestrel": {
Expand Down
2 changes: 1 addition & 1 deletion LeaderboardBackend/appsettings.Staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"EnvPath": ".env",
"WebsiteUrl": "https://lbgg.netlify.app",
"Jwt": {
"Key": "coolsecretkeywow",
"Key": "coolsecretkeywowitssoincredibleiloveit",
"Issuer": "leaderboards.gg"
},
"Kestrel": {
Expand Down
Loading

0 comments on commit 8977552

Please sign in to comment.