mirror of
https://github.com/bitwarden/android.git
synced 2024-12-21 08:42:39 +03:00
Cipher api repository and various sync operations from push notifications.
This commit is contained in:
parent
84836fe89b
commit
2d6e0bd95a
21 changed files with 399 additions and 62 deletions
|
@ -129,6 +129,7 @@ namespace Bit.Android
|
||||||
.RegisterType<IAuthApiRepository, AuthApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IAuthApiRepository, AuthApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IAccountsApiRepository, AccountsApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IAccountsApiRepository, AccountsApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<ICipherApiRepository, CipherApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
// Other
|
// Other
|
||||||
.RegisterInstance(CrossDeviceInfo.Current, new ContainerControlledLifetimeManager())
|
.RegisterInstance(CrossDeviceInfo.Current, new ContainerControlledLifetimeManager())
|
||||||
.RegisterInstance(CrossSettings.Current, new ContainerControlledLifetimeManager())
|
.RegisterInstance(CrossSettings.Current, new ContainerControlledLifetimeManager())
|
||||||
|
|
13
src/App/Abstractions/Repositories/ICipherApiRepository.cs
Normal file
13
src/App/Abstractions/Repositories/ICipherApiRepository.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models.Api;
|
||||||
|
|
||||||
|
namespace Bit.App.Abstractions
|
||||||
|
{
|
||||||
|
public interface ICipherApiRepository
|
||||||
|
{
|
||||||
|
Task<ApiResult<CipherResponse>> GetByIdAsync(string id);
|
||||||
|
Task<ApiResult<ListResponse<CipherResponse>>> GetAsync();
|
||||||
|
Task<ApiResult<CipherHistoryResponse>> GetByRevisionDateWithHistoryAsync(DateTime since);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,5 @@ using Bit.App.Models.Api;
|
||||||
namespace Bit.App.Abstractions
|
namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
public interface IFolderApiRepository : IApiRepository<FolderRequest, FolderResponse, string>
|
public interface IFolderApiRepository : IApiRepository<FolderRequest, FolderResponse, string>
|
||||||
{
|
{ }
|
||||||
Task<ApiResult<ListResponse<FolderResponse>>> GetByRevisionDateAsync(DateTime since);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -7,6 +7,5 @@ namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
public interface ISiteApiRepository : IApiRepository<SiteRequest, SiteResponse, string>
|
public interface ISiteApiRepository : IApiRepository<SiteRequest, SiteResponse, string>
|
||||||
{
|
{
|
||||||
Task<ApiResult<ListResponse<SiteResponse>>> GetByRevisionDateAsync(DateTime since);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,10 @@ namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
public interface ISyncService
|
public interface ISyncService
|
||||||
{
|
{
|
||||||
Task<bool> SyncAsync();
|
Task<bool> SyncAsync(string id);
|
||||||
|
Task<bool> SyncDeleteFolderAsync(string id);
|
||||||
|
Task<bool> SyncDeleteSiteAsync(string id);
|
||||||
|
Task<bool> FullSyncAsync();
|
||||||
|
Task<bool> IncrementalSyncAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -67,9 +67,12 @@
|
||||||
<Compile Include="Controls\FormPickerCell.cs" />
|
<Compile Include="Controls\FormPickerCell.cs" />
|
||||||
<Compile Include="Controls\FormEntryCell.cs" />
|
<Compile Include="Controls\FormEntryCell.cs" />
|
||||||
<Compile Include="Controls\PinControl.cs" />
|
<Compile Include="Controls\PinControl.cs" />
|
||||||
|
<Compile Include="Enums\CipherType.cs" />
|
||||||
|
<Compile Include="Enums\PushType.cs" />
|
||||||
<Compile Include="Enums\ReturnType.cs" />
|
<Compile Include="Enums\ReturnType.cs" />
|
||||||
<Compile Include="Models\Api\ApiError.cs" />
|
<Compile Include="Models\Api\ApiError.cs" />
|
||||||
<Compile Include="Models\Api\ApiResult.cs" />
|
<Compile Include="Models\Api\ApiResult.cs" />
|
||||||
|
<Compile Include="Models\Api\FolderDataModel.cs" />
|
||||||
<Compile Include="Models\Api\Request\DeviceTokenRequest.cs" />
|
<Compile Include="Models\Api\Request\DeviceTokenRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\FolderRequest.cs" />
|
<Compile Include="Models\Api\Request\FolderRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\DeviceRequest.cs" />
|
<Compile Include="Models\Api\Request\DeviceRequest.cs" />
|
||||||
|
@ -77,6 +80,8 @@
|
||||||
<Compile Include="Models\Api\Request\SiteRequest.cs" />
|
<Compile Include="Models\Api\Request\SiteRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\TokenRequest.cs" />
|
<Compile Include="Models\Api\Request\TokenRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\TokenTwoFactorRequest.cs" />
|
<Compile Include="Models\Api\Request\TokenTwoFactorRequest.cs" />
|
||||||
|
<Compile Include="Models\Api\Response\CipherHistoryResponse.cs" />
|
||||||
|
<Compile Include="Models\Api\Response\CipherResponse.cs" />
|
||||||
<Compile Include="Models\Api\Response\ErrorResponse.cs" />
|
<Compile Include="Models\Api\Response\ErrorResponse.cs" />
|
||||||
<Compile Include="Models\Api\Response\FolderResponse.cs" />
|
<Compile Include="Models\Api\Response\FolderResponse.cs" />
|
||||||
<Compile Include="Models\Api\Response\ListResponse.cs" />
|
<Compile Include="Models\Api\Response\ListResponse.cs" />
|
||||||
|
@ -84,6 +89,7 @@
|
||||||
<Compile Include="Models\Api\Response\SiteResponse.cs" />
|
<Compile Include="Models\Api\Response\SiteResponse.cs" />
|
||||||
<Compile Include="Models\Api\Response\TokenResponse.cs" />
|
<Compile Include="Models\Api\Response\TokenResponse.cs" />
|
||||||
<Compile Include="Models\Api\Response\ProfileResponse.cs" />
|
<Compile Include="Models\Api\Response\ProfileResponse.cs" />
|
||||||
|
<Compile Include="Models\Api\SiteDataModel.cs" />
|
||||||
<Compile Include="Models\Cipher.cs" />
|
<Compile Include="Models\Cipher.cs" />
|
||||||
<Compile Include="Models\CipherString.cs" />
|
<Compile Include="Models\CipherString.cs" />
|
||||||
<Compile Include="Models\Data\FolderData.cs" />
|
<Compile Include="Models\Data\FolderData.cs" />
|
||||||
|
@ -93,6 +99,7 @@
|
||||||
<Compile Include="Models\Folder.cs" />
|
<Compile Include="Models\Folder.cs" />
|
||||||
<Compile Include="Models\Page\SettingsFolderPageModel.cs" />
|
<Compile Include="Models\Page\SettingsFolderPageModel.cs" />
|
||||||
<Compile Include="Models\Page\PinPageModel.cs" />
|
<Compile Include="Models\Page\PinPageModel.cs" />
|
||||||
|
<Compile Include="Models\PushNotification.cs" />
|
||||||
<Compile Include="Models\Site.cs" />
|
<Compile Include="Models\Site.cs" />
|
||||||
<Compile Include="Models\Page\VaultViewSitePageModel.cs" />
|
<Compile Include="Models\Page\VaultViewSitePageModel.cs" />
|
||||||
<Compile Include="Pages\HomePage.cs" />
|
<Compile Include="Pages\HomePage.cs" />
|
||||||
|
@ -116,6 +123,8 @@
|
||||||
<Compile Include="Repositories\AuthApiRepository.cs" />
|
<Compile Include="Repositories\AuthApiRepository.cs" />
|
||||||
<Compile Include="Abstractions\Repositories\IAuthApiRepository.cs" />
|
<Compile Include="Abstractions\Repositories\IAuthApiRepository.cs" />
|
||||||
<Compile Include="Repositories\DeviceApiRepository.cs" />
|
<Compile Include="Repositories\DeviceApiRepository.cs" />
|
||||||
|
<Compile Include="Repositories\CipherApiRepository.cs" />
|
||||||
|
<Compile Include="Abstractions\Repositories\ICipherApiRepository.cs" />
|
||||||
<Compile Include="Repositories\SiteApiRepository.cs" />
|
<Compile Include="Repositories\SiteApiRepository.cs" />
|
||||||
<Compile Include="Repositories\FolderApiRepository.cs" />
|
<Compile Include="Repositories\FolderApiRepository.cs" />
|
||||||
<Compile Include="Repositories\SiteRepository.cs" />
|
<Compile Include="Repositories\SiteRepository.cs" />
|
||||||
|
|
8
src/App/Enums/CipherType.cs
Normal file
8
src/App/Enums/CipherType.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Bit.App.Enums
|
||||||
|
{
|
||||||
|
public enum CipherType : short
|
||||||
|
{
|
||||||
|
Folder = 0,
|
||||||
|
Site = 1
|
||||||
|
}
|
||||||
|
}
|
11
src/App/Enums/PushType.cs
Normal file
11
src/App/Enums/PushType.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Bit.App.Enums
|
||||||
|
{
|
||||||
|
public enum PushType : short
|
||||||
|
{
|
||||||
|
SyncCipherUpdate = 0,
|
||||||
|
SyncCipherCreate = 1,
|
||||||
|
SyncSiteDelete = 2,
|
||||||
|
SyncFolderDelete = 3,
|
||||||
|
SyncCiphers = 4
|
||||||
|
}
|
||||||
|
}
|
7
src/App/Models/Api/FolderDataModel.cs
Normal file
7
src/App/Models/Api/FolderDataModel.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Bit.App.Models.Api
|
||||||
|
{
|
||||||
|
public class FolderDataModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
src/App/Models/Api/Response/CipherHistoryResponse.cs
Normal file
10
src/App/Models/Api/Response/CipherHistoryResponse.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Bit.App.Models.Api
|
||||||
|
{
|
||||||
|
public class CipherHistoryResponse
|
||||||
|
{
|
||||||
|
public IEnumerable<CipherResponse> Revised { get; set; }
|
||||||
|
public IEnumerable<string> Deleted { get; set; }
|
||||||
|
}
|
||||||
|
}
|
15
src/App/Models/Api/Response/CipherResponse.cs
Normal file
15
src/App/Models/Api/Response/CipherResponse.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using Bit.App.Enums;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bit.App.Models.Api
|
||||||
|
{
|
||||||
|
public class CipherResponse
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string FolderId { get; set; }
|
||||||
|
public CipherType Type { get; set; }
|
||||||
|
public bool Favorite { get; set; }
|
||||||
|
public dynamic Data { get; set; }
|
||||||
|
public DateTime RevisionDate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
11
src/App/Models/Api/SiteDataModel.cs
Normal file
11
src/App/Models/Api/SiteDataModel.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Bit.App.Models.Api
|
||||||
|
{
|
||||||
|
public class SiteDataModel
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Uri { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,24 @@ namespace Bit.App.Models.Data
|
||||||
Name = folder.Name;
|
Name = folder.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FolderData(CipherResponse cipher, string userId)
|
||||||
|
{
|
||||||
|
if(cipher.Type != Enums.CipherType.Folder)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(cipher.Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = cipher.Data as FolderDataModel;
|
||||||
|
if(data == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(cipher.Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = cipher.Id;
|
||||||
|
UserId = userId;
|
||||||
|
Name = data.Name;
|
||||||
|
}
|
||||||
|
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
[Indexed]
|
[Indexed]
|
||||||
|
|
|
@ -37,6 +37,29 @@ namespace Bit.App.Models.Data
|
||||||
Favorite = site.Favorite;
|
Favorite = site.Favorite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SiteData(CipherResponse cipher, string userId)
|
||||||
|
{
|
||||||
|
if(cipher.Type != Enums.CipherType.Site)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(cipher.Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = cipher.Data as SiteDataModel;
|
||||||
|
if(data == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(cipher.Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = cipher.Id;
|
||||||
|
UserId = userId;
|
||||||
|
Name = data.Name;
|
||||||
|
Uri = data.Uri;
|
||||||
|
Username = data.Username;
|
||||||
|
Password = data.Password;
|
||||||
|
Notes = data.Notes;
|
||||||
|
Favorite = cipher.Favorite;
|
||||||
|
}
|
||||||
|
|
||||||
[PrimaryKey]
|
[PrimaryKey]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public string FolderId { get; set; }
|
public string FolderId { get; set; }
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Bit.App.Models.Page
|
||||||
Id = site.Id;
|
Id = site.Id;
|
||||||
FolderId = folderId;
|
FolderId = folderId;
|
||||||
Name = site.Name?.Decrypt();
|
Name = site.Name?.Decrypt();
|
||||||
Username = site.Username?.Decrypt() ?? " ";
|
Username = site.Username?.Decrypt();
|
||||||
Password = site.Password?.Decrypt();
|
Password = site.Password?.Decrypt();
|
||||||
Uri = site.Uri?.Decrypt();
|
Uri = site.Uri?.Decrypt();
|
||||||
}
|
}
|
||||||
|
|
19
src/App/Models/PushNotification.cs
Normal file
19
src/App/Models/PushNotification.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using Bit.App.Enums;
|
||||||
|
|
||||||
|
namespace Bit.App.Models
|
||||||
|
{
|
||||||
|
public class PushNotification
|
||||||
|
{
|
||||||
|
public PushType Type { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SyncPushNotification : PushNotification
|
||||||
|
{
|
||||||
|
public string UserId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SyncCipherPushNotification : SyncPushNotification
|
||||||
|
{
|
||||||
|
public string CipherId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
72
src/App/Repositories/CipherApiRepository.cs
Normal file
72
src/App/Repositories/CipherApiRepository.cs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Models.Api;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Bit.App.Repositories
|
||||||
|
{
|
||||||
|
public class CipherApiRepository : BaseApiRepository, ICipherApiRepository
|
||||||
|
{
|
||||||
|
protected override string ApiRoute => "ciphers";
|
||||||
|
|
||||||
|
public virtual async Task<ApiResult<CipherResponse>> GetByIdAsync(string id)
|
||||||
|
{
|
||||||
|
var requestMessage = new TokenHttpRequestMessage()
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Get,
|
||||||
|
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "/", id)),
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await Client.SendAsync(requestMessage);
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return await HandleErrorAsync<CipherResponse>(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var responseObj = JsonConvert.DeserializeObject<CipherResponse>(responseContent);
|
||||||
|
return ApiResult<CipherResponse>.Success(responseObj, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<ApiResult<ListResponse<CipherResponse>>> GetAsync()
|
||||||
|
{
|
||||||
|
var requestMessage = new TokenHttpRequestMessage()
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Get,
|
||||||
|
RequestUri = new Uri(Client.BaseAddress, ApiRoute),
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await Client.SendAsync(requestMessage);
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return await HandleErrorAsync<ListResponse<CipherResponse>>(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var responseObj = JsonConvert.DeserializeObject<ListResponse<CipherResponse>>(responseContent);
|
||||||
|
return ApiResult<ListResponse<CipherResponse>>.Success(responseObj, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual async Task<ApiResult<CipherHistoryResponse>> GetByRevisionDateWithHistoryAsync(DateTime since)
|
||||||
|
{
|
||||||
|
var requestMessage = new TokenHttpRequestMessage()
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Get,
|
||||||
|
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "?since=", since)),
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await Client.SendAsync(requestMessage);
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return await HandleErrorAsync<CipherHistoryResponse>(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var responseObj = JsonConvert.DeserializeObject<CipherHistoryResponse>(responseContent);
|
||||||
|
return ApiResult<CipherHistoryResponse>.Success(responseObj, response.StatusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,24 +11,5 @@ namespace Bit.App.Repositories
|
||||||
public class SiteApiRepository : ApiRepository<SiteRequest, SiteResponse, string>, ISiteApiRepository
|
public class SiteApiRepository : ApiRepository<SiteRequest, SiteResponse, string>, ISiteApiRepository
|
||||||
{
|
{
|
||||||
protected override string ApiRoute => "sites";
|
protected override string ApiRoute => "sites";
|
||||||
|
|
||||||
public virtual async Task<ApiResult<ListResponse<SiteResponse>>> GetByRevisionDateAsync(DateTime since)
|
|
||||||
{
|
|
||||||
var requestMessage = new TokenHttpRequestMessage()
|
|
||||||
{
|
|
||||||
Method = HttpMethod.Get,
|
|
||||||
RequestUri = new Uri(Client.BaseAddress, string.Concat(ApiRoute, "?since=", since)),
|
|
||||||
};
|
|
||||||
|
|
||||||
var response = await Client.SendAsync(requestMessage);
|
|
||||||
if(!response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
return await HandleErrorAsync<ListResponse<SiteResponse>>(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
|
||||||
var responseObj = JsonConvert.DeserializeObject<ListResponse<SiteResponse>>(responseContent);
|
|
||||||
return ApiResult<ListResponse<SiteResponse>>.Success(responseObj, response.StatusCode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ using PushNotification.Plugin;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Models;
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
|
@ -31,6 +32,29 @@ namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
_showNotification = false;
|
_showNotification = false;
|
||||||
Debug.WriteLine("Message Arrived: {0}", JsonConvert.SerializeObject(values));
|
Debug.WriteLine("Message Arrived: {0}", JsonConvert.SerializeObject(values));
|
||||||
|
|
||||||
|
var type = (Enums.PushType)values.GetValue("type", System.StringComparison.OrdinalIgnoreCase).ToObject<short>();
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case Enums.PushType.SyncCipherUpdate:
|
||||||
|
case Enums.PushType.SyncCipherCreate:
|
||||||
|
var createUpdateMessage = values.ToObject<SyncCipherPushNotification>();
|
||||||
|
_syncService.SyncAsync(createUpdateMessage.CipherId);
|
||||||
|
break;
|
||||||
|
case Enums.PushType.SyncFolderDelete:
|
||||||
|
var folderDeleteMessage = values.ToObject<SyncCipherPushNotification>();
|
||||||
|
_syncService.SyncDeleteFolderAsync(folderDeleteMessage.CipherId);
|
||||||
|
break;
|
||||||
|
case Enums.PushType.SyncSiteDelete:
|
||||||
|
var siteDeleteMessage = values.ToObject<SyncCipherPushNotification>();
|
||||||
|
_syncService.SyncDeleteFolderAsync(siteDeleteMessage.CipherId);
|
||||||
|
break;
|
||||||
|
case Enums.PushType.SyncCiphers:
|
||||||
|
_syncService.FullSyncAsync();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OnRegistered(string token, DeviceType deviceType)
|
public async void OnRegistered(string token, DeviceType deviceType)
|
||||||
|
|
|
@ -4,6 +4,8 @@ using System.Threading.Tasks;
|
||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Models.Data;
|
using Bit.App.Models.Data;
|
||||||
using Plugin.Settings.Abstractions;
|
using Plugin.Settings.Abstractions;
|
||||||
|
using Bit.App.Models.Api;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
|
@ -11,6 +13,7 @@ namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
private const string LastSyncKey = "lastSync";
|
private const string LastSyncKey = "lastSync";
|
||||||
|
|
||||||
|
private readonly ICipherApiRepository _cipherApiRepository;
|
||||||
private readonly IFolderApiRepository _folderApiRepository;
|
private readonly IFolderApiRepository _folderApiRepository;
|
||||||
private readonly ISiteApiRepository _siteApiRepository;
|
private readonly ISiteApiRepository _siteApiRepository;
|
||||||
private readonly IFolderRepository _folderRepository;
|
private readonly IFolderRepository _folderRepository;
|
||||||
|
@ -19,6 +22,7 @@ namespace Bit.App.Services
|
||||||
private readonly ISettings _settings;
|
private readonly ISettings _settings;
|
||||||
|
|
||||||
public SyncService(
|
public SyncService(
|
||||||
|
ICipherApiRepository cipherApiRepository,
|
||||||
IFolderApiRepository folderApiRepository,
|
IFolderApiRepository folderApiRepository,
|
||||||
ISiteApiRepository siteApiRepository,
|
ISiteApiRepository siteApiRepository,
|
||||||
IFolderRepository folderRepository,
|
IFolderRepository folderRepository,
|
||||||
|
@ -26,6 +30,7 @@ namespace Bit.App.Services
|
||||||
IAuthService authService,
|
IAuthService authService,
|
||||||
ISettings settings)
|
ISettings settings)
|
||||||
{
|
{
|
||||||
|
_cipherApiRepository = cipherApiRepository;
|
||||||
_folderApiRepository = folderApiRepository;
|
_folderApiRepository = folderApiRepository;
|
||||||
_siteApiRepository = siteApiRepository;
|
_siteApiRepository = siteApiRepository;
|
||||||
_folderRepository = folderRepository;
|
_folderRepository = folderRepository;
|
||||||
|
@ -34,36 +39,145 @@ namespace Bit.App.Services
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SyncAsync()
|
public async Task<bool> SyncAsync(string id)
|
||||||
{
|
{
|
||||||
var now = DateTime.UtcNow;
|
if(!_authService.IsAuthenticated)
|
||||||
var lastSync = _settings.GetValueOrDefault(LastSyncKey, now.AddYears(-100));
|
|
||||||
|
|
||||||
var siteTask = SyncSitesAsync(lastSync);
|
|
||||||
var folderTask = SyncFoldersAsync(lastSync);
|
|
||||||
await Task.WhenAll(siteTask, folderTask);
|
|
||||||
|
|
||||||
if(await siteTask && await folderTask && folderTask.Exception == null && siteTask.Exception == null)
|
|
||||||
{
|
|
||||||
_settings.AddOrUpdateValue(LastSyncKey, now);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> SyncFoldersAsync(DateTime lastSync)
|
|
||||||
{
|
|
||||||
var folderResponse = await _folderApiRepository.GetAsync();
|
|
||||||
if(!folderResponse.Succeeded)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverFolders = folderResponse.Result.Data;
|
var cipher = await _cipherApiRepository.GetByIdAsync(id);
|
||||||
|
if(!cipher.Succeeded)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(cipher.Result.Type)
|
||||||
|
{
|
||||||
|
case Enums.CipherType.Folder:
|
||||||
|
var folderData = new FolderData(cipher.Result, _authService.UserId);
|
||||||
|
var existingLocalFolder = _folderRepository.GetByIdAsync(id);
|
||||||
|
if(existingLocalFolder == null)
|
||||||
|
{
|
||||||
|
await _folderRepository.InsertAsync(folderData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _folderRepository.UpdateAsync(folderData);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Enums.CipherType.Site:
|
||||||
|
var siteData = new SiteData(cipher.Result, _authService.UserId);
|
||||||
|
var existingLocalSite = _siteRepository.GetByIdAsync(id);
|
||||||
|
if(existingLocalSite == null)
|
||||||
|
{
|
||||||
|
await _siteRepository.InsertAsync(siteData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _siteRepository.UpdateAsync(siteData);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SyncDeleteFolderAsync(string id)
|
||||||
|
{
|
||||||
|
if(!_authService.IsAuthenticated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _folderRepository.DeleteAsync(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SyncDeleteSiteAsync(string id)
|
||||||
|
{
|
||||||
|
if(!_authService.IsAuthenticated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _siteRepository.DeleteAsync(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> FullSyncAsync()
|
||||||
|
{
|
||||||
|
if(!_authService.IsAuthenticated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
var ciphers = await _cipherApiRepository.GetAsync();
|
||||||
|
if(!ciphers.Succeeded)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var siteTask = SyncSitesAsync(ciphers.Result.Data.Where(c => c.Type == Enums.CipherType.Site), true);
|
||||||
|
var folderTask = SyncFoldersAsync(ciphers.Result.Data.Where(c => c.Type == Enums.CipherType.Folder), true);
|
||||||
|
await Task.WhenAll(siteTask, folderTask);
|
||||||
|
|
||||||
|
if(folderTask.Exception == null || siteTask.Exception == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_settings.AddOrUpdateValue(LastSyncKey, now);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IncrementalSyncAsync()
|
||||||
|
{
|
||||||
|
if(!_authService.IsAuthenticated)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
DateTime? lastSync = _settings.GetValueOrDefault<DateTime?>(LastSyncKey);
|
||||||
|
if(lastSync == null)
|
||||||
|
{
|
||||||
|
return await FullSyncAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var ciphers = await _cipherApiRepository.GetByRevisionDateWithHistoryAsync(lastSync.Value);
|
||||||
|
if(!ciphers.Succeeded)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var siteTask = SyncSitesAsync(ciphers.Result.Revised.Where(c => c.Type == Enums.CipherType.Site), false);
|
||||||
|
var folderTask = SyncFoldersAsync(ciphers.Result.Revised.Where(c => c.Type == Enums.CipherType.Folder), false);
|
||||||
|
|
||||||
|
foreach(var cipherId in ciphers.Result.Deleted)
|
||||||
|
{
|
||||||
|
await _siteRepository.DeleteAsync(cipherId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(siteTask, folderTask);
|
||||||
|
|
||||||
|
if(folderTask.Exception == null || siteTask.Exception == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_settings.AddOrUpdateValue(LastSyncKey, now);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SyncFoldersAsync(IEnumerable<CipherResponse> serverFolders, bool deleteMissing)
|
||||||
|
{
|
||||||
var localFolders = await _folderRepository.GetAllByUserIdAsync(_authService.UserId);
|
var localFolders = await _folderRepository.GetAllByUserIdAsync(_authService.UserId);
|
||||||
|
|
||||||
foreach(var serverFolder in serverFolders.Where(f => f.RevisionDate >= lastSync))
|
foreach(var serverFolder in serverFolders)
|
||||||
{
|
{
|
||||||
var data = new FolderData(serverFolder, _authService.UserId);
|
var data = new FolderData(serverFolder, _authService.UserId);
|
||||||
var existingLocalFolder = localFolders.SingleOrDefault(f => f.Id == serverFolder.Id);
|
var existingLocalFolder = localFolders.SingleOrDefault(f => f.Id == serverFolder.Id);
|
||||||
|
@ -77,26 +191,22 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!deleteMissing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach(var folder in localFolders.Where(localFolder => !serverFolders.Any(serverFolder => serverFolder.Id == localFolder.Id)))
|
foreach(var folder in localFolders.Where(localFolder => !serverFolders.Any(serverFolder => serverFolder.Id == localFolder.Id)))
|
||||||
{
|
{
|
||||||
await _folderRepository.DeleteAsync(folder.Id);
|
await _folderRepository.DeleteAsync(folder.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> SyncSitesAsync(DateTime lastSync)
|
private async Task SyncSitesAsync(IEnumerable<CipherResponse> serverSites, bool deleteMissing)
|
||||||
{
|
{
|
||||||
var siteResponse = await _siteApiRepository.GetAsync();
|
|
||||||
if(!siteResponse.Succeeded)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverSites = siteResponse.Result.Data;
|
|
||||||
var localSites = await _siteRepository.GetAllByUserIdAsync(_authService.UserId);
|
var localSites = await _siteRepository.GetAllByUserIdAsync(_authService.UserId);
|
||||||
|
|
||||||
foreach(var serverSite in serverSites.Where(s => s.RevisionDate >= lastSync))
|
foreach(var serverSite in serverSites)
|
||||||
{
|
{
|
||||||
var data = new SiteData(serverSite, _authService.UserId);
|
var data = new SiteData(serverSite, _authService.UserId);
|
||||||
var existingLocalSite = localSites.SingleOrDefault(s => s.Id == serverSite.Id);
|
var existingLocalSite = localSites.SingleOrDefault(s => s.Id == serverSite.Id);
|
||||||
|
@ -110,12 +220,15 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!deleteMissing)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach(var site in localSites.Where(localSite => !serverSites.Any(serverSite => serverSite.Id == localSite.Id)))
|
foreach(var site in localSites.Where(localSite => !serverSites.Any(serverSite => serverSite.Id == localSite.Id)))
|
||||||
{
|
{
|
||||||
await _siteRepository.DeleteAsync(site.Id);
|
await _siteRepository.DeleteAsync(site.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,6 +189,7 @@ namespace Bit.iOS
|
||||||
.RegisterType<IAuthApiRepository, AuthApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IAuthApiRepository, AuthApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
.RegisterType<IAccountsApiRepository, AccountsApiRepository>(new ContainerControlledLifetimeManager())
|
.RegisterType<IAccountsApiRepository, AccountsApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
|
.RegisterType<ICipherApiRepository, CipherApiRepository>(new ContainerControlledLifetimeManager())
|
||||||
// Other
|
// Other
|
||||||
.RegisterInstance(CrossDeviceInfo.Current, new ContainerControlledLifetimeManager())
|
.RegisterInstance(CrossDeviceInfo.Current, new ContainerControlledLifetimeManager())
|
||||||
.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
|
.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
|
||||||
|
|
Loading…
Reference in a new issue