diff --git a/src/Console/Program.cs b/src/Console/Program.cs index f8bfc622..aeba9747 100644 --- a/src/Console/Program.cs +++ b/src/Console/Program.cs @@ -323,6 +323,17 @@ namespace Bit.Console { config.GSuite.SecretFile = parameters["f"]; } + + if(parameters.ContainsKey("d")) + { + config.GSuite.Domain = parameters["d"]; + config.GSuite.Customer = null; + } + else if(parameters.ContainsKey("c")) + { + config.GSuite.Customer = parameters["c"]; + config.GSuite.Domain = null; + } } else { @@ -442,6 +453,13 @@ namespace Bit.Console { config.GSuite.SecretFile = input.Trim(); } + Con.Write("Domain [{0}]: ", config.GSuite.Domain); + input = Con.ReadLine(); + if(!string.IsNullOrEmpty(input)) + { + config.GSuite.Domain = input.Trim(); + config.GSuite.Customer = null; + } } else { diff --git a/src/Core/Models/GSuiteConfiguration.cs b/src/Core/Models/GSuiteConfiguration.cs index 0560477b..2c874e33 100644 --- a/src/Core/Models/GSuiteConfiguration.cs +++ b/src/Core/Models/GSuiteConfiguration.cs @@ -3,5 +3,7 @@ public class GSuiteConfiguration { public string SecretFile { get; set; } = "client_secret.json"; + public string Customer { get; set; } + public string Domain { get; set; } = "yourcompany.com"; } } \ No newline at end of file diff --git a/src/Core/Services/GSuiteDirectoryService.cs b/src/Core/Services/GSuiteDirectoryService.cs index 52600732..100b9a09 100644 --- a/src/Core/Services/GSuiteDirectoryService.cs +++ b/src/Core/Services/GSuiteDirectoryService.cs @@ -8,6 +8,9 @@ using Google.Apis.Auth.OAuth2; using System.IO; using Bit.Core.Utilities; using System.Linq; +using Google.Apis.Admin.Directory.directory_v1.Data; +using System.Threading; +using Google.Apis.Util.Store; namespace Bit.Core.Services { @@ -18,12 +21,28 @@ namespace Bit.Core.Services private GSuiteDirectoryService() { - GoogleCredential creds; - using(var stream = new FileStream(SettingsService.Instance.Server.GSuite.SecretFile, FileMode.Open)) + //GoogleCredential creds; + UserCredential creds; + + var secretFilePath = Path.Combine(Constants.BaseStoragePath, SettingsService.Instance.Server.GSuite.SecretFile); + using(var stream = new FileStream(secretFilePath, FileMode.Open, FileAccess.Read)) { - creds = GoogleCredential.FromStream(stream).CreateScoped( + var scopes = new List + { DirectoryService.Scope.AdminDirectoryUserReadonly, - DirectoryService.Scope.AdminDirectoryGroupReadonly); + DirectoryService.Scope.AdminDirectoryGroupReadonly, + DirectoryService.Scope.AdminDirectoryGroupMemberReadonly + }; + + //creds = GoogleCredential.FromStream(stream).CreateScoped(scopes); + + var credsPath = Path.Combine(Constants.BaseStoragePath, "gsuite_credentials"); + creds = GoogleWebAuthorizationBroker.AuthorizeAsync( + GoogleClientSecrets.Load(stream).Secrets, + scopes, + "user", + CancellationToken.None, + new FileDataStore(credsPath, true)).Result; } _service = new DirectoryService(new BaseClientService.Initializer @@ -80,12 +99,124 @@ namespace Bit.Core.Services private async Task> GetGroupsAsync(bool force) { - return new List(); + var entries = new List(); + + var request = _service.Groups.List(); + request.Domain = SettingsService.Instance.Server.GSuite.Domain; + request.Customer = SettingsService.Instance.Server.GSuite.Customer; + var groups = await request.ExecuteAsync(); + + if(groups.GroupsValue != null) + { + foreach(var group in groups.GroupsValue) + { + // TODO: Group filter? + + var entry = await BuildGroupAsync(group); + entries.Add(entry); + } + } + + return entries; + } + + private async static Task BuildGroupAsync(Group group) + { + var entry = new GroupEntry + { + ReferenceId = group.Id, + ExternalId = group.Id, + Name = group.Name + }; + + var memberRequest = _service.Members.List(group.Id); + var members = await memberRequest.ExecuteAsync(); + + if(members.MembersValue != null) + { + foreach(var member in members.MembersValue) + { + if(!member.Role.Equals("member", StringComparison.InvariantCultureIgnoreCase) || + !member.Status.Equals("active", StringComparison.InvariantCultureIgnoreCase)) + { + continue; + } + + if(member.Type.Equals("user", StringComparison.InvariantCultureIgnoreCase)) + { + entry.UserMemberExternalIds.Add(member.Id); + } + else if(member.Type.Equals("group", StringComparison.InvariantCultureIgnoreCase)) + { + entry.GroupMemberReferenceIds.Add(member.Id); + } + } + } + + return entry; } private async Task> GetUsersAsync(bool force) { - return new List(); + var entries = new List(); + + var request = _service.Users.List(); + request.Domain = SettingsService.Instance.Server.GSuite.Domain; + request.Customer = SettingsService.Instance.Server.GSuite.Customer; + request.Query = SettingsService.Instance.Sync.UserFilter; + var users = await request.ExecuteAsync(); + + if(users.UsersValue != null) + { + foreach(var user in users.UsersValue) + { + var entry = BuildUser(user, false); + if(entry != null) + { + entries.Add(entry); + } + } + } + + var deletedRequest = _service.Users.List(); + request.Domain = SettingsService.Instance.Server.GSuite.Domain; + request.Customer = SettingsService.Instance.Server.GSuite.Customer; + request.Query = SettingsService.Instance.Sync.UserFilter; + request.ShowDeleted = "true"; + var deletedUsers = await request.ExecuteAsync(); + + if(deletedUsers.UsersValue != null) + { + foreach(var user in deletedUsers.UsersValue) + { + var entry = BuildUser(user, true); + if(entry != null) + { + entries.Add(entry); + } + } + } + + return entries; + } + + private UserEntry BuildUser(User user, bool deleted) + { + var entry = new UserEntry + { + ReferenceId = user.Id, + ExternalId = user.Id, + Email = user.PrimaryEmail, + Disabled = user.Suspended.GetValueOrDefault(false), + Deleted = deleted + }; + + if(string.IsNullOrWhiteSpace(entry.Email) && !entry.Deleted) + { + return null; + } + + return entry; } } }