1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-17 07:30:59 -05:00

[PM-22405] Add debugging instrument for finding invalid OrganizationUser state. (#5955)

This commit is contained in:
Jimmy Vo
2025-06-30 09:45:15 -04:00
committed by GitHub
parent 4eb2134e10
commit 1da39aa2b8
4 changed files with 198 additions and 1 deletions

View File

@ -11,6 +11,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.AdminConsole.Utilities.DebuggingInstruments;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Repositories;
using Bit.Core.Billing.Constants;
@ -900,6 +901,8 @@ public class OrganizationService : IOrganizationService
IEnumerable<Guid> organizationUsersId)
{
var orgUsers = await _organizationUserRepository.GetManyAsync(organizationUsersId);
_logger.LogUserInviteStateDiagnostics(orgUsers);
var org = await GetOrgById(organizationId);
var result = new List<Tuple<OrganizationUser, string>>();
@ -928,6 +931,8 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("User invalid.");
}
_logger.LogUserInviteStateDiagnostics(orgUser);
var org = await GetOrgById(orgUser.OrganizationId);
await SendInviteAsync(orgUser, org, initOrganization);
}

View File

@ -0,0 +1,67 @@
using System.Text.Json;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Microsoft.Extensions.Logging;
using Quartz.Util;
namespace Bit.Core.AdminConsole.Utilities.DebuggingInstruments;
/// <summary>
/// Temporary code: Log warning when OrganizationUser is in an invalid state,
/// so we can identify which flow is causing the issue through Datadog.
/// </summary>
public static class UserInviteDebuggingLogger
{
public static void LogUserInviteStateDiagnostics(this ILogger logger, OrganizationUser orgUser)
{
LogUserInviteStateDiagnostics(logger, [orgUser]);
}
public static void LogUserInviteStateDiagnostics(this ILogger logger, IEnumerable<OrganizationUser> allOrgUsers)
{
try
{
var invalidInviteState = allOrgUsers.Any(user => user.Status == OrganizationUserStatusType.Invited && user.Email.IsNullOrWhiteSpace());
if (invalidInviteState)
{
var logData = MapObjectDataToLog(allOrgUsers);
logger.LogWarning("Warning invalid invited state. {logData}", logData);
}
var invalidConfirmedOrAcceptedState = allOrgUsers.Any(user => (user.Status == OrganizationUserStatusType.Confirmed || user.Status == OrganizationUserStatusType.Accepted) && !user.Email.IsNullOrWhiteSpace());
if (invalidConfirmedOrAcceptedState)
{
var logData = MapObjectDataToLog(allOrgUsers);
logger.LogWarning("Warning invalid confirmed or accepted state. {logData}", logData);
}
}
catch (Exception exception)
{
// Ensure that this debugging instrument does not interfere with the current flow.
logger.LogWarning(exception, "Unexpected exception from UserInviteDebuggingLogger");
}
}
private static string MapObjectDataToLog(IEnumerable<OrganizationUser> allOrgUsers)
{
var log = allOrgUsers.Select(allOrgUser => new
{
allOrgUser.OrganizationId,
allOrgUser.Status,
hasEmail = !allOrgUser.Email.IsNullOrWhiteSpace(),
userId = allOrgUser.UserId,
allOrgUserId = allOrgUser.Id
});
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
return JsonSerializer.Serialize(log, options);
}
}