1
0
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:
Kyle Spearrin
2017-05-17 00:01:03 -04:00
parent f3ff7e0e73
commit 19a02b3732
6 changed files with 162 additions and 95 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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; }

View File

@@ -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

View File

@@ -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)

View File

@@ -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);
}
}