diff --git a/src/Android/MainApplication.cs b/src/Android/MainApplication.cs index bcbd6336d..2dbb66c81 100644 --- a/src/Android/MainApplication.cs +++ b/src/Android/MainApplication.cs @@ -228,6 +228,7 @@ namespace Bit.Android container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); + container.RegisterSingleton(); // Other container.RegisterSingleton(CrossSettings.Current); diff --git a/src/App/Abstractions/Repositories/IAccountsApiRepository.cs b/src/App/Abstractions/Repositories/IAccountsApiRepository.cs index 257084337..980526eb2 100644 --- a/src/App/Abstractions/Repositories/IAccountsApiRepository.cs +++ b/src/App/Abstractions/Repositories/IAccountsApiRepository.cs @@ -10,6 +10,5 @@ namespace Bit.App.Abstractions Task PostPasswordHintAsync(PasswordHintRequest requestObj); Task> GetAccountRevisionDateAsync(); Task> GetProfileAsync(); - Task> GetKeys(); } } \ No newline at end of file diff --git a/src/App/Abstractions/Repositories/ISyncApiRepository.cs b/src/App/Abstractions/Repositories/ISyncApiRepository.cs new file mode 100644 index 000000000..5d8c881a7 --- /dev/null +++ b/src/App/Abstractions/Repositories/ISyncApiRepository.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Bit.App.Models.Api; + +namespace Bit.App.Abstractions +{ + public interface ISyncApiRepository + { + Task> Get(); + } +} \ No newline at end of file diff --git a/src/App/App.csproj b/src/App/App.csproj index 98d3b05aa..2e07b6536 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -36,6 +36,7 @@ + @@ -116,6 +117,7 @@ + @@ -169,6 +171,7 @@ + diff --git a/src/App/Models/Api/Response/SyncResponse.cs b/src/App/Models/Api/Response/SyncResponse.cs new file mode 100644 index 000000000..dae0ad764 --- /dev/null +++ b/src/App/Models/Api/Response/SyncResponse.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Bit.App.Models.Api +{ + public class SyncResponse + { + public ProfileResponse Profile { get; set; } + public IEnumerable Folders { get; set; } + public IEnumerable Ciphers { get; set; } + public DomainsResponse Domains { get; set; } + } +} diff --git a/src/App/Repositories/AccountsApiRepository.cs b/src/App/Repositories/AccountsApiRepository.cs index 4d7a0bbd8..f81133a6e 100644 --- a/src/App/Repositories/AccountsApiRepository.cs +++ b/src/App/Repositories/AccountsApiRepository.cs @@ -172,45 +172,5 @@ namespace Bit.App.Repositories } } } - - public virtual async Task> GetKeys() - { - if(!Connectivity.IsConnected) - { - return HandledNotConnected(); - } - - var tokenStateResponse = await HandleTokenStateAsync(); - if(!tokenStateResponse.Succeeded) - { - return tokenStateResponse; - } - - using(var client = HttpService.ApiClient) - { - var requestMessage = new TokenHttpRequestMessage() - { - Method = HttpMethod.Get, - RequestUri = new Uri(string.Concat(client.BaseAddress, ApiRoute, "/keys")), - }; - - try - { - var response = await client.SendAsync(requestMessage).ConfigureAwait(false); - if(!response.IsSuccessStatusCode) - { - return await HandleErrorAsync(response).ConfigureAwait(false); - } - - var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - var responseObj = JsonConvert.DeserializeObject(responseContent); - return ApiResult.Success(responseObj, response.StatusCode); - } - catch - { - return HandledWebException(); - } - } - } } } diff --git a/src/App/Repositories/SyncApiRepository.cs b/src/App/Repositories/SyncApiRepository.cs new file mode 100644 index 000000000..a913d91d9 --- /dev/null +++ b/src/App/Repositories/SyncApiRepository.cs @@ -0,0 +1,63 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Bit.App.Abstractions; +using Bit.App.Models.Api; +using Plugin.Connectivity.Abstractions; +using Newtonsoft.Json; + +namespace Bit.App.Repositories +{ + public class SyncApiRepository : BaseApiRepository, ISyncApiRepository + { + public SyncApiRepository( + IConnectivity connectivity, + IHttpService httpService, + ITokenService tokenService) + : base(connectivity, httpService, tokenService) + { } + + protected override string ApiRoute => "sync"; + + public virtual async Task> Get() + { + if(!Connectivity.IsConnected) + { + return HandledNotConnected(); + } + + var tokenStateResponse = await HandleTokenStateAsync(); + if(!tokenStateResponse.Succeeded) + { + return tokenStateResponse; + } + + using(var client = HttpService.ApiClient) + { + var requestMessage = new TokenHttpRequestMessage() + { + Method = HttpMethod.Get, + RequestUri = new Uri( + string.Concat(client.BaseAddress, ApiRoute)), + }; + + try + { + var response = await client.SendAsync(requestMessage).ConfigureAwait(false); + if(!response.IsSuccessStatusCode) + { + return await HandleErrorAsync(response).ConfigureAwait(false); + } + + var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var responseObj = JsonConvert.DeserializeObject(responseContent); + return ApiResult.Success(responseObj, response.StatusCode); + } + catch + { + return HandledWebException(); + } + } + } + } +} diff --git a/src/App/Services/SyncService.cs b/src/App/Services/SyncService.cs index aff4442b1..d4f085de2 100644 --- a/src/App/Services/SyncService.cs +++ b/src/App/Services/SyncService.cs @@ -16,9 +16,9 @@ namespace Bit.App.Services { private readonly ICipherApiRepository _cipherApiRepository; private readonly IFolderApiRepository _folderApiRepository; - private readonly ILoginApiRepository _loginApiRepository; private readonly IAccountsApiRepository _accountsApiRepository; private readonly ISettingsApiRepository _settingsApiRepository; + private readonly ISyncApiRepository _syncApiRepository; private readonly IFolderRepository _folderRepository; private readonly ILoginRepository _loginRepository; private readonly IAttachmentRepository _attachmentRepository; @@ -31,9 +31,9 @@ namespace Bit.App.Services public SyncService( ICipherApiRepository cipherApiRepository, IFolderApiRepository folderApiRepository, - ILoginApiRepository loginApiRepository, IAccountsApiRepository accountsApiRepository, ISettingsApiRepository settingsApiRepository, + ISyncApiRepository syncApiRepository, IFolderRepository folderRepository, ILoginRepository loginRepository, IAttachmentRepository attachmentRepository, @@ -45,9 +45,9 @@ namespace Bit.App.Services { _cipherApiRepository = cipherApiRepository; _folderApiRepository = folderApiRepository; - _loginApiRepository = loginApiRepository; _accountsApiRepository = accountsApiRepository; _settingsApiRepository = settingsApiRepository; + _syncApiRepository = syncApiRepository; _folderRepository = folderRepository; _loginRepository = loginRepository; _attachmentRepository = attachmentRepository; @@ -268,29 +268,23 @@ namespace Bit.App.Services var now = DateTime.UtcNow; - // Just check profile first to make sure we'll have a success with the API - var profile = await _accountsApiRepository.GetProfileAsync().ConfigureAwait(false); - if(!CheckSuccess(profile, !string.IsNullOrWhiteSpace(_appSettingsService.SecurityStamp) && - _appSettingsService.SecurityStamp != profile.Result.SecurityStamp)) + var syncResponse = await _syncApiRepository.Get(); + if(!CheckSuccess(syncResponse, + !string.IsNullOrWhiteSpace(_appSettingsService.SecurityStamp) && + syncResponse.Result?.Profile != null && + _appSettingsService.SecurityStamp != syncResponse.Result.Profile.SecurityStamp)) { return false; } - var ciphers = await _cipherApiRepository.GetAsync().ConfigureAwait(false); - var folders = await _folderApiRepository.GetAsync().ConfigureAwait(false); - var domains = await _settingsApiRepository.GetDomains(false).ConfigureAwait(false); - if(!CheckSuccess(ciphers) || !CheckSuccess(folders) || !CheckSuccess(domains)) - { - return false; - } - - var loginsDict = ciphers.Result.Data.Where(c => c.Type == Enums.CipherType.Login).ToDictionary(s => s.Id); - var foldersDict = folders.Result.Data.ToDictionary(f => f.Id); + var loginsDict = syncResponse.Result.Ciphers.Where(c => c.Type == Enums.CipherType.Login) + .ToDictionary(s => s.Id); + var foldersDict = syncResponse.Result.Folders.ToDictionary(f => f.Id); var loginTask = SyncLoginsAsync(loginsDict); var folderTask = SyncFoldersAsync(foldersDict); - var domainsTask = SyncDomainsAsync(domains.Result); - var profileTask = SyncProfileKeysAsync(profile.Result); + var domainsTask = SyncDomainsAsync(syncResponse.Result.Domains); + var profileTask = SyncProfileKeysAsync(syncResponse.Result.Profile); await Task.WhenAll(loginTask, folderTask, domainsTask, profileTask).ConfigureAwait(false); if(folderTask.Exception != null || loginTask.Exception != null || domainsTask.Exception != null || @@ -436,6 +430,11 @@ namespace Bit.App.Services private async Task SyncDomainsAsync(DomainsResponse serverDomains) { + if(serverDomains == null) + { + return; + } + var eqDomains = new List>(); if(serverDomains.EquivalentDomains != null) { @@ -460,6 +459,11 @@ namespace Bit.App.Services private Task SyncProfileKeysAsync(ProfileResponse profile) { + if(profile == null) + { + return Task.FromResult(0); + } + if(!string.IsNullOrWhiteSpace(profile.Key)) { _cryptoService.SetEncKey(new CipherString(profile.Key)); diff --git a/src/iOS.Extension/LoadingViewController.cs b/src/iOS.Extension/LoadingViewController.cs index a81b7895c..29ba31b6a 100644 --- a/src/iOS.Extension/LoadingViewController.cs +++ b/src/iOS.Extension/LoadingViewController.cs @@ -299,6 +299,7 @@ namespace Bit.iOS.Extension container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); + container.RegisterSingleton(); // Other container.RegisterSingleton(CrossConnectivity.Current); diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs index bd3bda698..c1ee132eb 100644 --- a/src/iOS/AppDelegate.cs +++ b/src/iOS/AppDelegate.cs @@ -285,6 +285,7 @@ namespace Bit.iOS container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); + container.RegisterSingleton(); // Other container.RegisterSingleton(CrossConnectivity.Current);