mirror of
https://github.com/bitwarden/server.git
synced 2025-05-11 14:42:19 -05:00
create new model for sync data
This commit is contained in:
parent
2f4574f6b7
commit
9801c63d28
@ -0,0 +1,10 @@
|
|||||||
|
namespace Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
|
|
||||||
|
public class OrganizationUserSyncData
|
||||||
|
{
|
||||||
|
public HashSet<string> NewUsersSet { get; set; }
|
||||||
|
public ICollection<OrganizationUserUserDetails> ExistingUsers { get; set; }
|
||||||
|
public IEnumerable<OrganizationUserUserDetails> ExistingExternalUsers { get; set; }
|
||||||
|
public Dictionary<string, Guid> ExistingExternalUsersIdDict { get; set; }
|
||||||
|
|
||||||
|
}
|
@ -47,6 +47,20 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
|
|||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<OrganizationUserUserDetails> GetExistingExternalUsers(ICollection<OrganizationUserUserDetails> existingUsers)
|
||||||
|
{
|
||||||
|
return existingUsers
|
||||||
|
.Where(u => !string.IsNullOrWhiteSpace(u.ExternalId))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, Guid> GetExistingExternalUsersIdDict(ICollection<OrganizationUserUserDetails> existingUsers)
|
||||||
|
{
|
||||||
|
return existingUsers
|
||||||
|
.Where(u => !string.IsNullOrWhiteSpace(u.ExternalId))
|
||||||
|
.ToDictionary(u => u.ExternalId, u => u.Id);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ImportAsync(Guid organizationId,
|
public async Task ImportAsync(Guid organizationId,
|
||||||
IEnumerable<ImportedGroup> groups,
|
IEnumerable<ImportedGroup> groups,
|
||||||
IEnumerable<ImportedOrganizationUser> newUsers,
|
IEnumerable<ImportedOrganizationUser> newUsers,
|
||||||
@ -66,94 +80,30 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
|
|||||||
throw new BadRequestException("Organization cannot use directory syncing.");
|
throw new BadRequestException("Organization cannot use directory syncing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var newUsersSet = new HashSet<string>(newUsers?.Select(u => u.ExternalId) ?? new List<string>());
|
|
||||||
var existingUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
|
var existingUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
|
||||||
var existingExternalUsers = existingUsers.Where(u => !string.IsNullOrWhiteSpace(u.ExternalId)).ToList();
|
|
||||||
var existingExternalUsersIdDict = existingExternalUsers.ToDictionary(u => u.ExternalId, u => u.Id);
|
|
||||||
|
|
||||||
// Users
|
var syncData = new OrganizationUserSyncData
|
||||||
|
{
|
||||||
|
NewUsersSet = new HashSet<string>(newUsers?.Select(u => u.ExternalId) ?? new List<string>()),
|
||||||
|
ExistingUsers = existingUsers,
|
||||||
|
ExistingExternalUsers = GetExistingExternalUsers(existingUsers),
|
||||||
|
ExistingExternalUsersIdDict = GetExistingExternalUsersIdDict(existingUsers)
|
||||||
|
};
|
||||||
|
|
||||||
var events = new List<(OrganizationUserUserDetails ou, EventType e, DateTime? d)>();
|
var events = new List<(OrganizationUserUserDetails ou, EventType e, DateTime? d)>();
|
||||||
|
|
||||||
if (removeUserExternalIds?.Any() ?? false)
|
// Users
|
||||||
{
|
await RemoveExistingExternalUsers(removeUserExternalIds, events, syncData);
|
||||||
await RemoveExistingExternalUsers(removeUserExternalIds, events, existingExternalUsers, newUsersSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overwriteExisting)
|
if (overwriteExisting)
|
||||||
{
|
{
|
||||||
await OverwriteExisting(existingExternalUsers, existingExternalUsersIdDict, newUsersSet, events);
|
await OverwriteExisting(events, syncData);
|
||||||
}
|
|
||||||
|
|
||||||
if (newUsers?.Any() ?? false)
|
|
||||||
{
|
|
||||||
await RemoveExistingUsers(existingUsers, newUsers, existingExternalUsersIdDict, newUsersSet, organization, eventSystemUser);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await RemoveExistingUsers(existingUsers, newUsers, organization, eventSystemUser, syncData);
|
||||||
|
|
||||||
// Groups
|
// Groups
|
||||||
if (groups?.Any() ?? false)
|
await SyncGroups(organization, groups, eventSystemUser, syncData);
|
||||||
{
|
|
||||||
if (!organization.UseGroups)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization cannot use groups.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var groupsDict = groups.ToDictionary(g => g.Group.ExternalId);
|
|
||||||
var existingGroups = await _groupRepository.GetManyByOrganizationIdAsync(organizationId);
|
|
||||||
var existingExternalGroups = existingGroups
|
|
||||||
.Where(u => !string.IsNullOrWhiteSpace(u.ExternalId)).ToList();
|
|
||||||
var existingExternalGroupsDict = existingExternalGroups.ToDictionary(g => g.ExternalId);
|
|
||||||
|
|
||||||
var newGroups = groups
|
|
||||||
.Where(g => !existingExternalGroupsDict.ContainsKey(g.Group.ExternalId))
|
|
||||||
.Select(g => g.Group).ToList();
|
|
||||||
|
|
||||||
var savedGroups = new List<Group>();
|
|
||||||
foreach (var group in newGroups)
|
|
||||||
{
|
|
||||||
group.CreationDate = group.RevisionDate = DateTime.UtcNow;
|
|
||||||
|
|
||||||
savedGroups.Add(await _groupRepository.CreateAsync(group));
|
|
||||||
await UpdateUsersAsync(group, groupsDict[group.ExternalId].ExternalUserIds,
|
|
||||||
existingExternalUsersIdDict);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _eventService.LogGroupEventsAsync(
|
|
||||||
savedGroups.Select(g => (g, EventType.Group_Created, (EventSystemUser?)eventSystemUser, (DateTime?)DateTime.UtcNow)));
|
|
||||||
|
|
||||||
var updateGroups = existingExternalGroups
|
|
||||||
.Where(g => groupsDict.ContainsKey(g.ExternalId))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (updateGroups.Any())
|
|
||||||
{
|
|
||||||
var groupUsers = await _groupRepository.GetManyGroupUsersByOrganizationIdAsync(organizationId);
|
|
||||||
var existingGroupUsers = groupUsers
|
|
||||||
.GroupBy(gu => gu.GroupId)
|
|
||||||
.ToDictionary(g => g.Key, g => new HashSet<Guid>(g.Select(gr => gr.OrganizationUserId)));
|
|
||||||
|
|
||||||
foreach (var group in updateGroups)
|
|
||||||
{
|
|
||||||
var updatedGroup = groupsDict[group.ExternalId].Group;
|
|
||||||
if (group.Name != updatedGroup.Name)
|
|
||||||
{
|
|
||||||
group.RevisionDate = DateTime.UtcNow;
|
|
||||||
group.Name = updatedGroup.Name;
|
|
||||||
|
|
||||||
await _groupRepository.ReplaceAsync(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
await UpdateUsersAsync(group, groupsDict[group.ExternalId].ExternalUserIds,
|
|
||||||
existingExternalUsersIdDict,
|
|
||||||
existingGroupUsers.ContainsKey(group.Id) ? existingGroupUsers[group.Id] : null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
await _eventService.LogGroupEventsAsync(
|
|
||||||
updateGroups.Select(g => (g, EventType.Group_Updated, (EventSystemUser?)eventSystemUser, (DateTime?)DateTime.UtcNow)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _eventService.LogOrganizationUserEventsAsync(events.Select(e => (e.ou, e.e, eventSystemUser, e.d)));
|
await _eventService.LogOrganizationUserEventsAsync(events.Select(e => (e.ou, e.e, eventSystemUser, e.d)));
|
||||||
await _referenceEventService.RaiseEventAsync(
|
await _referenceEventService.RaiseEventAsync(
|
||||||
@ -181,13 +131,17 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
|
|||||||
private async Task RemoveExistingExternalUsers(
|
private async Task RemoveExistingExternalUsers(
|
||||||
IEnumerable<string> removeUserExternalIds,
|
IEnumerable<string> removeUserExternalIds,
|
||||||
List<(OrganizationUserUserDetails ou, EventType e, DateTime? d)> events,
|
List<(OrganizationUserUserDetails ou, EventType e, DateTime? d)> events,
|
||||||
IEnumerable<OrganizationUserUserDetails> existingExternalUsers,
|
OrganizationUserSyncData syncData
|
||||||
HashSet<string> newUsersSet
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var existingUsersDict = existingExternalUsers.ToDictionary(u => u.ExternalId);
|
if (!removeUserExternalIds.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingUsersDict = syncData.ExistingExternalUsers.ToDictionary(u => u.ExternalId);
|
||||||
var removeUsersSet = new HashSet<string>(removeUserExternalIds)
|
var removeUsersSet = new HashSet<string>(removeUserExternalIds)
|
||||||
.Except(newUsersSet)
|
.Except(syncData.NewUsersSet)
|
||||||
.Where(u => existingUsersDict.ContainsKey(u) && existingUsersDict[u].Type != OrganizationUserType.Owner)
|
.Where(u => existingUsersDict.ContainsKey(u) && existingUsersDict[u].Type != OrganizationUserType.Owner)
|
||||||
.Select(u => existingUsersDict[u]);
|
.Select(u => existingUsersDict[u]);
|
||||||
|
|
||||||
@ -203,12 +157,16 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
|
|||||||
private async Task RemoveExistingUsers(
|
private async Task RemoveExistingUsers(
|
||||||
IEnumerable<OrganizationUserUserDetails> existingUsers,
|
IEnumerable<OrganizationUserUserDetails> existingUsers,
|
||||||
IEnumerable<ImportedOrganizationUser> newUsers,
|
IEnumerable<ImportedOrganizationUser> newUsers,
|
||||||
IDictionary<string, Guid> existingExternalUsersIdDict,
|
|
||||||
HashSet<string> newUsersSet,
|
|
||||||
Organization organization,
|
Organization organization,
|
||||||
EventSystemUser eventSystemUser
|
EventSystemUser eventSystemUser,
|
||||||
|
OrganizationUserSyncData syncData
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
if (!newUsers.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Marry existing users
|
// Marry existing users
|
||||||
var existingUsersEmailsDict = existingUsers
|
var existingUsersEmailsDict = existingUsers
|
||||||
.Where(u => string.IsNullOrWhiteSpace(u.ExternalId))
|
.Where(u => string.IsNullOrWhiteSpace(u.ExternalId))
|
||||||
@ -224,14 +182,14 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
|
|||||||
{
|
{
|
||||||
orgUser.ExternalId = newUsersEmailsDict[user].ExternalId;
|
orgUser.ExternalId = newUsersEmailsDict[user].ExternalId;
|
||||||
usersToUpsert.Add(orgUser);
|
usersToUpsert.Add(orgUser);
|
||||||
existingExternalUsersIdDict.Add(orgUser.ExternalId, orgUser.Id);
|
syncData.ExistingExternalUsersIdDict.Add(orgUser.ExternalId, orgUser.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await _organizationUserRepository.UpsertManyAsync(usersToUpsert);
|
await _organizationUserRepository.UpsertManyAsync(usersToUpsert);
|
||||||
|
|
||||||
// Add new users
|
// Add new users
|
||||||
var existingUsersSet = new HashSet<string>(existingExternalUsersIdDict.Keys);
|
var existingUsersSet = new HashSet<string>(syncData.ExistingExternalUsersIdDict.Keys);
|
||||||
var usersToAdd = newUsersSet.Except(existingUsersSet).ToList();
|
var usersToAdd = syncData.NewUsersSet.Except(existingUsersSet).ToList();
|
||||||
|
|
||||||
var seatsAvailable = int.MaxValue;
|
var seatsAvailable = int.MaxValue;
|
||||||
var enoughSeatsAvailable = true;
|
var enoughSeatsAvailable = true;
|
||||||
@ -274,22 +232,20 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
|
|||||||
var invitedUsers = await _organizationService.InviteUsersAsync(organization.Id, invitingUserId: null, systemUser: eventSystemUser, userInvites);
|
var invitedUsers = await _organizationService.InviteUsersAsync(organization.Id, invitingUserId: null, systemUser: eventSystemUser, userInvites);
|
||||||
foreach (var invitedUser in invitedUsers)
|
foreach (var invitedUser in invitedUsers)
|
||||||
{
|
{
|
||||||
existingExternalUsersIdDict.Add(invitedUser.ExternalId, invitedUser.Id);
|
syncData.ExistingExternalUsersIdDict.Add(invitedUser.ExternalId, invitedUser.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OverwriteExisting(
|
private async Task OverwriteExisting(
|
||||||
IEnumerable<OrganizationUserUserDetails> existingExternalUsers,
|
List<(OrganizationUserUserDetails ou, EventType e, DateTime? d)> events,
|
||||||
IDictionary<string, Guid> existingExternalUsersIdDict,
|
OrganizationUserSyncData syncData
|
||||||
HashSet<string> newUsersSet,
|
|
||||||
List<(OrganizationUserUserDetails ou, EventType e, DateTime? d)> events
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Remove existing external users that are not in new user set
|
// Remove existing external users that are not in new user set
|
||||||
var usersToDelete = existingExternalUsers.Where(u =>
|
var usersToDelete = syncData.ExistingExternalUsers.Where(u =>
|
||||||
u.Type != OrganizationUserType.Owner &&
|
u.Type != OrganizationUserType.Owner &&
|
||||||
!newUsersSet.Contains(u.ExternalId) &&
|
!syncData.NewUsersSet.Contains(u.ExternalId) &&
|
||||||
existingExternalUsersIdDict.ContainsKey(u.ExternalId));
|
syncData.ExistingExternalUsersIdDict.ContainsKey(u.ExternalId));
|
||||||
await _organizationUserRepository.DeleteManyAsync(usersToDelete.Select(u => u.Id));
|
await _organizationUserRepository.DeleteManyAsync(usersToDelete.Select(u => u.Id));
|
||||||
events.AddRange(usersToDelete.Select(u => (
|
events.AddRange(usersToDelete.Select(u => (
|
||||||
u,
|
u,
|
||||||
@ -299,7 +255,80 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
|
|||||||
);
|
);
|
||||||
foreach (var deletedUser in usersToDelete)
|
foreach (var deletedUser in usersToDelete)
|
||||||
{
|
{
|
||||||
existingExternalUsersIdDict.Remove(deletedUser.ExternalId);
|
syncData.ExistingExternalUsersIdDict.Remove(deletedUser.ExternalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SyncGroups(Organization organization,
|
||||||
|
IEnumerable<ImportedGroup> groups,
|
||||||
|
EventSystemUser eventSystemUser,
|
||||||
|
OrganizationUserSyncData syncData
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!groups.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!organization.UseGroups)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization cannot use groups.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupsDict = groups.ToDictionary(g => g.Group.ExternalId);
|
||||||
|
var existingGroups = await _groupRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||||
|
var existingExternalGroups = existingGroups
|
||||||
|
.Where(u => !string.IsNullOrWhiteSpace(u.ExternalId)).ToList();
|
||||||
|
var existingExternalGroupsDict = existingExternalGroups.ToDictionary(g => g.ExternalId);
|
||||||
|
|
||||||
|
var newGroups = groups
|
||||||
|
.Where(g => !existingExternalGroupsDict.ContainsKey(g.Group.ExternalId))
|
||||||
|
.Select(g => g.Group).ToList();
|
||||||
|
|
||||||
|
var savedGroups = new List<Group>();
|
||||||
|
foreach (var group in newGroups)
|
||||||
|
{
|
||||||
|
group.CreationDate = group.RevisionDate = DateTime.UtcNow;
|
||||||
|
|
||||||
|
savedGroups.Add(await _groupRepository.CreateAsync(group));
|
||||||
|
await UpdateUsersAsync(group, groupsDict[group.ExternalId].ExternalUserIds,
|
||||||
|
syncData.ExistingExternalUsersIdDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _eventService.LogGroupEventsAsync(
|
||||||
|
savedGroups.Select(g => (g, EventType.Group_Created, (EventSystemUser?)eventSystemUser, (DateTime?)DateTime.UtcNow)));
|
||||||
|
|
||||||
|
var updateGroups = existingExternalGroups
|
||||||
|
.Where(g => groupsDict.ContainsKey(g.ExternalId))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (updateGroups.Any())
|
||||||
|
{
|
||||||
|
var groupUsers = await _groupRepository.GetManyGroupUsersByOrganizationIdAsync(organization.Id);
|
||||||
|
var existingGroupUsers = groupUsers
|
||||||
|
.GroupBy(gu => gu.GroupId)
|
||||||
|
.ToDictionary(g => g.Key, g => new HashSet<Guid>(g.Select(gr => gr.OrganizationUserId)));
|
||||||
|
|
||||||
|
foreach (var group in updateGroups)
|
||||||
|
{
|
||||||
|
var updatedGroup = groupsDict[group.ExternalId].Group;
|
||||||
|
if (group.Name != updatedGroup.Name)
|
||||||
|
{
|
||||||
|
group.RevisionDate = DateTime.UtcNow;
|
||||||
|
group.Name = updatedGroup.Name;
|
||||||
|
|
||||||
|
await _groupRepository.ReplaceAsync(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
await UpdateUsersAsync(group, groupsDict[group.ExternalId].ExternalUserIds,
|
||||||
|
syncData.ExistingExternalUsersIdDict,
|
||||||
|
existingGroupUsers.ContainsKey(group.Id) ? existingGroupUsers[group.Id] : null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await _eventService.LogGroupEventsAsync(
|
||||||
|
updateGroups.Select(g => (g, EventType.Group_Updated, (EventSystemUser?)eventSystemUser, (DateTime?)DateTime.UtcNow)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user