Skip to content

Commit 9a1f91f

Browse files
fix(auth-validator): [PM-22975] Client Version Validator - Fixed tests.
1 parent f44cd58 commit 9a1f91f

File tree

2 files changed

+61
-23
lines changed

2 files changed

+61
-23
lines changed

test/Identity.IntegrationTest/Login/ClientVersionGateTests.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace Bit.Identity.IntegrationTest.Login;
1313
public class ClientVersionGateTests : IClassFixture<IdentityApplicationFactory>
1414
{
1515
private readonly IdentityApplicationFactory _factory;
16+
private const string DefaultEncryptedString = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=";
1617

1718
public ClientVersionGateTests(IdentityApplicationFactory factory)
1819
{
@@ -23,6 +24,7 @@ public ClientVersionGateTests(IdentityApplicationFactory factory)
2324
[Theory, BitAutoData, RegisterFinishRequestModelCustomize]
2425
public async Task TokenEndpoint_GrantTypePassword_V2User_OnOldClientVersion_Blocked(RegisterFinishRequestModel requestModel)
2526
{
27+
NormalizeEncryptedStrings(requestModel);
2628
var localFactory = new IdentityApplicationFactory
2729
{
2830
UseMockClientVersionValidator = false
@@ -72,6 +74,7 @@ public async Task TokenEndpoint_GrantTypePassword_V2User_OnOldClientVersion_Bloc
7274
[Theory, BitAutoData, RegisterFinishRequestModelCustomize]
7375
public async Task TokenEndpoint_GrantTypePassword_V2User_OnMinClientVersion_Succeeds(RegisterFinishRequestModel requestModel)
7476
{
77+
NormalizeEncryptedStrings(requestModel);
7578
var localFactory = new IdentityApplicationFactory
7679
{
7780
UseMockClientVersionValidator = false
@@ -123,4 +126,24 @@ private void ReinitializeDbForTests(IdentityApplicationFactory factory)
123126
databaseContext.Users.RemoveRange(databaseContext.Users);
124127
databaseContext.SaveChanges();
125128
}
129+
130+
private static void NormalizeEncryptedStrings(RegisterFinishRequestModel requestModel)
131+
{
132+
var accountKeys = requestModel.UserAsymmetricKeys.AccountKeys;
133+
if (accountKeys == null)
134+
{
135+
return;
136+
}
137+
138+
accountKeys.UserKeyEncryptedAccountPrivateKey = DefaultEncryptedString;
139+
if (accountKeys.PublicKeyEncryptionKeyPair != null)
140+
{
141+
accountKeys.PublicKeyEncryptionKeyPair.WrappedPrivateKey = DefaultEncryptedString;
142+
}
143+
if (accountKeys.SignatureKeyPair != null)
144+
{
145+
accountKeys.SignatureKeyPair.WrappedSigningKey = DefaultEncryptedString;
146+
accountKeys.SignatureKeyPair.SignatureAlgorithm = "ed25519";
147+
}
148+
}
126149
}

test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Concurrent;
55
using System.Net.Http.Json;
6+
using System.Text;
67
using System.Text.Json;
78
using Bit.Core.Auth.Models.Api.Request.Accounts;
89
using Bit.Core.Entities;
@@ -25,6 +26,7 @@ public class IdentityApplicationFactory : WebApplicationFactoryBase<Startup>
2526
public const string DefaultDeviceIdentifier = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb";
2627
public const string DefaultUserEmail = "[email protected]";
2728
public const string DefaultUserPasswordHash = "default_password_hash";
29+
private const string DefaultEncryptedString = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=";
2830
public bool UseMockClientVersionValidator { get; set; } = true;
2931

3032
/// <summary>
@@ -88,20 +90,8 @@ public async Task<HttpContext> PostRegisterVerificationEmailClicked(RegisterVeri
8890
var context = await ContextFromPasswordAsync(
8991
username, password, deviceIdentifier, clientId, deviceType, deviceName);
9092

91-
// Provide clearer diagnostics on failure
92-
if (context.Response.StatusCode != StatusCodes.Status200OK)
93-
{
94-
var contentType = context.Response.ContentType ?? string.Empty;
95-
if (context.Response.Body.CanSeek)
96-
{
97-
context.Response.Body.Position = 0;
98-
}
99-
string rawBody = await new StreamReader(context.Response.Body).ReadToEndAsync();
100-
throw new Xunit.Sdk.XunitException($"Login failed: status={context.Response.StatusCode}, contentType='{contentType}', body='{rawBody}'");
101-
}
102-
103-
using var jsonDoc = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
104-
var root = jsonDoc.RootElement;
93+
using var body = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
94+
var root = body.RootElement;
10595

10696
return (root.GetProperty("access_token").GetString(), root.GetProperty("refresh_token").GetString());
10797
}
@@ -124,13 +114,7 @@ public async Task<HttpContext> ContextFromPasswordAsync(
124114
{ "grant_type", "password" },
125115
{ "username", username },
126116
{ "password", password },
127-
}),
128-
http =>
129-
{
130-
// Ensure JSON content negotiation for errors and set a sane client version
131-
http.Request.Headers.Append("Accept", "application/json");
132-
http.Request.Headers.Append("Bitwarden-Client-Version", "2025.11.0");
133-
});
117+
}));
134118

135119
return context;
136120
}
@@ -242,8 +226,11 @@ public async Task<User> RegisterNewIdentityFactoryUserAsync(
242226
requestModel.EmailVerificationToken = RegistrationTokens[requestModel.Email];
243227

244228
var postRegisterFinishHttpContext = await PostRegisterFinishAsync(requestModel);
245-
246-
Assert.Equal(StatusCodes.Status200OK, postRegisterFinishHttpContext.Response.StatusCode);
229+
if (postRegisterFinishHttpContext.Response.StatusCode != StatusCodes.Status200OK)
230+
{
231+
var body = await ReadResponseBodyAsync(postRegisterFinishHttpContext);
232+
Assert.Fail($"register/finish failed (status {postRegisterFinishHttpContext.Response.StatusCode}). Body: {body}");
233+
}
247234

248235
var database = GetDatabaseContext();
249236
var user = await database.Users
@@ -253,4 +240,32 @@ public async Task<User> RegisterNewIdentityFactoryUserAsync(
253240

254241
return user;
255242
}
243+
244+
private static async Task<string> ReadResponseBodyAsync(HttpContext ctx)
245+
{
246+
try
247+
{
248+
if (ctx?.Response.Body == null)
249+
{
250+
return "<no body>";
251+
}
252+
var stream = ctx.Response.Body;
253+
if (stream.CanSeek)
254+
{
255+
stream.Seek(0, SeekOrigin.Begin);
256+
}
257+
using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true);
258+
var text = await reader.ReadToEndAsync();
259+
if (stream.CanSeek)
260+
{
261+
stream.Seek(0, SeekOrigin.Begin);
262+
}
263+
return string.IsNullOrWhiteSpace(text) ? "<empty body>" : text;
264+
}
265+
catch (Exception ex)
266+
{
267+
return $"<error reading body: {ex.Message}>";
268+
}
269+
}
270+
256271
}

0 commit comments

Comments
 (0)