diff --git a/.github/workflows/lexbox-api.yaml b/.github/workflows/lexbox-api.yaml index 32cef0f0c..09b4e0630 100644 --- a/.github/workflows/lexbox-api.yaml +++ b/.github/workflows/lexbox-api.yaml @@ -45,7 +45,9 @@ jobs: steps: - uses: actions/checkout@v3 - + - uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.x' - name: Dotnet build run: dotnet build - name: Unit tests diff --git a/backend/Directory.Build.props b/backend/Directory.Build.props index 6c5a1264e..ed66f2da0 100644 --- a/backend/Directory.Build.props +++ b/backend/Directory.Build.props @@ -1,12 +1,17 @@ - - /app/obj/ - /app/bin/ + + $(DefaultItemExcludes);$(MSBuildProjectDirectory)/obj/**/* + $(DefaultItemExcludes);$(MSBuildProjectDirectory)/bin/**/* + + + + $(MSBuildProjectDirectory)/obj/container/ + $(MSBuildProjectDirectory)/bin/container/ false - + diff --git a/backend/Dockerfile b/backend/Dockerfile index 3bff244cb..5db510894 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,10 +1,10 @@ # syntax=docker/dockerfile:1 -FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build COPY . . # WORKDIR /src diff --git a/backend/FixFwData/FixFwData.csproj b/backend/FixFwData/FixFwData.csproj index 0d98c4104..f8bc8031c 100644 --- a/backend/FixFwData/FixFwData.csproj +++ b/backend/FixFwData/FixFwData.csproj @@ -8,7 +8,7 @@ SIL International LexBoxApi Testing Copyright © 2023 SIL International - net7.0 + net8.0 enable enable diff --git a/backend/LexBoxApi/Auth/JwtTicketDataFormat.cs b/backend/LexBoxApi/Auth/JwtTicketDataFormat.cs index e013991fc..604575a38 100644 --- a/backend/LexBoxApi/Auth/JwtTicketDataFormat.cs +++ b/backend/LexBoxApi/Auth/JwtTicketDataFormat.cs @@ -51,7 +51,7 @@ public static string ConvertAuthTicketToJwt(AuthenticationTicket data, { var jwtDate = DateTime.UtcNow; _jwtSecurityTokenHandler.MapInboundClaims = jwtBearerOptions.MapInboundClaims; - var claimsIdentity = new ClaimsIdentity(data.Principal.Claims, data.Principal.Identity?.AuthenticationType); + var claimsIdentity = new ClaimsIdentity(data.Principal.Claims.Where(c => c.Type != JwtRegisteredClaimNames.Jti), data.Principal.Identity?.AuthenticationType); var keyId = Guid.NewGuid().ToString().GetHashCode().ToString("x", CultureInfo.InvariantCulture); claimsIdentity.AddClaim(new Claim(JwtRegisteredClaimNames.Jti, keyId)); //there may already be an audience claim, we want to reuse that if it exists, if not fallback to the default audience diff --git a/backend/LexBoxApi/GraphQL/GraphQlSetupKernel.cs b/backend/LexBoxApi/GraphQL/GraphQlSetupKernel.cs index fbcc71e8d..0121e0e92 100644 --- a/backend/LexBoxApi/GraphQL/GraphQlSetupKernel.cs +++ b/backend/LexBoxApi/GraphQL/GraphQlSetupKernel.cs @@ -64,6 +64,7 @@ public static void AddLexGraphQL(this IServiceCollection services, IHostEnvironm { options.IncludeExceptionDetails = true; }) + .AddType() .AddType(new DateTimeType("DateTime")) .AddType(new UuidType("UUID")) .AddType(new DateTimeType("timestamptz")) diff --git a/backend/LexBoxApi/LexBoxApi.csproj b/backend/LexBoxApi/LexBoxApi.csproj index 9e51fe404..7a6e5ea4d 100644 --- a/backend/LexBoxApi/LexBoxApi.csproj +++ b/backend/LexBoxApi/LexBoxApi.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable Linux @@ -10,35 +10,36 @@ - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - + + + + - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + + + + + + + - - - + + + diff --git a/backend/LexBoxApi/dev.Dockerfile b/backend/LexBoxApi/dev.Dockerfile index 38591a28f..20e768811 100644 --- a/backend/LexBoxApi/dev.Dockerfile +++ b/backend/LexBoxApi/dev.Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build EXPOSE 80 EXPOSE 443 RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ @@ -14,5 +14,4 @@ RUN for file in $(ls *.csproj); do dir=${file%.*} mkdir -p ${file%.*}/ && mv $fi COPY . . WORKDIR /src/backend/LexBoxApi RUN mkdir /src/frontend -ENV DockerDev=true CMD dotnet watch run -lp docker --property:InformationalVersion=dockerDev diff --git a/backend/LexCore/LexCore.csproj b/backend/LexCore/LexCore.csproj index 83321cc3d..276363f1a 100644 --- a/backend/LexCore/LexCore.csproj +++ b/backend/LexCore/LexCore.csproj @@ -1,14 +1,14 @@ - net7.0 + net8.0 enable enable dev - + diff --git a/backend/LexData/LexData.csproj b/backend/LexData/LexData.csproj index 836de9f92..8e7835b61 100644 --- a/backend/LexData/LexData.csproj +++ b/backend/LexData/LexData.csproj @@ -1,25 +1,25 @@ - net7.0 + net8.0 enable enable dev - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - + + + + + + diff --git a/backend/SyncReverseProxy/Auth/BasicAuthHandler.cs b/backend/SyncReverseProxy/Auth/BasicAuthHandler.cs index 0e77adf4f..5b9039a6f 100644 --- a/backend/SyncReverseProxy/Auth/BasicAuthHandler.cs +++ b/backend/SyncReverseProxy/Auth/BasicAuthHandler.cs @@ -20,9 +20,8 @@ public class BasicAuthHandler : AuthenticationHandler options, ILoggerFactory logger, UrlEncoder encoder, - ISystemClock clock, ILexProxyService lexProxyService, - IMemoryCache memoryCache) : base(options, logger, encoder, clock) + IMemoryCache memoryCache) : base(options, logger, encoder) { _lexProxyService = lexProxyService; _memoryCache = memoryCache; diff --git a/backend/SyncReverseProxy/SyncReverseProxy.csproj b/backend/SyncReverseProxy/SyncReverseProxy.csproj index 9cd1d3788..d275d5110 100644 --- a/backend/SyncReverseProxy/SyncReverseProxy.csproj +++ b/backend/SyncReverseProxy/SyncReverseProxy.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable Linux @@ -10,17 +10,17 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/backend/Testing/LexCore/LexAuthUserTests.cs b/backend/Testing/LexCore/LexAuthUserTests.cs index 96e2423ee..170c5c618 100644 --- a/backend/Testing/LexCore/LexAuthUserTests.cs +++ b/backend/Testing/LexCore/LexAuthUserTests.cs @@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; using Shouldly; @@ -15,6 +16,10 @@ namespace Testing.LexCore; public class LexAuthUserTests { + static LexAuthUserTests() + { + IdentityModelEventSource.ShowPII = true; + } private readonly LexAuthService _lexAuthService = new LexAuthService( new OptionsWrapper(JwtOptions.TestingOptions), null!, @@ -30,6 +35,12 @@ public class LexAuthUserTests Projects = new[] { new AuthUserProject(ProjectRole.Manager, Guid.NewGuid()) } }; + private static readonly JwtBearerOptions JwtBearerOptions = new() + { + TokenValidationParameters = LexAuthService.TokenValidationParameters(JwtOptions.TestingOptions), + MapInboundClaims = false + }; + [Fact] public void CanGetClaimsFromUser() { @@ -73,11 +84,7 @@ public void CanRoundTripClaimsWhenUsingSecurityTokenDescriptor() var jwt = JwtTicketDataFormat.ConvertAuthTicketToJwt( new AuthenticationTicket(_user.GetPrincipal("test"), "test"), "testing", - new JwtBearerOptions - { - TokenValidationParameters = LexAuthService.TokenValidationParameters(jwtUserOptions), - MapInboundClaims = false - }, + JwtBearerOptions, jwtUserOptions ); var tokenHandler = new JwtSecurityTokenHandler(); @@ -120,4 +127,25 @@ public void CheckingJwtLength() var (jwt, _) = _lexAuthService.GenerateJwt(user); jwt.Length.ShouldBeLessThan(LexAuthUser.MaxJwtLength); } + + [Fact] + public void CanRoundTripThroughRefresh() + { + var (forgotJwt, _) = _lexAuthService.GenerateJwt(_user, audience:LexboxAudience.ForgotPassword); + //simulate parsing the token into a claims principal + var tokenHandler = new JwtSecurityTokenHandler(); + var forgotPrincipal = new ClaimsPrincipal(new ClaimsIdentity(tokenHandler.ReadJwtToken(forgotJwt).Claims, "Testing")); + + //simulate redirect refreshing the token + var redirectJwt = JwtTicketDataFormat.ConvertAuthTicketToJwt( + new AuthenticationTicket(forgotPrincipal, "test"), + "testing", + JwtBearerOptions, + JwtOptions.TestingOptions + ); + + var loggedInPrincipal = new ClaimsPrincipal(new ClaimsIdentity(tokenHandler.ReadJwtToken(redirectJwt).Claims, "Testing")); + var newUser = LexAuthUser.FromClaimsPrincipal(loggedInPrincipal); + (_user with { Audience = LexboxAudience.ForgotPassword }).ShouldBeEquivalentTo(newUser); + } } diff --git a/backend/Testing/Testing.csproj b/backend/Testing/Testing.csproj index 5c57fc2aa..0cbc8fcd1 100644 --- a/backend/Testing/Testing.csproj +++ b/backend/Testing/Testing.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable @@ -10,29 +10,29 @@ - - - - - - - - - + + + + + + + + + - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/deployment/base/lexbox-deployment.yaml b/deployment/base/lexbox-deployment.yaml index f7dde5ada..ba01ad748 100644 --- a/deployment/base/lexbox-deployment.yaml +++ b/deployment/base/lexbox-deployment.yaml @@ -36,6 +36,7 @@ spec: selector: matchLabels: app: lexbox + progressDeadlineSeconds: 60 strategy: rollingUpdate: maxSurge: 2 diff --git a/frontend/schema.graphql b/frontend/schema.graphql index 579adc24e..fd19d68bf 100644 --- a/frontend/schema.graphql +++ b/frontend/schema.graphql @@ -554,6 +554,8 @@ directive @authorize("The name of the authorization policy that determines acces "The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR +directive @tag(name: String!) repeatable on SCHEMA | SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION + scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") scalar UUID