mirror of
https://github.com/bitwarden/server
synced 2026-01-02 00:23:40 +00:00
test(auth-validator): [PM-22975] Client Version Validator - WIP changes.
This commit is contained in:
@@ -68,7 +68,7 @@ public class ApiApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
UserAsymmetricKeys = new KeysRequestModel()
|
||||
{
|
||||
PublicKey = TestEncryptionConstants.PublicKey,
|
||||
EncryptedPrivateKey = TestEncryptionConstants.V1EncryptedBase64 // v1-format so parsing succeeds and user is treated as v1
|
||||
EncryptedPrivateKey = TestEncryptionConstants.V1EncryptedBase64
|
||||
},
|
||||
UserSymmetricKey = TestEncryptionConstants.V1EncryptedBase64,
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ public class GetMinimumClientVersionForUserQueryTests
|
||||
{
|
||||
var sut = new GetMinimumClientVersionForUserQuery(new FakeIsV2Query(true));
|
||||
var version = await sut.Run(new User());
|
||||
Assert.Equal(Core.KeyManagement.Constants.MinimumClientVersion, version);
|
||||
Assert.Equal(Core.KeyManagement.Constants.MinimumClientVersionForV2Encryption, version);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -3,6 +3,7 @@ using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.IntegrationTestCommon;
|
||||
using Bit.IntegrationTestCommon.Factories;
|
||||
using Bit.Test.Common.Constants;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
@@ -58,10 +59,10 @@ public class EventsApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
|
||||
UserAsymmetricKeys = new KeysRequestModel()
|
||||
{
|
||||
PublicKey = "public_key",
|
||||
EncryptedPrivateKey = "private_key"
|
||||
PublicKey = TestEncryptionConstants.PublicKey,
|
||||
EncryptedPrivateKey = TestEncryptionConstants.V1EncryptedBase64
|
||||
},
|
||||
UserSymmetricKey = "sym_key",
|
||||
UserSymmetricKey = TestEncryptionConstants.V1EncryptedBase64,
|
||||
});
|
||||
|
||||
return await _identityApplicationFactory.TokenFromPasswordAsync(email, masterPasswordHash);
|
||||
|
||||
@@ -343,7 +343,7 @@ public class IdentityServerSsoTests
|
||||
{ "code_verifier", challenge },
|
||||
{ "redirect_uri", "https://localhost:8080/sso-connector.html" }
|
||||
}),
|
||||
http => { http.Request.Headers.Append("Bitwarden-Client-Version", "2025.11.0"); });
|
||||
http => { http.Request.Headers.Append("Bitwarden-Client-Version", "2025.10.0"); });
|
||||
|
||||
// Assert
|
||||
// If the organization has selected TrustedDeviceEncryption but the user still has their master password
|
||||
@@ -415,7 +415,7 @@ public class IdentityServerSsoTests
|
||||
}),
|
||||
http =>
|
||||
{
|
||||
http.Request.Headers.Append("Bitwarden-Client-Version", "2025.11.0");
|
||||
http.Request.Headers.Append("Bitwarden-Client-Version", "2025.10.0");
|
||||
http.Request.Headers.Append("Accept", "application/json");
|
||||
});
|
||||
|
||||
@@ -491,7 +491,7 @@ public class IdentityServerSsoTests
|
||||
{ "code_verifier", challenge },
|
||||
{ "redirect_uri", "https://localhost:8080/sso-connector.html" }
|
||||
}),
|
||||
http => { http.Request.Headers.Append("Bitwarden-Client-Version", "2025.11.0"); });
|
||||
http => { http.Request.Headers.Append("Bitwarden-Client-Version", "2025.10.0"); });
|
||||
|
||||
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||
using var responseBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||
@@ -569,7 +569,7 @@ public class IdentityServerSsoTests
|
||||
{ "code_verifier", challenge },
|
||||
{ "redirect_uri", "https://localhost:8080/sso-connector.html" }
|
||||
}),
|
||||
http => { http.Request.Headers.Append("Bitwarden-Client-Version", "2025.11.0"); });
|
||||
http => { http.Request.Headers.Append("Bitwarden-Client-Version", "2025.10.0"); });
|
||||
|
||||
// If this fails, surface detailed error information to aid debugging
|
||||
if (context.Response.StatusCode != StatusCodes.Status200OK)
|
||||
@@ -656,8 +656,11 @@ public class IdentityServerSsoTests
|
||||
|
||||
factory.SubstituteService<IAuthorizationCodeStore>(service =>
|
||||
{
|
||||
service.GetAuthorizationCodeAsync("test_code")
|
||||
// Return our pre-built authorization code regardless of handle representation
|
||||
service.GetAuthorizationCodeAsync(Arg.Any<string>())
|
||||
.Returns(authorizationCode);
|
||||
service.RemoveAuthorizationCodeAsync(Arg.Any<string>())
|
||||
.Returns(Task.CompletedTask);
|
||||
});
|
||||
|
||||
var user = await factory.RegisterNewIdentityFactoryUserAsync(
|
||||
|
||||
@@ -387,10 +387,10 @@ public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFac
|
||||
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
|
||||
UserAsymmetricKeys = new KeysRequestModel()
|
||||
{
|
||||
PublicKey = "public_key",
|
||||
EncryptedPrivateKey = "private_key"
|
||||
PublicKey = Bit.Test.Common.Constants.TestEncryptionConstants.PublicKey,
|
||||
EncryptedPrivateKey = Bit.Test.Common.Constants.TestEncryptionConstants.V1EncryptedBase64
|
||||
},
|
||||
UserSymmetricKey = "sym_key",
|
||||
UserSymmetricKey = Bit.Test.Common.Constants.TestEncryptionConstants.V1EncryptedBase64,
|
||||
});
|
||||
Assert.NotNull(user);
|
||||
|
||||
@@ -441,10 +441,10 @@ public class IdentityServerTwoFactorTests : IClassFixture<IdentityApplicationFac
|
||||
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
|
||||
UserAsymmetricKeys = new KeysRequestModel()
|
||||
{
|
||||
PublicKey = "public_key",
|
||||
EncryptedPrivateKey = "private_key"
|
||||
PublicKey = Bit.Test.Common.Constants.TestEncryptionConstants.PublicKey,
|
||||
EncryptedPrivateKey = Bit.Test.Common.Constants.TestEncryptionConstants.V1EncryptedBase64
|
||||
},
|
||||
UserSymmetricKey = "sym_key",
|
||||
UserSymmetricKey = Bit.Test.Common.Constants.TestEncryptionConstants.V1EncryptedBase64,
|
||||
});
|
||||
|
||||
var userService = factory.GetService<IUserService>();
|
||||
|
||||
@@ -613,10 +613,10 @@ public class ResourceOwnerPasswordValidatorTests : IClassFixture<IdentityApplica
|
||||
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
|
||||
UserAsymmetricKeys = new KeysRequestModel
|
||||
{
|
||||
PublicKey = "public_key",
|
||||
EncryptedPrivateKey = "private_key"
|
||||
PublicKey = Bit.Test.Common.Constants.TestEncryptionConstants.PublicKey,
|
||||
EncryptedPrivateKey = Bit.Test.Common.Constants.TestEncryptionConstants.V1EncryptedBase64
|
||||
},
|
||||
UserSymmetricKey = "sym_key",
|
||||
UserSymmetricKey = Bit.Test.Common.Constants.TestEncryptionConstants.V1EncryptedBase64,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -50,4 +50,17 @@ public class ClientVersionValidatorTests
|
||||
var ok = await sut.ValidateAsync(new User(), new Bit.Identity.IdentityServer.CustomValidatorRequestContext());
|
||||
Assert.True(ok);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Allows_When_ClientVersionHeaderMissing()
|
||||
{
|
||||
// Do not set ClientVersion on the context (remains null) and ensure we fail open
|
||||
var ctx = Substitute.For<ICurrentContext>();
|
||||
var minQuery = MakeMinQuery(new Version("2025.11.0"));
|
||||
var sut = new ClientVersionValidator(ctx, minQuery);
|
||||
|
||||
var ok = await sut.ValidateAsync(new User(), new Bit.Identity.IdentityServer.CustomValidatorRequestContext());
|
||||
|
||||
Assert.True(ok);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,20 @@ public class IdentityApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
var context = await ContextFromPasswordAsync(
|
||||
username, password, deviceIdentifier, clientId, deviceType, deviceName);
|
||||
|
||||
using var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||
var root = body.RootElement;
|
||||
// Provide clearer diagnostics on failure
|
||||
if (context.Response.StatusCode != StatusCodes.Status200OK)
|
||||
{
|
||||
var contentType = context.Response.ContentType ?? string.Empty;
|
||||
if (context.Response.Body.CanSeek)
|
||||
{
|
||||
context.Response.Body.Position = 0;
|
||||
}
|
||||
string rawBody = await new StreamReader(context.Response.Body).ReadToEndAsync();
|
||||
throw new Xunit.Sdk.XunitException($"Login failed: status={context.Response.StatusCode}, contentType='{contentType}', body='{rawBody}'");
|
||||
}
|
||||
|
||||
using var jsonDoc = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||
var root = jsonDoc.RootElement;
|
||||
|
||||
return (root.GetProperty("access_token").GetString(), root.GetProperty("refresh_token").GetString());
|
||||
}
|
||||
@@ -99,7 +111,13 @@ public class IdentityApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
{ "grant_type", "password" },
|
||||
{ "username", username },
|
||||
{ "password", password },
|
||||
}));
|
||||
}),
|
||||
http =>
|
||||
{
|
||||
// Ensure JSON content negotiation for errors and set a sane client version
|
||||
http.Request.Headers.Append("Accept", "application/json");
|
||||
http.Request.Headers.Append("Bitwarden-Client-Version", "2025.11.0");
|
||||
});
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ public static class WebApplicationFactoryExtensions
|
||||
// it runs after this so it will take precedence.
|
||||
httpContext.Connection.RemoteIpAddress = IPAddress.Parse(FactoryConstants.WhitelistedIp);
|
||||
|
||||
// Ensure response body is bufferable and seekable for tests to read later
|
||||
httpContext.Response.Body = new MemoryStream();
|
||||
|
||||
httpContext.Request.Path = new PathString(requestUri);
|
||||
httpContext.Request.Method = method.Method;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user