Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET 8 #213

Merged
merged 15 commits into from
Jul 2, 2024
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
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();
zysim marked this conversation as resolved.
Show resolved Hide resolved

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