diff --git a/src/Console/Program.cs b/src/Console/Program.cs
index b586c2a3..fe88473b 100644
--- a/src/Console/Program.cs
+++ b/src/Console/Program.cs
@@ -555,7 +555,7 @@ namespace Bit.Console
}
Con.WriteLine("Syncing...");
- var result = await Sync.SyncAllAsync(force);
+ var result = await Sync.SyncAllAsync(force, true);
if(result.Success)
{
@@ -595,7 +595,7 @@ namespace Bit.Console
Con.WriteLine("Querying...");
Con.WriteLine();
- var result = await Sync.GatherAsync(force);
+ var result = await Sync.SyncAllAsync(force, false);
if(result.Success)
{
Con.WriteLine("Groups:");
diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj
index 4885741b..cb41e8fc 100644
--- a/src/Core/Core.csproj
+++ b/src/Core/Core.csproj
@@ -68,6 +68,9 @@
+
+
+
diff --git a/src/Core/Services/AzureDirectoryService.cs b/src/Core/Services/AzureDirectoryService.cs
new file mode 100644
index 00000000..b54c57d8
--- /dev/null
+++ b/src/Core/Services/AzureDirectoryService.cs
@@ -0,0 +1,32 @@
+using Bit.Core.Models;
+using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+namespace Bit.Core.Services
+{
+ public class AzureDirectoryService : IDirectoryService
+ {
+ private static AzureDirectoryService _instance;
+
+ private AzureDirectoryService() { }
+
+ public static IDirectoryService Instance
+ {
+ get
+ {
+ if(_instance == null)
+ {
+ _instance = new AzureDirectoryService();
+ }
+
+ return _instance;
+ }
+ }
+
+ public Task, List>> GetEntriesAsync(bool force = false)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Core/Services/IDirectoryService.cs b/src/Core/Services/IDirectoryService.cs
new file mode 100644
index 00000000..02f4f93f
--- /dev/null
+++ b/src/Core/Services/IDirectoryService.cs
@@ -0,0 +1,14 @@
+using Bit.Core.Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Bit.Core.Services
+{
+ public interface IDirectoryService
+ {
+ Task, List>> GetEntriesAsync(bool force = false);
+ }
+}
diff --git a/src/Core/Services/LdapDirectoryService.cs b/src/Core/Services/LdapDirectoryService.cs
new file mode 100644
index 00000000..27795cb3
--- /dev/null
+++ b/src/Core/Services/LdapDirectoryService.cs
@@ -0,0 +1,235 @@
+using Bit.Core.Models;
+using Bit.Core.Utilities;
+using System;
+using System.Collections.Generic;
+using System.DirectoryServices;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Bit.Core.Services
+{
+ public class LdapDirectoryService : IDirectoryService
+ {
+ private static LdapDirectoryService _instance;
+
+ private LdapDirectoryService() { }
+
+ public static IDirectoryService Instance
+ {
+ get
+ {
+ if(_instance == null)
+ {
+ _instance = new LdapDirectoryService();
+ }
+
+ return _instance;
+ }
+ }
+
+ public async Task, List>> GetEntriesAsync(bool force = false)
+ {
+ if(!AuthService.Instance.Authenticated || !AuthService.Instance.OrganizationSet)
+ {
+ throw new ApplicationException("Not logged in or have an org set.");
+ }
+
+ if(SettingsService.Instance.Server == null)
+ {
+ throw new ApplicationException("No configuration for directory server.");
+ }
+
+ if(SettingsService.Instance.Sync == null)
+ {
+ throw new ApplicationException("No configuration for sync.");
+ }
+
+ List groups = null;
+ if(SettingsService.Instance.Sync.SyncGroups)
+ {
+ groups = await GetGroupsAsync(force);
+ }
+
+ List users = null;
+ if(SettingsService.Instance.Sync.SyncUsers)
+ {
+ users = await GetUsersAsync(force);
+ }
+
+ return new Tuple, List>(groups, users);
+ }
+
+ private static Task> GetGroupsAsync(bool force = false)
+ {
+ if(!SettingsService.Instance.Sync.SyncGroups)
+ {
+ throw new ApplicationException("Not configured to sync groups.");
+ }
+
+ if(SettingsService.Instance.Server == null)
+ {
+ throw new ApplicationException("No configuration for directory server.");
+ }
+
+ if(SettingsService.Instance.Sync == null)
+ {
+ throw new ApplicationException("No configuration for sync.");
+ }
+
+ if(!AuthService.Instance.Authenticated)
+ {
+ throw new ApplicationException("Not authenticated.");
+ }
+
+ var entry = SettingsService.Instance.Server.GetDirectoryEntry();
+ var filter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.GroupFilter) ? null :
+ SettingsService.Instance.Sync.GroupFilter;
+
+ if(!force && !string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.RevisionDateAttribute) &&
+ SettingsService.Instance.LastGroupSyncDate.HasValue)
+ {
+ filter = string.Format("(&{0}({1}>{2}))",
+ filter != null ? string.Format("({0})", filter) : string.Empty,
+ SettingsService.Instance.Sync.RevisionDateAttribute,
+ SettingsService.Instance.LastGroupSyncDate.Value.ToGeneralizedTimeUTC());
+ }
+
+ var searcher = new DirectorySearcher(entry, filter);
+ var result = searcher.FindAll();
+
+ var groups = new List();
+ foreach(SearchResult item in result)
+ {
+ var group = new GroupEntry
+ {
+ DistinguishedName = new Uri(item.Path).Segments?.LastOrDefault()
+ };
+
+ if(group.DistinguishedName == null)
+ {
+ continue;
+ }
+
+ // 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 Task> GetUsersAsync(bool force = false)
+ {
+ if(!SettingsService.Instance.Sync.SyncUsers)
+ {
+ throw new ApplicationException("Not configured to sync users.");
+ }
+
+ if(SettingsService.Instance.Server == null)
+ {
+ throw new ApplicationException("No configuration for directory server.");
+ }
+
+ if(SettingsService.Instance.Sync == null)
+ {
+ throw new ApplicationException("No configuration for sync.");
+ }
+
+ if(!AuthService.Instance.Authenticated)
+ {
+ throw new ApplicationException("Not authenticated.");
+ }
+
+ var entry = SettingsService.Instance.Server.GetDirectoryEntry();
+ var filter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.UserFilter) ? null :
+ SettingsService.Instance.Sync.UserFilter;
+
+ if(!force && !string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.RevisionDateAttribute) &&
+ SettingsService.Instance.LastUserSyncDate.HasValue)
+ {
+ filter = string.Format("(&{0}({1}>{2}))",
+ filter != null ? string.Format("({0})", filter) : string.Empty,
+ SettingsService.Instance.Sync.RevisionDateAttribute,
+ SettingsService.Instance.LastUserSyncDate.Value.ToGeneralizedTimeUTC());
+ }
+
+ var searcher = new DirectorySearcher(entry, filter);
+ var result = searcher.FindAll();
+
+ var users = new List();
+ foreach(SearchResult item in result)
+ {
+ var user = new UserEntry
+ {
+ DistinguishedName = new Uri(item.Path).Segments?.LastOrDefault()
+ };
+
+ if(user.DistinguishedName == null)
+ {
+ continue;
+ }
+
+ // Email
+ if(SettingsService.Instance.Sync.EmailPrefixSuffix &&
+ item.Properties.Contains(SettingsService.Instance.Sync.UserEmailPrefixAttribute) &&
+ item.Properties[SettingsService.Instance.Sync.UserEmailPrefixAttribute].Count > 0 &&
+ !string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.UserEmailSuffix))
+ {
+ user.Email = string.Concat(
+ item.Properties[SettingsService.Instance.Sync.UserEmailPrefixAttribute][0].ToString(),
+ SettingsService.Instance.Sync.UserEmailSuffix).ToLowerInvariant();
+ }
+ else if(item.Properties.Contains(SettingsService.Instance.Sync.UserEmailAttribute) &&
+ item.Properties[SettingsService.Instance.Sync.UserEmailAttribute].Count > 0)
+ {
+ user.Email = item.Properties[SettingsService.Instance.Sync.UserEmailAttribute][0]
+ .ToString()
+ .ToLowerInvariant();
+ }
+ else
+ {
+ continue;
+ }
+
+ // Dates
+ user.CreationDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.CreationDateAttribute);
+ user.RevisionDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.RevisionDateAttribute);
+
+ users.Add(user);
+ }
+
+ return Task.FromResult(users);
+ }
+ }
+}
diff --git a/src/Core/Utilities/Sync.cs b/src/Core/Utilities/Sync.cs
index 2aa48b7f..2f2eea43 100644
--- a/src/Core/Utilities/Sync.cs
+++ b/src/Core/Utilities/Sync.cs
@@ -1,9 +1,7 @@
using Bit.Core.Models;
using Bit.Core.Services;
using System;
-using System.Collections;
using System.Collections.Generic;
-using System.DirectoryServices;
using System.Linq;
using System.Threading.Tasks;
@@ -11,268 +9,76 @@ namespace Bit.Core.Utilities
{
public static class Sync
{
- public static async Task SyncAllAsync(bool force = false)
+ public static async Task SyncAllAsync(bool force = false, bool sendToServer = true)
{
- var now = DateTime.UtcNow;
- var gatherResult = await GatherAsync(force);
- if(!gatherResult.Success)
+ try
{
- return gatherResult;
- }
+ var now = DateTime.UtcNow;
+ var entriesResult = await GetDirectoryService().GetEntriesAsync(force);
+ var groups = entriesResult.Item1;
+ var users = entriesResult.Item2;
- var request = new ImportRequest(gatherResult.Groups, gatherResult.Users);
- var response = await ApiService.Instance.PostImportAsync(request);
- if(response.Succeeded)
- {
- if(SettingsService.Instance.Sync.SyncGroups)
+ FlattenGroupsToUsers(groups, null, groups, users);
+
+ if(!sendToServer)
{
- SettingsService.Instance.LastGroupSyncDate = now;
- }
-
- if(SettingsService.Instance.Sync.SyncUsers)
- {
- SettingsService.Instance.LastUserSyncDate = now;
- }
-
- return new SyncResult
- {
- Success = true,
- Groups = gatherResult.Groups,
- Users = gatherResult.Users
- };
- }
- else
- {
- return new SyncResult
- {
- Success = false,
- ErrorMessage = response.Errors.FirstOrDefault()?.Message
- };
- }
- }
-
- public static async Task GatherAsync(bool force = false)
- {
- if(!AuthService.Instance.Authenticated || !AuthService.Instance.OrganizationSet)
- {
- return new SyncResult
- {
- Success = false,
- ErrorMessage = "Not logged in or have an org set."
- };
- }
-
- if(SettingsService.Instance.Server == null)
- {
- return new SyncResult
- {
- Success = false,
- ErrorMessage = "No configuration for directory server."
- };
- }
-
- if(SettingsService.Instance.Sync == null)
- {
- return new SyncResult
- {
- Success = false,
- ErrorMessage = "No configuration for sync."
- };
- }
-
- List groups = null;
- if(SettingsService.Instance.Sync.SyncGroups)
- {
- groups = await GetGroupsAsync(force);
- }
-
- List users = null;
- if(SettingsService.Instance.Sync.SyncUsers)
- {
- users = await GetUsersAsync(force);
- }
-
- FlattenGroupsToUsers(groups, null, groups, users);
-
- return new SyncResult
- {
- Success = true,
- Groups = groups,
- Users = users
- };
- }
-
- private static Task> GetGroupsAsync(bool force = false)
- {
- if(!SettingsService.Instance.Sync.SyncGroups)
- {
- throw new ApplicationException("Not configured to sync groups.");
- }
-
- if(SettingsService.Instance.Server == null)
- {
- throw new ApplicationException("No configuration for directory server.");
- }
-
- if(SettingsService.Instance.Sync == null)
- {
- throw new ApplicationException("No configuration for sync.");
- }
-
- if(!AuthService.Instance.Authenticated)
- {
- throw new ApplicationException("Not authenticated.");
- }
-
- var entry = SettingsService.Instance.Server.GetDirectoryEntry();
- var filter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.GroupFilter) ? null :
- SettingsService.Instance.Sync.GroupFilter;
-
- if(!force && !string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.RevisionDateAttribute) &&
- SettingsService.Instance.LastGroupSyncDate.HasValue)
- {
- filter = string.Format("(&{0}({1}>{2}))",
- filter != null ? string.Format("({0})", filter) : string.Empty,
- SettingsService.Instance.Sync.RevisionDateAttribute,
- SettingsService.Instance.LastGroupSyncDate.Value.ToGeneralizedTimeUTC());
- }
-
- var searcher = new DirectorySearcher(entry, filter);
- var result = searcher.FindAll();
-
- var groups = new List();
- foreach(SearchResult item in result)
- {
- var group = new GroupEntry
- {
- DistinguishedName = new Uri(item.Path).Segments?.LastOrDefault()
- };
-
- if(group.DistinguishedName == null)
- {
- continue;
- }
-
- // 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])
+ return new SyncResult
{
- var memberDn = member.ToString();
- if(!group.Members.Contains(memberDn))
- {
- group.Members.Add(memberDn);
- }
+ Success = true,
+ Groups = groups,
+ Users = users
+ };
+ }
+
+ var request = new ImportRequest(groups, users);
+ var response = await ApiService.Instance.PostImportAsync(request);
+ if(response.Succeeded)
+ {
+ if(SettingsService.Instance.Sync.SyncGroups)
+ {
+ SettingsService.Instance.LastGroupSyncDate = now;
}
- }
- groups.Add(group);
- }
+ if(SettingsService.Instance.Sync.SyncUsers)
+ {
+ SettingsService.Instance.LastUserSyncDate = now;
+ }
- return Task.FromResult(groups);
- }
-
- private static Task> GetUsersAsync(bool force = false)
- {
- if(!SettingsService.Instance.Sync.SyncUsers)
- {
- throw new ApplicationException("Not configured to sync users.");
- }
-
- if(SettingsService.Instance.Server == null)
- {
- throw new ApplicationException("No configuration for directory server.");
- }
-
- if(SettingsService.Instance.Sync == null)
- {
- throw new ApplicationException("No configuration for sync.");
- }
-
- if(!AuthService.Instance.Authenticated)
- {
- throw new ApplicationException("Not authenticated.");
- }
-
- var entry = SettingsService.Instance.Server.GetDirectoryEntry();
- var filter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.UserFilter) ? null :
- SettingsService.Instance.Sync.UserFilter;
-
- if(!force && !string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.RevisionDateAttribute) &&
- SettingsService.Instance.LastUserSyncDate.HasValue)
- {
- filter = string.Format("(&{0}({1}>{2}))",
- filter != null ? string.Format("({0})", filter) : string.Empty,
- SettingsService.Instance.Sync.RevisionDateAttribute,
- SettingsService.Instance.LastUserSyncDate.Value.ToGeneralizedTimeUTC());
- }
-
- var searcher = new DirectorySearcher(entry, filter);
- var result = searcher.FindAll();
-
- var users = new List();
- foreach(SearchResult item in result)
- {
- var user = new UserEntry
- {
- DistinguishedName = new Uri(item.Path).Segments?.LastOrDefault()
- };
-
- if(user.DistinguishedName == null)
- {
- continue;
- }
-
- // Email
- if(SettingsService.Instance.Sync.EmailPrefixSuffix &&
- item.Properties.Contains(SettingsService.Instance.Sync.UserEmailPrefixAttribute) &&
- item.Properties[SettingsService.Instance.Sync.UserEmailPrefixAttribute].Count > 0 &&
- !string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.UserEmailSuffix))
- {
- user.Email = string.Concat(
- item.Properties[SettingsService.Instance.Sync.UserEmailPrefixAttribute][0].ToString(),
- SettingsService.Instance.Sync.UserEmailSuffix).ToLowerInvariant();
- }
- else if(item.Properties.Contains(SettingsService.Instance.Sync.UserEmailAttribute) &&
- item.Properties[SettingsService.Instance.Sync.UserEmailAttribute].Count > 0)
- {
- user.Email = item.Properties[SettingsService.Instance.Sync.UserEmailAttribute][0]
- .ToString()
- .ToLowerInvariant();
+ return new SyncResult
+ {
+ Success = true,
+ Groups = groups,
+ Users = users
+ };
}
else
{
- continue;
+ return new SyncResult
+ {
+ Success = false,
+ ErrorMessage = response.Errors.FirstOrDefault()?.Message
+ };
}
-
- // Dates
- user.CreationDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.CreationDateAttribute);
- user.RevisionDate = item.Properties.ParseDateTime(SettingsService.Instance.Sync.RevisionDateAttribute);
-
- users.Add(user);
}
+ catch (ApplicationException e)
+ {
+ return new SyncResult
+ {
+ Success = false,
+ ErrorMessage = e.Message
+ };
+ }
+ }
- return Task.FromResult(users);
+ private static IDirectoryService GetDirectoryService()
+ {
+ switch(SettingsService.Instance.Server.Type)
+ {
+ case Enums.DirectoryType.AzureActiveDirectory:
+ return AzureDirectoryService.Instance;
+ default:
+ return LdapDirectoryService.Instance;
+ }
}
private static void FlattenGroupsToUsers(List currentGroups, List currentGroupsUsers,