mirror of
https://github.com/bitwarden/server.git
synced 2025-04-21 13:05:11 -05:00
PM-17921 change the GenerateAccessData method to process lists in parallel (#5552)
* PM-17921 change the GenerateAccessData method to process lists in parallel. * PM-17921 removing old method
This commit is contained in:
parent
01daad5942
commit
1cf9ff34c1
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using System.Collections.Concurrent;
|
||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
@ -59,13 +60,12 @@ public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery
|
|||||||
var orgItems = await _organizationCiphersQuery.GetAllOrganizationCiphers(request.OrganizationId);
|
var orgItems = await _organizationCiphersQuery.GetAllOrganizationCiphers(request.OrganizationId);
|
||||||
var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers);
|
var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers);
|
||||||
|
|
||||||
var memberAccessCipherDetails = GenerateAccessData(
|
var memberAccessCipherDetails = GenerateAccessDataParallel(
|
||||||
orgGroups,
|
orgGroups,
|
||||||
orgCollectionsWithAccess,
|
orgCollectionsWithAccess,
|
||||||
orgItems,
|
orgItems,
|
||||||
organizationUsersTwoFactorEnabled,
|
organizationUsersTwoFactorEnabled,
|
||||||
orgAbility
|
orgAbility);
|
||||||
);
|
|
||||||
|
|
||||||
return memberAccessCipherDetails;
|
return memberAccessCipherDetails;
|
||||||
}
|
}
|
||||||
@ -82,72 +82,72 @@ public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery
|
|||||||
/// <param name="organizationUsersTwoFactorEnabled">Organization users and two factor status</param>
|
/// <param name="organizationUsersTwoFactorEnabled">Organization users and two factor status</param>
|
||||||
/// <param name="orgAbility">Organization ability for account recovery status</param>
|
/// <param name="orgAbility">Organization ability for account recovery status</param>
|
||||||
/// <returns>List of the MemberAccessCipherDetailsModel</returns>;
|
/// <returns>List of the MemberAccessCipherDetailsModel</returns>;
|
||||||
private IEnumerable<MemberAccessCipherDetails> GenerateAccessData(
|
private IEnumerable<MemberAccessCipherDetails> GenerateAccessDataParallel(
|
||||||
ICollection<Group> orgGroups,
|
ICollection<Group> orgGroups,
|
||||||
ICollection<Tuple<Collection, CollectionAccessDetails>> orgCollectionsWithAccess,
|
ICollection<Tuple<Collection, CollectionAccessDetails>> orgCollectionsWithAccess,
|
||||||
IEnumerable<CipherOrganizationDetailsWithCollections> orgItems,
|
IEnumerable<CipherOrganizationDetailsWithCollections> orgItems,
|
||||||
IEnumerable<(OrganizationUserUserDetails user, bool twoFactorIsEnabled)> organizationUsersTwoFactorEnabled,
|
IEnumerable<(OrganizationUserUserDetails user, bool twoFactorIsEnabled)> organizationUsersTwoFactorEnabled,
|
||||||
OrganizationAbility orgAbility)
|
OrganizationAbility orgAbility)
|
||||||
{
|
{
|
||||||
var orgUsers = organizationUsersTwoFactorEnabled.Select(x => x.user);
|
var orgUsers = organizationUsersTwoFactorEnabled.Select(x => x.user).ToList();
|
||||||
// Create a dictionary to lookup the group names later.
|
|
||||||
var groupNameDictionary = orgGroups.ToDictionary(x => x.Id, x => x.Name);
|
var groupNameDictionary = orgGroups.ToDictionary(x => x.Id, x => x.Name);
|
||||||
|
|
||||||
// Get collections grouped and into a dictionary for counts
|
|
||||||
var collectionItems = orgItems
|
var collectionItems = orgItems
|
||||||
.SelectMany(x => x.CollectionIds,
|
.SelectMany(x => x.CollectionIds,
|
||||||
(cipher, collectionId) => new { Cipher = cipher, CollectionId = collectionId })
|
(cipher, collectionId) => new { Cipher = cipher, CollectionId = collectionId })
|
||||||
.GroupBy(y => y.CollectionId,
|
.GroupBy(y => y.CollectionId,
|
||||||
(key, ciphers) => new { CollectionId = key, Ciphers = ciphers });
|
(key, ciphers) => new { CollectionId = key, Ciphers = ciphers });
|
||||||
var itemLookup = collectionItems.ToDictionary(x => x.CollectionId.ToString(), x => x.Ciphers.Select(c => c.Cipher.Id.ToString()));
|
var itemLookup = collectionItems.ToDictionary(x => x.CollectionId.ToString(), x => x.Ciphers.Select(c => c.Cipher.Id.ToString()).ToList());
|
||||||
|
|
||||||
// Loop through the org users and populate report and access data
|
var memberAccessCipherDetails = new ConcurrentBag<MemberAccessCipherDetails>();
|
||||||
var memberAccessCipherDetails = new List<MemberAccessCipherDetails>();
|
|
||||||
foreach (var user in orgUsers)
|
Parallel.ForEach(orgUsers, user =>
|
||||||
{
|
{
|
||||||
var groupAccessDetails = new List<MemberAccessDetails>();
|
var groupAccessDetails = new List<MemberAccessDetails>();
|
||||||
var userCollectionAccessDetails = new List<MemberAccessDetails>();
|
var userCollectionAccessDetails = new List<MemberAccessDetails>();
|
||||||
|
|
||||||
foreach (var tCollect in orgCollectionsWithAccess)
|
foreach (var tCollect in orgCollectionsWithAccess)
|
||||||
{
|
{
|
||||||
var hasItems = itemLookup.TryGetValue(tCollect.Item1.Id.ToString(), out var items);
|
if (itemLookup.TryGetValue(tCollect.Item1.Id.ToString(), out var items))
|
||||||
var collectionCiphers = hasItems ? items.Select(x => x) : null;
|
|
||||||
|
|
||||||
var itemCounts = hasItems ? collectionCiphers.Count() : 0;
|
|
||||||
if (tCollect.Item2.Groups.Count() > 0)
|
|
||||||
{
|
{
|
||||||
|
var itemCounts = items.Count;
|
||||||
|
|
||||||
var groupDetails = tCollect.Item2.Groups.Where((tCollectGroups) => user.Groups.Contains(tCollectGroups.Id)).Select(x =>
|
if (tCollect.Item2.Groups.Any())
|
||||||
new MemberAccessDetails
|
{
|
||||||
{
|
var groupDetails = tCollect.Item2.Groups
|
||||||
CollectionId = tCollect.Item1.Id,
|
.Where(tCollectGroups => user.Groups.Contains(tCollectGroups.Id))
|
||||||
CollectionName = tCollect.Item1.Name,
|
.Select(x => new MemberAccessDetails
|
||||||
GroupId = x.Id,
|
{
|
||||||
GroupName = groupNameDictionary[x.Id],
|
CollectionId = tCollect.Item1.Id,
|
||||||
ReadOnly = x.ReadOnly,
|
CollectionName = tCollect.Item1.Name,
|
||||||
HidePasswords = x.HidePasswords,
|
GroupId = x.Id,
|
||||||
Manage = x.Manage,
|
GroupName = groupNameDictionary[x.Id],
|
||||||
ItemCount = itemCounts,
|
ReadOnly = x.ReadOnly,
|
||||||
CollectionCipherIds = items
|
HidePasswords = x.HidePasswords,
|
||||||
});
|
Manage = x.Manage,
|
||||||
|
ItemCount = itemCounts,
|
||||||
|
CollectionCipherIds = items
|
||||||
|
});
|
||||||
|
|
||||||
groupAccessDetails.AddRange(groupDetails);
|
groupAccessDetails.AddRange(groupDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All collections assigned to users and their permissions
|
if (tCollect.Item2.Users.Any())
|
||||||
if (tCollect.Item2.Users.Count() > 0)
|
{
|
||||||
{
|
var userCollectionDetails = tCollect.Item2.Users
|
||||||
var userCollectionDetails = tCollect.Item2.Users.Where((tCollectUser) => tCollectUser.Id == user.Id).Select(x =>
|
.Where(tCollectUser => tCollectUser.Id == user.Id)
|
||||||
new MemberAccessDetails
|
.Select(x => new MemberAccessDetails
|
||||||
{
|
{
|
||||||
CollectionId = tCollect.Item1.Id,
|
CollectionId = tCollect.Item1.Id,
|
||||||
CollectionName = tCollect.Item1.Name,
|
CollectionName = tCollect.Item1.Name,
|
||||||
ReadOnly = x.ReadOnly,
|
ReadOnly = x.ReadOnly,
|
||||||
HidePasswords = x.HidePasswords,
|
HidePasswords = x.HidePasswords,
|
||||||
Manage = x.Manage,
|
Manage = x.Manage,
|
||||||
ItemCount = itemCounts,
|
ItemCount = itemCounts,
|
||||||
CollectionCipherIds = items
|
CollectionCipherIds = items
|
||||||
});
|
});
|
||||||
userCollectionAccessDetails.AddRange(userCollectionDetails);
|
|
||||||
|
userCollectionAccessDetails.AddRange(userCollectionDetails);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,6 @@ public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery
|
|||||||
UserName = user.Name,
|
UserName = user.Name,
|
||||||
Email = user.Email,
|
Email = user.Email,
|
||||||
TwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == user.Id).twoFactorIsEnabled,
|
TwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == user.Id).twoFactorIsEnabled,
|
||||||
// Both the user's ResetPasswordKey must be set and the organization can UseResetPassword
|
|
||||||
AccountRecoveryEnabled = !string.IsNullOrEmpty(user.ResetPasswordKey) && orgAbility.UseResetPassword,
|
AccountRecoveryEnabled = !string.IsNullOrEmpty(user.ResetPasswordKey) && orgAbility.UseResetPassword,
|
||||||
UserGuid = user.Id,
|
UserGuid = user.Id,
|
||||||
UsesKeyConnector = user.UsesKeyConnector
|
UsesKeyConnector = user.UsesKeyConnector
|
||||||
@ -169,9 +168,8 @@ public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery
|
|||||||
userAccessDetails.AddRange(userGroups);
|
userAccessDetails.AddRange(userGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
// There can be edge cases where groups don't have a collection
|
|
||||||
var groupsWithoutCollections = user.Groups.Where(x => !userAccessDetails.Any(y => x == y.GroupId));
|
var groupsWithoutCollections = user.Groups.Where(x => !userAccessDetails.Any(y => x == y.GroupId));
|
||||||
if (groupsWithoutCollections.Count() > 0)
|
if (groupsWithoutCollections.Any())
|
||||||
{
|
{
|
||||||
var emptyGroups = groupsWithoutCollections.Select(x => new MemberAccessDetails
|
var emptyGroups = groupsWithoutCollections.Select(x => new MemberAccessDetails
|
||||||
{
|
{
|
||||||
@ -189,20 +187,20 @@ public class MemberAccessCipherDetailsQuery : IMemberAccessCipherDetailsQuery
|
|||||||
}
|
}
|
||||||
report.AccessDetails = userAccessDetails;
|
report.AccessDetails = userAccessDetails;
|
||||||
|
|
||||||
var userCiphers =
|
var userCiphers = report.AccessDetails
|
||||||
report.AccessDetails
|
.Where(x => x.ItemCount > 0)
|
||||||
.Where(x => x.ItemCount > 0)
|
.SelectMany(y => y.CollectionCipherIds)
|
||||||
.SelectMany(y => y.CollectionCipherIds)
|
.Distinct();
|
||||||
.Distinct();
|
|
||||||
report.CipherIds = userCiphers;
|
report.CipherIds = userCiphers;
|
||||||
report.TotalItemCount = userCiphers.Count();
|
report.TotalItemCount = userCiphers.Count();
|
||||||
|
|
||||||
// Distinct items only
|
|
||||||
var distinctItems = report.AccessDetails.Where(x => x.CollectionId.HasValue).Select(x => x.CollectionId).Distinct();
|
var distinctItems = report.AccessDetails.Where(x => x.CollectionId.HasValue).Select(x => x.CollectionId).Distinct();
|
||||||
report.CollectionsCount = distinctItems.Count();
|
report.CollectionsCount = distinctItems.Count();
|
||||||
report.GroupsCount = report.AccessDetails.Select(x => x.GroupId).Where(y => y.HasValue).Distinct().Count();
|
report.GroupsCount = report.AccessDetails.Select(x => x.GroupId).Where(y => y.HasValue).Distinct().Count();
|
||||||
|
|
||||||
memberAccessCipherDetails.Add(report);
|
memberAccessCipherDetails.Add(report);
|
||||||
}
|
});
|
||||||
|
|
||||||
return memberAccessCipherDetails;
|
return memberAccessCipherDetails;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user