From 387dc2f59c1b41939fdc1a5c16406d11a5c705e8 Mon Sep 17 00:00:00 2001 From: Matt Portune <59324545+mportune-bw@users.noreply.github.com> Date: Fri, 21 Feb 2020 10:23:38 -0500 Subject: [PATCH] Beginning of policy support (#736) * Model & service support for policies * Formatting * Changes to match existing service and model patterns --- src/Core/Abstractions/IPolicyService.cs | 16 ++++++ src/Core/Enums/PolicyType.cs | 9 +++ src/Core/Models/Data/PolicyData.cs | 26 +++++++++ src/Core/Models/Domain/Policy.cs | 26 +++++++++ src/Core/Models/Response/PolicyResponse.cs | 14 +++++ src/Core/Models/Response/SyncResponse.cs | 1 + src/Core/Services/PolicyService.cs | 66 ++++++++++++++++++++++ src/Core/Services/SyncService.cs | 10 ++++ src/Core/Utilities/ServiceContainer.cs | 5 +- 9 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/Core/Abstractions/IPolicyService.cs create mode 100644 src/Core/Enums/PolicyType.cs create mode 100644 src/Core/Models/Data/PolicyData.cs create mode 100644 src/Core/Models/Domain/Policy.cs create mode 100644 src/Core/Models/Response/PolicyResponse.cs create mode 100644 src/Core/Services/PolicyService.cs diff --git a/src/Core/Abstractions/IPolicyService.cs b/src/Core/Abstractions/IPolicyService.cs new file mode 100644 index 000000000..6a870a4b0 --- /dev/null +++ b/src/Core/Abstractions/IPolicyService.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Bit.Core.Enums; +using Bit.Core.Models.Data; +using Bit.Core.Models.Domain; + +namespace Bit.Core.Abstractions +{ + public interface IPolicyService + { + void ClearCache(); + Task> GetAll(PolicyType? type); + Task Replace(Dictionary policies); + Task Clear(string userId); + } +} diff --git a/src/Core/Enums/PolicyType.cs b/src/Core/Enums/PolicyType.cs new file mode 100644 index 000000000..ebed30b32 --- /dev/null +++ b/src/Core/Enums/PolicyType.cs @@ -0,0 +1,9 @@ +namespace Bit.Core.Enums +{ + public enum PolicyType : byte + { + TwoFactorAuthentication = 0, + MasterPassword = 1, + PasswordGenerator = 2 + } +} diff --git a/src/Core/Models/Data/PolicyData.cs b/src/Core/Models/Data/PolicyData.cs new file mode 100644 index 000000000..bedee8a25 --- /dev/null +++ b/src/Core/Models/Data/PolicyData.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Bit.Core.Enums; +using Bit.Core.Models.Response; + +namespace Bit.Core.Models.Data +{ + public class PolicyData : Data + { + public PolicyData() { } + + public PolicyData(PolicyResponse response) + { + Id = response.Id; + OrganizationId = response.OrganizationId; + Type = response.Type; + Data = response.Data; + Enabled = response.Enabled; + } + + public string Id { get; set; } + public string OrganizationId { get; set; } + public PolicyType Type { get; set; } + public Dictionary Data { get; set; } + public bool Enabled { get; set; } + } +} diff --git a/src/Core/Models/Domain/Policy.cs b/src/Core/Models/Domain/Policy.cs new file mode 100644 index 000000000..470c92855 --- /dev/null +++ b/src/Core/Models/Domain/Policy.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Bit.Core.Enums; +using Bit.Core.Models.Data; + +namespace Bit.Core.Models.Domain +{ + public class Policy : Domain + { + public Policy() { } + + public Policy(PolicyData obj) + { + Id = obj.Id; + OrganizationId = obj.OrganizationId; + Type = obj.Type; + Data = obj.Data; + Enabled = obj.Enabled; + } + + public string Id { get; set; } + public string OrganizationId { get; set; } + public PolicyType Type { get; set; } + public Dictionary Data { get; set; } + public bool Enabled { get; set; } + } +} diff --git a/src/Core/Models/Response/PolicyResponse.cs b/src/Core/Models/Response/PolicyResponse.cs new file mode 100644 index 000000000..d4afbd987 --- /dev/null +++ b/src/Core/Models/Response/PolicyResponse.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Bit.Core.Enums; + +namespace Bit.Core.Models.Response +{ + public class PolicyResponse + { + public string Id { get; set; } + public string OrganizationId { get; set; } + public PolicyType Type { get; set; } + public Dictionary Data { get; set; } + public bool Enabled { get; set; } + } +} diff --git a/src/Core/Models/Response/SyncResponse.cs b/src/Core/Models/Response/SyncResponse.cs index 76e4fb46c..c5f60cf49 100644 --- a/src/Core/Models/Response/SyncResponse.cs +++ b/src/Core/Models/Response/SyncResponse.cs @@ -9,5 +9,6 @@ namespace Bit.Core.Models.Response public List Collections { get; set; } = new List(); public List Ciphers { get; set; } = new List(); public DomainsResponse Domains { get; set; } + public List Policies { get; set; } = new List(); } } diff --git a/src/Core/Services/PolicyService.cs b/src/Core/Services/PolicyService.cs new file mode 100644 index 000000000..297f4db08 --- /dev/null +++ b/src/Core/Services/PolicyService.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Bit.Core.Abstractions; +using Bit.Core.Enums; +using Bit.Core.Models.Data; +using Bit.Core.Models.Domain; + +namespace Bit.Core.Services +{ + public class PolicyService : IPolicyService + { + private const string Keys_PoliciesPrefix = "policies_{0}"; + + private readonly IStorageService _storageService; + private readonly IUserService _userService; + + private IEnumerable _policyCache; + + public PolicyService( + IStorageService storageService, + IUserService userService) + { + _storageService = storageService; + _userService = userService; + } + + public void ClearCache() + { + _policyCache = null; + } + + public async Task> GetAll(PolicyType? type) + { + if(_policyCache == null) + { + var userId = await _userService.GetUserIdAsync(); + var policies = await _storageService.GetAsync>( + string.Format(Keys_PoliciesPrefix, userId)); + _policyCache = policies.Select(p => new Policy(policies[p.Key])); + } + + if(type != null) + { + return _policyCache.Where(p => p.Type == type).ToList(); + } + else + { + return _policyCache; + } + } + + public async Task Replace(Dictionary policies) + { + var userId = await _userService.GetUserIdAsync(); + await _storageService.SaveAsync(string.Format(Keys_PoliciesPrefix, userId), policies); + _policyCache = null; + } + + public async Task Clear(string userId) + { + await _storageService.RemoveAsync(string.Format(Keys_PoliciesPrefix, userId)); + _policyCache = null; + } + } +} diff --git a/src/Core/Services/SyncService.cs b/src/Core/Services/SyncService.cs index 8b9ba4479..cfb175f84 100644 --- a/src/Core/Services/SyncService.cs +++ b/src/Core/Services/SyncService.cs @@ -23,6 +23,7 @@ namespace Bit.Core.Services private readonly ICollectionService _collectionService; private readonly IStorageService _storageService; private readonly IMessagingService _messagingService; + private readonly IPolicyService _policyService; private readonly Func _logoutCallbackAsync; public SyncService( @@ -35,6 +36,7 @@ namespace Bit.Core.Services ICollectionService collectionService, IStorageService storageService, IMessagingService messagingService, + IPolicyService policyService, Func logoutCallbackAsync) { _userService = userService; @@ -46,6 +48,7 @@ namespace Bit.Core.Services _collectionService = collectionService; _storageService = storageService; _messagingService = messagingService; + _policyService = policyService; _logoutCallbackAsync = logoutCallbackAsync; } @@ -101,6 +104,7 @@ namespace Bit.Core.Services await SyncCollectionsAsync(response.Collections); await SyncCiphersAsync(userId, response.Ciphers); await SyncSettingsAsync(userId, response.Domains); + await SyncPolicies(response.Policies); await SetLastSyncAsync(now); return SyncCompleted(true); } @@ -358,5 +362,11 @@ namespace Bit.Core.Services } await _settingsService.SetEquivalentDomainsAsync(eqDomains); } + + private async Task SyncPolicies(List response) + { + var policies = response.ToDictionary(p => p.Id, p => new PolicyData(p)); + await _policyService.Replace(policies); + } } } diff --git a/src/Core/Utilities/ServiceContainer.cs b/src/Core/Utilities/ServiceContainer.cs index aba5a061a..768fdab4e 100644 --- a/src/Core/Utilities/ServiceContainer.cs +++ b/src/Core/Utilities/ServiceContainer.cs @@ -47,8 +47,10 @@ namespace Bit.Core.Utilities searchService = new SearchService(cipherService); var lockService = new LockService(cryptoService, userService, platformUtilsService, storageService, folderService, cipherService, collectionService, searchService, messagingService, null); + var policyService = new PolicyService(storageService, userService); var syncService = new SyncService(userService, apiService, settingsService, folderService, - cipherService, cryptoService, collectionService, storageService, messagingService, (bool expired) => + cipherService, cryptoService, collectionService, storageService, messagingService, policyService, + (bool expired) => { messagingService.Send("logout", expired); return Task.FromResult(0); @@ -75,6 +77,7 @@ namespace Bit.Core.Utilities Register("folderService", folderService); Register("collectionService", collectionService); Register("searchService", searchService); + Register("policyService", policyService); Register("syncService", syncService); Register("lockService", lockService); Register("passwordGenerationService", passwordGenerationService);