diff --git a/src/Console/Program.cs b/src/Console/Program.cs
index 7606b0db..197c3ec8 100644
--- a/src/Console/Program.cs
+++ b/src/Console/Program.cs
@@ -313,8 +313,8 @@ namespace Bit.Console
break;
}
Con.Write("Type [{0}]: ", currentType);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
switch(input)
{
@@ -330,32 +330,32 @@ namespace Bit.Console
}
Con.Write("Address [{0}]: ", config.Address);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.Address = input;
}
Con.Write("Port [{0}]: ", config.Port);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.Port = input;
}
Con.Write("Path [{0}]: ", config.Path);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.Path = input;
}
Con.Write("Username [{0}]: ", config.Username);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.Username = input;
}
Con.Write("Password: ");
input = ReadSecureLine();
- if(!string.IsNullOrWhiteSpace(input))
+ if(!string.IsNullOrEmpty(input))
{
config.Password = new EncryptedData(input);
input = null;
@@ -385,7 +385,8 @@ namespace Bit.Console
private static Task ConfigSyncAsync()
{
- var config = Core.Services.SettingsService.Instance.Sync ?? new SyncConfiguration();
+ var config = Core.Services.SettingsService.Instance.Sync ??
+ new SyncConfiguration(Core.Services.SettingsService.Instance.Server.Type);
if(_usingArgs)
{
@@ -443,57 +444,63 @@ namespace Bit.Console
string input;
Con.Write("Sync groups? [{0}]: ", config.SyncGroups ? "y" : "n");
- input = Con.ReadLine().Trim().ToLower();
- config.SyncGroups = input == "y" || input == "yes" || string.IsNullOrWhiteSpace(input);
+ input = Con.ReadLine().ToLower();
+ if(!string.IsNullOrEmpty(input))
+ {
+ config.SyncGroups = input == "y" || input == "yes";
+ }
if(config.SyncGroups)
{
Con.Write("Group filter [{0}]: ", config.GroupFilter);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.GroupFilter = input;
}
Con.Write("Group name attribute [{0}]: ", config.GroupNameAttribute);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.GroupNameAttribute = input;
}
}
Con.Write("Sync users? [{0}]: ", config.SyncUsers ? "y" : "n");
- input = Con.ReadLine().Trim().ToLower();
- config.SyncUsers = input == "y" || input == "yes" || string.IsNullOrWhiteSpace(input);
+ input = Con.ReadLine().ToLower();
+ if(!string.IsNullOrEmpty(input))
+ {
+ config.SyncUsers = input == "y" || input == "yes";
+ }
if(config.SyncUsers)
{
Con.Write("User filter [{0}]: ", config.UserFilter);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.UserFilter = input;
}
Con.Write("User email attribute [{0}]: ", config.UserEmailAttribute);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.GroupNameAttribute = input;
}
}
Con.Write("Member Of Attribute [{0}]: ", config.MemberAttribute);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.MemberAttribute = input;
}
Con.Write("Creation Attribute [{0}]: ", config.CreationDateAttribute);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.CreationDateAttribute = input;
}
Con.Write("Changed Attribute [{0}]: ", config.RevisionDateAttribute);
- input = Con.ReadLine().Trim();
- if(!string.IsNullOrWhiteSpace(input))
+ input = Con.ReadLine();
+ if(!string.IsNullOrEmpty(input))
{
config.RevisionDateAttribute = input;
}
diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj
index 884c6f8b..4885741b 100644
--- a/src/Core/Core.csproj
+++ b/src/Core/Core.csproj
@@ -73,6 +73,7 @@
+
diff --git a/src/Core/Enums/DirectoryType.cs b/src/Core/Enums/DirectoryType.cs
index ee064a19..b25222a9 100644
--- a/src/Core/Enums/DirectoryType.cs
+++ b/src/Core/Enums/DirectoryType.cs
@@ -9,7 +9,7 @@ namespace Bit.Core.Enums
public enum DirectoryType : byte
{
ActiveDirectory = 0,
- AzureActiveCirectory = 1,
+ AzureActiveDirectory = 1,
Other = 2
}
}
diff --git a/src/Core/Models/SyncConfiguration.cs b/src/Core/Models/SyncConfiguration.cs
index 92e7e3f5..f955e13a 100644
--- a/src/Core/Models/SyncConfiguration.cs
+++ b/src/Core/Models/SyncConfiguration.cs
@@ -1,4 +1,5 @@
-using Newtonsoft.Json;
+using Bit.Core.Enums;
+using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
@@ -10,17 +11,38 @@ namespace Bit.Core.Models
{
public class SyncConfiguration
{
+ public SyncConfiguration() { }
+
+ public SyncConfiguration(DirectoryType type)
+ {
+ switch(type)
+ {
+ case DirectoryType.ActiveDirectory:
+ MemberAttribute = "memberOf";
+ CreationDateAttribute = "whenCreated";
+ RevisionDateAttribute = "whenChanged";
+ UserEmailPrefixAttribute = "sAMAccountName";
+ break;
+ case DirectoryType.AzureActiveDirectory:
+ break;
+ case DirectoryType.Other:
+ break;
+ default:
+ break;
+ }
+ }
+
public string GroupFilter { get; set; } = "(&(objectClass=group))";
public string UserFilter { get; set; } = "(&(objectClass=person))";
public bool SyncGroups { get; set; } = true;
public bool SyncUsers { get; set; } = true;
- public string MemberAttribute { get; set; } = "memberOf";
+ public string MemberAttribute { get; set; } = "member";
public string GroupNameAttribute { get; set; } = "name";
public string UserEmailAttribute { get; set; } = "mail";
public bool EmailPrefixSuffix { get; set; } = false;
- public string UserEmailPrefixAttribute { get; set; } = "sAMAccountName";
+ public string UserEmailPrefixAttribute { get; set; } = "cn";
public string UserEmailSuffix { get; set; } = "@companyname.com";
- public string CreationDateAttribute { get; set; } = "whenCreated";
- public string RevisionDateAttribute { get; set; } = "whenChanged";
+ public string CreationDateAttribute { get; set; }
+ public string RevisionDateAttribute { get; set; }
}
}
diff --git a/src/Core/Services/SettingsService.cs b/src/Core/Services/SettingsService.cs
index bea3c8e3..91b0ada3 100644
--- a/src/Core/Services/SettingsService.cs
+++ b/src/Core/Services/SettingsService.cs
@@ -166,6 +166,32 @@ namespace Bit.Core.Services
}
}
+ public DateTime? LastGroupSyncDate
+ {
+ get
+ {
+ return Settings.LastGroupSyncDate;
+ }
+ set
+ {
+ Settings.LastGroupSyncDate = value;
+ SaveSettings();
+ }
+ }
+
+ public DateTime? LastUserSyncDate
+ {
+ get
+ {
+ return Settings.LastUserSyncDate;
+ }
+ set
+ {
+ Settings.LastUserSyncDate = value;
+ SaveSettings();
+ }
+ }
+
public class SettingsModel
{
public string ApiBaseUrl { get; set; }
@@ -175,6 +201,8 @@ namespace Bit.Core.Services
public ServerConfiguration Server { get; set; }
public SyncConfiguration Sync { get; set; }
public Organization Organization { get; set; }
+ public DateTime? LastGroupSyncDate { get; set; }
+ public DateTime? LastUserSyncDate { get; set; }
}
}
}
diff --git a/src/Core/Utilities/Extensions.cs b/src/Core/Utilities/Extensions.cs
new file mode 100644
index 00000000..5137c552
--- /dev/null
+++ b/src/Core/Utilities/Extensions.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Bit.Core.Utilities
+{
+ public static class Extensions
+ {
+ private const string GeneralizedTimeFormat = "yyyyMMddHHmmss.f'Z'";
+
+ public static DateTime ToDateTime(this string generalizedTimeString)
+ {
+ return DateTime.ParseExact(generalizedTimeString, GeneralizedTimeFormat, CultureInfo.InvariantCulture);
+ }
+
+ public static string ToGeneralizedTimeUTC(this DateTime date)
+ {
+ return date.ToString("yyyyMMddHHmmss.f'Z'");
+ }
+ }
+}
diff --git a/src/Core/Utilities/Sync.cs b/src/Core/Utilities/Sync.cs
index 2d1229b4..403de437 100644
--- a/src/Core/Utilities/Sync.cs
+++ b/src/Core/Utilities/Sync.cs
@@ -40,6 +40,8 @@ namespace Bit.Core.Utilities
};
}
+ var now = DateTime.UtcNow;
+
List groups = null;
if(SettingsService.Instance.Sync.SyncGroups)
{
@@ -58,6 +60,16 @@ namespace Bit.Core.Utilities
var response = await ApiService.Instance.PostImportAsync(request);
if(response.Succeeded)
{
+ if(SettingsService.Instance.Sync.SyncGroups)
+ {
+ SettingsService.Instance.LastGroupSyncDate = now;
+ }
+
+ if(SettingsService.Instance.Sync.SyncUsers)
+ {
+ SettingsService.Instance.LastUserSyncDate = now;
+ }
+
return new SyncResult
{
Success = true,
@@ -100,6 +112,16 @@ namespace Bit.Core.Utilities
var entry = SettingsService.Instance.Server.GetDirectoryEntry();
var filter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.GroupFilter) ? null :
SettingsService.Instance.Sync.GroupFilter;
+
+ if(!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();
@@ -180,6 +202,16 @@ namespace Bit.Core.Utilities
var entry = SettingsService.Instance.Server.GetDirectoryEntry();
var filter = string.IsNullOrWhiteSpace(SettingsService.Instance.Sync.UserFilter) ? null :
SettingsService.Instance.Sync.UserFilter;
+
+ if(!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();