From 3d50133fa8d31142d75303691eda6386a483f861 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 24 Apr 2019 14:52:26 -0400 Subject: [PATCH] search service --- src/App/App.csproj | 3 + src/App/Pages/Vault/CiphersPage.xaml | 38 +++++++++ src/App/Pages/Vault/CiphersPage.xaml.cs | 54 +++++++++++++ src/App/Pages/Vault/CiphersPageViewModel.cs | 50 ++++++++++++ src/Core/Abstractions/ISearchService.cs | 17 ++++ src/Core/Services/CipherService.cs | 17 +++- src/Core/Services/SearchService.cs | 90 +++++++++++++++++++++ src/Core/Utilities/ServiceContainer.cs | 4 +- 8 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 src/App/Pages/Vault/CiphersPage.xaml create mode 100644 src/App/Pages/Vault/CiphersPage.xaml.cs create mode 100644 src/App/Pages/Vault/CiphersPageViewModel.cs create mode 100644 src/Core/Abstractions/ISearchService.cs create mode 100644 src/Core/Services/SearchService.cs diff --git a/src/App/App.csproj b/src/App/App.csproj index ea6f7c8d8..77ac609dc 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -28,6 +28,9 @@ GeneratorPage.xaml + + CiphersPage.xaml + ViewPage.xaml diff --git a/src/App/Pages/Vault/CiphersPage.xaml b/src/App/Pages/Vault/CiphersPage.xaml new file mode 100644 index 000000000..bf8083c67 --- /dev/null +++ b/src/App/Pages/Vault/CiphersPage.xaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/App/Pages/Vault/CiphersPage.xaml.cs b/src/App/Pages/Vault/CiphersPage.xaml.cs new file mode 100644 index 000000000..711cd4101 --- /dev/null +++ b/src/App/Pages/Vault/CiphersPage.xaml.cs @@ -0,0 +1,54 @@ +using Bit.Core.Abstractions; +using Bit.Core.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +namespace Bit.App.Pages +{ + public partial class ViewPage : ContentPage + { + private readonly IBroadcasterService _broadcasterService; + private ViewPageViewModel _vm; + + public ViewPage(string cipherId) + { + InitializeComponent(); + _broadcasterService = ServiceContainer.Resolve("broadcasterService"); + _vm = BindingContext as ViewPageViewModel; + _vm.Page = this; + _vm.CipherId = cipherId; + } + + protected async override void OnAppearing() + { + base.OnAppearing(); + _broadcasterService.Subscribe(nameof(ViewPage), async (message) => + { + if(message.Command == "syncCompleted") + { + var data = message.Data as Dictionary; + if(data.ContainsKey("successfully")) + { + var success = data["successfully"] as bool?; + if(success.HasValue && success.Value) + { + await _vm.LoadAsync(); + } + } + } + }); + await _vm.LoadAsync(); + } + + protected override void OnDisappearing() + { + base.OnDisappearing(); + _broadcasterService.Unsubscribe(nameof(ViewPage)); + } + } +} diff --git a/src/App/Pages/Vault/CiphersPageViewModel.cs b/src/App/Pages/Vault/CiphersPageViewModel.cs new file mode 100644 index 000000000..18c59a216 --- /dev/null +++ b/src/App/Pages/Vault/CiphersPageViewModel.cs @@ -0,0 +1,50 @@ +using Bit.App.Abstractions; +using Bit.App.Resources; +using Bit.Core.Abstractions; +using Bit.Core.Models.View; +using Bit.Core.Utilities; +using System.Threading.Tasks; + +namespace Bit.App.Pages +{ + public class CiphersPageViewModel : BaseViewModel + { + private readonly IDeviceActionService _deviceActionService; + private readonly ICipherService _cipherService; + private readonly IUserService _userService; + private CipherView _cipher; + private bool _canAccessPremium; + + public CiphersPageViewModel() + { + _deviceActionService = ServiceContainer.Resolve("deviceActionService"); + _cipherService = ServiceContainer.Resolve("cipherService"); + _userService = ServiceContainer.Resolve("userService"); + + PageTitle = AppResources.ViewItem; + } + + public string CipherId { get; set; } + public CipherView Cipher + { + get => _cipher; + set => SetProperty(ref _cipher, value); + } + public bool CanAccessPremium + { + get => _canAccessPremium; + set => SetProperty(ref _canAccessPremium, value); + } + + public async Task LoadAsync() + { + // TODO: Cleanup + + var cipher = await _cipherService.GetAsync(CipherId); + Cipher = await cipher.DecryptAsync(); + CanAccessPremium = await _userService.CanAccessPremiumAsync(); + + // TODO: Totp + } + } +} diff --git a/src/Core/Abstractions/ISearchService.cs b/src/Core/Abstractions/ISearchService.cs new file mode 100644 index 000000000..49c1729b1 --- /dev/null +++ b/src/Core/Abstractions/ISearchService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Bit.Core.Models.View; + +namespace Bit.Core.Abstractions +{ + public interface ISearchService + { + void ClearIndex(); + Task IndexCiphersAsync(); + bool IsSearchable(string query); + Task> SearchCiphersAsync(string query, Func filter = null, + List ciphers = null); + List SearchCiphersBasic(List ciphers, string query); + } +} \ No newline at end of file diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs index f62ccc603..e8a98601b 100644 --- a/src/Core/Services/CipherService.cs +++ b/src/Core/Services/CipherService.cs @@ -32,6 +32,7 @@ namespace Bit.Core.Services private readonly IApiService _apiService; private readonly IStorageService _storageService; private readonly II18nService _i18nService; + private readonly Func _searchService; private Dictionary> _domainMatchBlacklist = new Dictionary> { ["google.com"] = new HashSet { "script.google.com" } @@ -45,7 +46,8 @@ namespace Bit.Core.Services ISettingsService settingsService, IApiService apiService, IStorageService storageService, - II18nService i18nService) + II18nService i18nService, + Func searchService) { _cryptoService = cryptoService; _userService = userService; @@ -53,6 +55,7 @@ namespace Bit.Core.Services _apiService = apiService; _storageService = storageService; _i18nService = i18nService; + _searchService = searchService; } private List DecryptedCipherCache @@ -65,7 +68,17 @@ namespace Bit.Core.Services _decryptedCipherCache.Clear(); } _decryptedCipherCache = value; - // TODO: update search index + if(_searchService != null) + { + if(value == null) + { + _searchService().ClearIndex(); + } + else + { + _searchService().IndexCiphersAsync(); + } + } } } diff --git a/src/Core/Services/SearchService.cs b/src/Core/Services/SearchService.cs new file mode 100644 index 000000000..bfb2af85e --- /dev/null +++ b/src/Core/Services/SearchService.cs @@ -0,0 +1,90 @@ +using Bit.Core.Abstractions; +using Bit.Core.Models.View; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Bit.Core.Services +{ + public class SearchService : ISearchService + { + private readonly ICipherService _cipherService; + + public SearchService( + ICipherService cipherService) + { + _cipherService = cipherService; + } + + public void ClearIndex() + { + // TODO + } + + public bool IsSearchable(string query) + { + return true; + } + + public Task IndexCiphersAsync() + { + // TODO + return Task.FromResult(0); + } + + public async Task> SearchCiphersAsync(string query, Func filter = null, + List ciphers = null) + { + var results = new List(); + if(query != null) + { + query = query.Trim().ToLower(); + } + if(query == string.Empty) + { + query = null; + } + if(ciphers == null) + { + ciphers = await _cipherService.GetAllDecryptedAsync(); + } + if(filter != null) + { + ciphers = ciphers.Where(filter).ToList(); + } + if(!IsSearchable(query)) + { + return ciphers; + } + + return SearchCiphersBasic(ciphers, query); + // TODO: advanced searching with index + } + + public List SearchCiphersBasic(List ciphers, string query) + { + query = query.Trim().ToLower(); + return ciphers.Where(c => + { + if(c.Name?.ToLower().Contains(query) ?? false) + { + return true; + } + if(query.Length >= 8 && c.Id.StartsWith(query)) + { + return true; + } + if(c.SubTitle?.ToLower().Contains(query) ?? false) + { + return true; + } + if(c.Login?.Uri?.ToLower()?.Contains(query) ?? false) + { + return true; + } + return false; + }).ToList(); + } + } +} diff --git a/src/Core/Utilities/ServiceContainer.cs b/src/Core/Utilities/ServiceContainer.cs index e5de3c099..17498fb70 100644 --- a/src/Core/Utilities/ServiceContainer.cs +++ b/src/Core/Utilities/ServiceContainer.cs @@ -25,6 +25,7 @@ namespace Bit.Core.Utilities var cryptoPrimitiveService = Resolve("cryptoPrimitiveService"); var i18nService = Resolve("i18nService"); var messagingService = Resolve("messagingService"); + ISearchService searchService = null; var stateService = new StateService(); var cryptoFunctionService = new PclCryptoFunctionService(cryptoPrimitiveService); @@ -35,10 +36,11 @@ namespace Bit.Core.Utilities var userService = new UserService(storageService, tokenService); var settingsService = new SettingsService(userService, storageService); var cipherService = new CipherService(cryptoService, userService, settingsService, apiService, - storageService, i18nService); + storageService, i18nService, () => searchService); var folderService = new FolderService(cryptoService, userService, apiService, storageService, i18nService, cipherService); var collectionService = new CollectionService(cryptoService, userService, storageService, i18nService); + searchService = new SearchService(cipherService); // TODO: lock service var syncService = new SyncService(userService, apiService, settingsService, folderService, cipherService, cryptoService, collectionService, storageService, messagingService);