mirror of
https://github.com/bitwarden/directory-connector
synced 2026-01-03 00:53:14 +00:00
handle group changes more appropriately
This commit is contained in:
@@ -702,7 +702,7 @@ namespace Bit.Console
|
||||
foreach(var group in result.Groups)
|
||||
{
|
||||
Con.WriteLine(" {0} - {1}", group.Name, group.ExternalId);
|
||||
foreach(var user in group.Users)
|
||||
foreach(var user in group.UserMemberExternalIds)
|
||||
{
|
||||
Con.WriteLine(" {0}", user);
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace Bit.Core.Models
|
||||
public class GroupEntry : Entry
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public HashSet<string> Members { get; set; } = new HashSet<string>();
|
||||
public HashSet<string> Users { get; set; } = new HashSet<string>();
|
||||
public HashSet<string> UserMemberExternalIds { get; set; } = new HashSet<string>();
|
||||
public HashSet<string> GroupMemberReferenceIds { get; set; } = new HashSet<string>();
|
||||
}
|
||||
|
||||
public class UserEntry : Entry
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Bit.Core.Models
|
||||
{
|
||||
Name = entry.Name;
|
||||
ExternalId = entry.ExternalId;
|
||||
Users = entry.Users;
|
||||
Users = entry.UserMemberExternalIds;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
@@ -88,29 +88,47 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
var entries = new List<GroupEntry>();
|
||||
|
||||
var groupRequest = _graphClient.Groups.Delta();
|
||||
IGroupDeltaCollectionPage groups = null;
|
||||
|
||||
var changedGroupIds = new List<string>();
|
||||
if(SettingsService.Instance.GroupDeltaToken != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var delataRequest = groupRequest.Request();
|
||||
var delataRequest = _graphClient.Groups.Delta().Request();
|
||||
delataRequest.QueryOptions.Add(new QueryOption("$deltatoken", SettingsService.Instance.GroupDeltaToken));
|
||||
groups = await delataRequest.GetAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
groups = null;
|
||||
var groupsDelta = await delataRequest.GetAsync();
|
||||
|
||||
while(true)
|
||||
{
|
||||
changedGroupIds.AddRange(groupsDelta.Select(g => g.Id));
|
||||
|
||||
if(groupsDelta.NextPageRequest == null)
|
||||
{
|
||||
object deltaLink;
|
||||
if(groupsDelta.AdditionalData.TryGetValue("@odata.deltaLink", out deltaLink))
|
||||
{
|
||||
var deltaUriQuery = new Uri(deltaLink.ToString()).ParseQueryString();
|
||||
if(deltaUriQuery["$deltatoken"] != null)
|
||||
{
|
||||
SettingsService.Instance.GroupDeltaToken = deltaUriQuery["$deltatoken"];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
groupsDelta = await groupsDelta.NextPageRequest.GetAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if(groups == null)
|
||||
if(!changedGroupIds.Any())
|
||||
{
|
||||
groups = await groupRequest.Request().GetAsync();
|
||||
return entries;
|
||||
}
|
||||
|
||||
var groups = await _graphClient.Groups.Request().GetAsync();
|
||||
while(true)
|
||||
{
|
||||
foreach(var group in groups)
|
||||
@@ -125,7 +143,14 @@ namespace Bit.Core.Services
|
||||
var members = await _graphClient.Groups[group.Id].Members.Request().Select("id").GetAsync();
|
||||
foreach(var member in members)
|
||||
{
|
||||
entry.Members.Add(member.Id);
|
||||
if(member is User)
|
||||
{
|
||||
entry.UserMemberExternalIds.Add(member.Id);
|
||||
}
|
||||
else if(member is Group)
|
||||
{
|
||||
entry.GroupMemberReferenceIds.Add(member.Id);
|
||||
}
|
||||
}
|
||||
|
||||
entries.Add(entry);
|
||||
@@ -133,15 +158,6 @@ namespace Bit.Core.Services
|
||||
|
||||
if(groups.NextPageRequest == null)
|
||||
{
|
||||
object deltaLink;
|
||||
if(groups.AdditionalData.TryGetValue("@odata.deltaLink", out deltaLink))
|
||||
{
|
||||
var deltaUriQuery = new Uri(deltaLink.ToString()).ParseQueryString();
|
||||
if(deltaUriQuery["$deltatoken"] != null)
|
||||
{
|
||||
SettingsService.Instance.GroupDeltaToken = deltaUriQuery["$deltatoken"];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -83,11 +83,14 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
var entry = SettingsService.Instance.Server.Ldap.GetDirectoryEntry();
|
||||
var filter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.GroupFilter) ? null :
|
||||
|
||||
string originalFilter;
|
||||
var filter = originalFilter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.GroupFilter) ? null :
|
||||
SettingsService.Instance.Sync.GroupFilter;
|
||||
|
||||
if(!force && !string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.RevisionDateAttribute) &&
|
||||
SettingsService.Instance.LastGroupSyncDate.HasValue)
|
||||
var searchSinceRevision = !force && !string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.RevisionDateAttribute) &&
|
||||
SettingsService.Instance.LastGroupSyncDate.HasValue;
|
||||
if(searchSinceRevision)
|
||||
{
|
||||
filter = string.Format("(&{0}({1}>{2}))",
|
||||
filter != null ? string.Format("({0})", filter) : string.Empty,
|
||||
@@ -98,68 +101,124 @@ namespace Bit.Core.Services
|
||||
var searcher = new DirectorySearcher(entry, filter);
|
||||
var result = searcher.FindAll();
|
||||
|
||||
var initialSearchGroupIds = new List<string>();
|
||||
foreach(SearchResult item in result)
|
||||
{
|
||||
initialSearchGroupIds.Add(new Uri(item.Path).Segments?.LastOrDefault());
|
||||
}
|
||||
|
||||
if(searchSinceRevision && !initialSearchGroupIds.Any())
|
||||
{
|
||||
return Task.FromResult(new List<GroupEntry>());
|
||||
}
|
||||
else if(searchSinceRevision)
|
||||
{
|
||||
searcher = new DirectorySearcher(entry, originalFilter);
|
||||
result = searcher.FindAll();
|
||||
}
|
||||
|
||||
var userFilter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.UserFilter) ? null :
|
||||
SettingsService.Instance.Sync.UserFilter;
|
||||
var userSearcher = new DirectorySearcher(entry, userFilter);
|
||||
var userResult = userSearcher.FindAll();
|
||||
|
||||
var userIdsDict = MakeIdIndex(userResult);
|
||||
|
||||
var groups = new List<GroupEntry>();
|
||||
foreach(SearchResult item in result)
|
||||
{
|
||||
var group = new GroupEntry
|
||||
{
|
||||
ReferenceId = new Uri(item.Path).Segments?.LastOrDefault()
|
||||
};
|
||||
|
||||
if(group.ReferenceId == null)
|
||||
var group = BuildGroup(item, userIdsDict);
|
||||
if(group == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// External Id
|
||||
if(item.Properties.Contains("objectGUID") && item.Properties["objectGUID"].Count > 0)
|
||||
{
|
||||
group.ExternalId = item.Properties["objectGUID"][0].ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
group.ExternalId = group.ReferenceId;
|
||||
}
|
||||
|
||||
// Name
|
||||
if(item.Properties.Contains(SettingsService.Instance.Sync.GroupNameAttribute) &&
|
||||
item.Properties[SettingsService.Instance.Sync.GroupNameAttribute].Count > 0)
|
||||
{
|
||||
group.Name = item.Properties[SettingsService.Instance.Sync.GroupNameAttribute][0].ToString();
|
||||
}
|
||||
else if(item.Properties.Contains("cn") && item.Properties["cn"].Count > 0)
|
||||
{
|
||||
group.Name = item.Properties["cn"][0].ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dates
|
||||
group.CreationDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.CreationDateAttribute);
|
||||
group.RevisionDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.RevisionDateAttribute);
|
||||
|
||||
// Members
|
||||
if(item.Properties.Contains(SettingsService.Instance.Sync.MemberAttribute) &&
|
||||
item.Properties[SettingsService.Instance.Sync.MemberAttribute].Count > 0)
|
||||
{
|
||||
foreach(var member in item.Properties[SettingsService.Instance.Sync.MemberAttribute])
|
||||
{
|
||||
var memberDn = member.ToString();
|
||||
if(!group.Members.Contains(memberDn))
|
||||
{
|
||||
group.Members.Add(memberDn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groups.Add(group);
|
||||
}
|
||||
|
||||
return Task.FromResult(groups);
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> MakeIdIndex(SearchResultCollection result)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
foreach(SearchResult item in result)
|
||||
{
|
||||
var referenceId = new Uri(item.Path).Segments?.LastOrDefault();
|
||||
var externalId = referenceId;
|
||||
|
||||
if(item.Properties.Contains("objectGUID") && item.Properties["objectGUID"].Count > 0)
|
||||
{
|
||||
externalId = item.Properties["objectGUID"][0].ToString();
|
||||
}
|
||||
|
||||
dict.Add(referenceId, externalId);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
private static GroupEntry BuildGroup(SearchResult item, Dictionary<string, string> userIndex)
|
||||
{
|
||||
var group = new GroupEntry
|
||||
{
|
||||
ReferenceId = new Uri(item.Path).Segments?.LastOrDefault()
|
||||
};
|
||||
|
||||
if(group.ReferenceId == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// External Id
|
||||
if(item.Properties.Contains("objectGUID") && item.Properties["objectGUID"].Count > 0)
|
||||
{
|
||||
group.ExternalId = item.Properties["objectGUID"][0].ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
group.ExternalId = group.ReferenceId;
|
||||
}
|
||||
|
||||
// Name
|
||||
if(item.Properties.Contains(SettingsService.Instance.Sync.GroupNameAttribute) &&
|
||||
item.Properties[SettingsService.Instance.Sync.GroupNameAttribute].Count > 0)
|
||||
{
|
||||
group.Name = item.Properties[SettingsService.Instance.Sync.GroupNameAttribute][0].ToString();
|
||||
}
|
||||
else if(item.Properties.Contains("cn") && item.Properties["cn"].Count > 0)
|
||||
{
|
||||
group.Name = item.Properties["cn"][0].ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Dates
|
||||
group.CreationDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.CreationDateAttribute);
|
||||
group.RevisionDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.RevisionDateAttribute);
|
||||
|
||||
// Members
|
||||
if(item.Properties.Contains(SettingsService.Instance.Sync.MemberAttribute) &&
|
||||
item.Properties[SettingsService.Instance.Sync.MemberAttribute].Count > 0)
|
||||
{
|
||||
foreach(var member in item.Properties[SettingsService.Instance.Sync.MemberAttribute])
|
||||
{
|
||||
var memberDn = member.ToString();
|
||||
if(userIndex.ContainsKey(memberDn) && !group.UserMemberExternalIds.Contains(userIndex[memberDn]))
|
||||
{
|
||||
group.UserMemberExternalIds.Add(userIndex[memberDn]);
|
||||
}
|
||||
else if(!group.GroupMemberReferenceIds.Contains(memberDn))
|
||||
{
|
||||
group.GroupMemberReferenceIds.Add(memberDn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
private static Task<List<UserEntry>> GetUsersAsync(bool force = false)
|
||||
{
|
||||
if(!SettingsService.Instance.Sync.SyncUsers)
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Bit.Core.Utilities
|
||||
var groups = entriesResult.Item1;
|
||||
var users = entriesResult.Item2;
|
||||
|
||||
FlattenUsersToGroups(groups, null, groups, users);
|
||||
FlattenUsersToGroups(groups, null, groups);
|
||||
|
||||
if(!sendToServer || (groups.Count == 0 && users.Count == 0))
|
||||
{
|
||||
@@ -92,29 +92,21 @@ namespace Bit.Core.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
private static void FlattenUsersToGroups(List<GroupEntry> currentGroups, List<UserEntry> currentGroupsUsers,
|
||||
List<GroupEntry> allGroups, List<UserEntry> allUsers)
|
||||
private static void FlattenUsersToGroups(List<GroupEntry> currentGroups, List<string> currentGroupsUsers,
|
||||
List<GroupEntry> allGroups)
|
||||
{
|
||||
foreach(var group in currentGroups)
|
||||
{
|
||||
var groupsInThisGroup = allGroups.Where(g => group.Members.Contains(g.ReferenceId)).ToList();
|
||||
var usersInThisGroup = allUsers.Where(u => group.Members.Contains(u.ReferenceId)).ToList();
|
||||
|
||||
foreach(var user in usersInThisGroup)
|
||||
{
|
||||
if(!group.Users.Contains(user.ExternalId))
|
||||
{
|
||||
group.Users.Add(user.ExternalId);
|
||||
}
|
||||
}
|
||||
var groupsInThisGroup = allGroups.Where(g => group.GroupMemberReferenceIds.Contains(g.ReferenceId)).ToList();
|
||||
var usersInThisGroup = group.UserMemberExternalIds.ToList();
|
||||
|
||||
if(currentGroupsUsers != null)
|
||||
{
|
||||
foreach(var user in currentGroupsUsers)
|
||||
foreach(var id in currentGroupsUsers)
|
||||
{
|
||||
if(!group.Users.Contains(user.ExternalId))
|
||||
if(!group.UserMemberExternalIds.Contains(id))
|
||||
{
|
||||
group.Users.Add(user.ExternalId);
|
||||
group.UserMemberExternalIds.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +114,7 @@ namespace Bit.Core.Utilities
|
||||
}
|
||||
|
||||
// Recurse it
|
||||
FlattenUsersToGroups(groupsInThisGroup, usersInThisGroup, allGroups, allUsers);
|
||||
FlattenUsersToGroups(groupsInThisGroup, usersInThisGroup, allGroups);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user