From 0a7ad44d230b0ce56da8cc223677a4d22880ee53 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 12 Jul 2017 16:23:24 -0400 Subject: [PATCH] sync and display attachments on view login --- src/Android/MainApplication.cs | 1 + .../Repositories/IAttachmentRepository.cs | 12 ++++++ src/App/App.csproj | 2 + src/App/Models/Attachment.cs | 6 +-- src/App/Models/Data/AttachmentData.cs | 2 +- .../Models/Page/VaultViewLoginPageModel.cs | 40 +++++++++++++++++++ src/App/Pages/Vault/VaultViewLoginPage.cs | 36 +++++++++++++++++ src/App/Repositories/AttachmentRepository.cs | 22 ++++++++++ src/App/Resources/AppResources.Designer.cs | 18 +++++++++ src/App/Resources/AppResources.resx | 6 +++ src/App/Services/DatabaseService.cs | 1 + src/App/Services/LoginService.cs | 6 ++- src/App/Services/SyncService.cs | 20 ++++++++++ src/iOS.Extension/LoadingViewController.cs | 1 + src/iOS/AppDelegate.cs | 1 + 15 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 src/App/Abstractions/Repositories/IAttachmentRepository.cs create mode 100644 src/App/Repositories/AttachmentRepository.cs diff --git a/src/Android/MainApplication.cs b/src/Android/MainApplication.cs index 6f308cd37..78e6f59b9 100644 --- a/src/Android/MainApplication.cs +++ b/src/Android/MainApplication.cs @@ -227,6 +227,7 @@ namespace Bit.Android container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); + container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); diff --git a/src/App/Abstractions/Repositories/IAttachmentRepository.cs b/src/App/Abstractions/Repositories/IAttachmentRepository.cs new file mode 100644 index 000000000..6c615b3b6 --- /dev/null +++ b/src/App/Abstractions/Repositories/IAttachmentRepository.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Bit.App.Models.Data; + +namespace Bit.App.Abstractions +{ + public interface IAttachmentRepository : IRepository + { + Task> GetAllByLoginIdAsync(string loginId); + } +} diff --git a/src/App/App.csproj b/src/App/App.csproj index 6b450e941..78ad71328 100644 --- a/src/App/App.csproj +++ b/src/App/App.csproj @@ -35,6 +35,7 @@ 4 + @@ -161,6 +162,7 @@ + diff --git a/src/App/Models/Attachment.cs b/src/App/Models/Attachment.cs index 003bdeac3..dc41a6218 100644 --- a/src/App/Models/Attachment.cs +++ b/src/App/Models/Attachment.cs @@ -12,7 +12,7 @@ namespace Bit.App.Models { Id = data.Id; Url = data.Url; - FileName = data.FileName; + FileName = data.FileName != null ? new CipherString(data.FileName) : null; Size = data.Size; SizeName = data.SizeName; } @@ -21,14 +21,14 @@ namespace Bit.App.Models { Id = response.Id; Url = response.Url; - FileName = response.FileName; + FileName = response.FileName != null ? new CipherString(response.FileName) : null; Size = response.Size; SizeName = response.SizeName; } public string Id { get; set; } public string Url { get; set; } - public string FileName { get; set; } + public CipherString FileName { get; set; } public string Size { get; set; } public string SizeName { get; set; } diff --git a/src/App/Models/Data/AttachmentData.cs b/src/App/Models/Data/AttachmentData.cs index 1e9f2d649..a64c8f1a2 100644 --- a/src/App/Models/Data/AttachmentData.cs +++ b/src/App/Models/Data/AttachmentData.cs @@ -15,7 +15,7 @@ namespace Bit.App.Models.Data Id = attachment.Id; LoginId = loginId; Url = attachment.Url; - FileName = attachment.FileName; + FileName = attachment.FileName?.EncryptedString; Size = attachment.Size; SizeName = attachment.SizeName; } diff --git a/src/App/Models/Page/VaultViewLoginPageModel.cs b/src/App/Models/Page/VaultViewLoginPageModel.cs index d8fcea766..99fad4de7 100644 --- a/src/App/Models/Page/VaultViewLoginPageModel.cs +++ b/src/App/Models/Page/VaultViewLoginPageModel.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using Bit.App.Resources; using Xamarin.Forms; +using System.Collections.Generic; namespace Bit.App.Models.Page { @@ -13,6 +14,7 @@ namespace Bit.App.Models.Page private string _uri; private string _notes; private bool _revealPassword; + private List _attachments; public VaultViewLoginPageModel() { } @@ -192,6 +194,18 @@ namespace Bit.App.Models.Page public string ShowHideText => RevealPassword ? AppResources.Hide : AppResources.Show; public ImageSource ShowHideImage => RevealPassword ? ImageSource.FromFile("eye_slash") : ImageSource.FromFile("eye"); + public List Attachments + { + get { return _attachments; } + set + { + _attachments = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(Attachments))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowAttachments))); + } + } + public bool ShowAttachments => (Attachments?.Count ?? 0) > 0; + public void Update(Login login) { Name = login.Name?.Decrypt(login.OrganizationId); @@ -199,6 +213,32 @@ namespace Bit.App.Models.Page Password = login.Password?.Decrypt(login.OrganizationId); Uri = login.Uri?.Decrypt(login.OrganizationId); Notes = login.Notes?.Decrypt(login.OrganizationId); + + if(login.Attachments != null) + { + var attachments = new List(); + foreach(var attachment in login.Attachments) + { + attachments.Add(new Attachment + { + Id = attachment.Id, + Name = attachment.FileName?.Decrypt(login.OrganizationId), + Size = attachment.SizeName + }); + } + Attachments = attachments; + } + else + { + login.Attachments = null; + } + } + + public class Attachment + { + public string Id { get; set; } + public string Name { get; set; } + public string Size { get; set; } } } } diff --git a/src/App/Pages/Vault/VaultViewLoginPage.cs b/src/App/Pages/Vault/VaultViewLoginPage.cs index c61582652..ebc329a44 100644 --- a/src/App/Pages/Vault/VaultViewLoginPage.cs +++ b/src/App/Pages/Vault/VaultViewLoginPage.cs @@ -32,6 +32,7 @@ namespace Bit.App.Pages private ExtendedTableView Table { get; set; } private TableSection LoginInformationSection { get; set; } private TableSection NotesSection { get; set; } + private TableSection AttachmentsSection { get; set; } public LabeledValueCell UsernameCell { get; set; } public LabeledValueCell PasswordCell { get; set; } public LabeledValueCell UriCell { get; set; } @@ -184,6 +185,23 @@ namespace Bit.App.Pages Table.Root.Add(NotesSection); } + if(!Model.ShowAttachments && Table.Root.Contains(AttachmentsSection)) + { + Table.Root.Remove(AttachmentsSection); + } + else if(Model.ShowAttachments && !Table.Root.Contains(AttachmentsSection)) + { + AttachmentsSection = new TableSection(AppResources.Attachments); + foreach(var attachment in Model.Attachments) + { + AttachmentsSection.Add(new AttachmentViewCell(attachment, () => + { + + })); + } + Table.Root.Add(AttachmentsSection); + } + base.OnAppearing(); } @@ -223,5 +241,23 @@ namespace Bit.App.Pages await _page.Navigation.PushForDeviceAsync(page); } } + + public class AttachmentViewCell : ExtendedTextCell + { + Action _clicked; + + public AttachmentViewCell(VaultViewLoginPageModel.Attachment attachment, Action clickedAction) + { + _clicked = clickedAction; + Text = attachment.Name; + Tapped += AttachmentViewCell_Tapped; + BackgroundColor = Color.White; + } + + private void AttachmentViewCell_Tapped(object sender, EventArgs e) + { + _clicked?.Invoke(); + } + } } } diff --git a/src/App/Repositories/AttachmentRepository.cs b/src/App/Repositories/AttachmentRepository.cs new file mode 100644 index 000000000..86f909bf5 --- /dev/null +++ b/src/App/Repositories/AttachmentRepository.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Bit.App.Abstractions; +using Bit.App.Models.Data; + +namespace Bit.App.Repositories +{ + public class AttachmentRepository : Repository, IAttachmentRepository + { + public AttachmentRepository(ISqlService sqlService) + : base(sqlService) + { } + + public Task> GetAllByLoginIdAsync(string loginId) + { + var attachments = Connection.Table().Where(a => a.LoginId == loginId).Cast(); + return Task.FromResult(attachments); + } + } +} diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index 7f08d4d1d..c7768822d 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -124,6 +124,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Add New Attachment. + /// + public static string AddNewAttachment { + get { + return ResourceManager.GetString("AddNewAttachment", resourceCulture); + } + } + /// /// Looks up a localized string similar to An error has occurred.. /// @@ -142,6 +151,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Attachments. + /// + public static string Attachments { + get { + return ResourceManager.GetString("Attachments", resourceCulture); + } + } + /// /// Looks up a localized string similar to Authenticator App. /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index d53c593cb..fc3405956 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -902,4 +902,10 @@ YubiKey NEO Security Key "YubiKey NEO" is the product name and should not be translated. + + Add New Attachment + + + Attachments + \ No newline at end of file diff --git a/src/App/Services/DatabaseService.cs b/src/App/Services/DatabaseService.cs index 9c68a985d..bc03aef43 100644 --- a/src/App/Services/DatabaseService.cs +++ b/src/App/Services/DatabaseService.cs @@ -18,6 +18,7 @@ namespace Bit.App.Services { _connection.CreateTable(); _connection.CreateTable(); + _connection.CreateTable(); _connection.CreateTable(); } } diff --git a/src/App/Services/LoginService.cs b/src/App/Services/LoginService.cs index b1f934bc1..dc5ab2aa9 100644 --- a/src/App/Services/LoginService.cs +++ b/src/App/Services/LoginService.cs @@ -13,17 +13,20 @@ namespace Bit.App.Services public class LoginService : ILoginService { private readonly ILoginRepository _loginRepository; + private readonly IAttachmentRepository _attachmentRepository; private readonly IAuthService _authService; private readonly ILoginApiRepository _loginApiRepository; private readonly ISettingsService _settingsService; public LoginService( ILoginRepository loginRepository, + IAttachmentRepository attachmentRepository, IAuthService authService, ILoginApiRepository loginApiRepository, ISettingsService settingsService) { _loginRepository = loginRepository; + _attachmentRepository = attachmentRepository; _authService = authService; _loginApiRepository = loginApiRepository; _settingsService = settingsService; @@ -37,7 +40,8 @@ namespace Bit.App.Services return null; } - var login = new Login(data); + var attachments = await _attachmentRepository.GetAllByLoginIdAsync(id); + var login = new Login(data, attachments); return login; } diff --git a/src/App/Services/SyncService.cs b/src/App/Services/SyncService.cs index 87ddc80b7..26cd4d219 100644 --- a/src/App/Services/SyncService.cs +++ b/src/App/Services/SyncService.cs @@ -21,6 +21,7 @@ namespace Bit.App.Services private readonly ISettingsApiRepository _settingsApiRepository; private readonly IFolderRepository _folderRepository; private readonly ILoginRepository _loginRepository; + private readonly IAttachmentRepository _attachmentRepository; private readonly ISettingsRepository _settingsRepository; private readonly IAuthService _authService; private readonly ICryptoService _cryptoService; @@ -35,6 +36,7 @@ namespace Bit.App.Services ISettingsApiRepository settingsApiRepository, IFolderRepository folderRepository, ILoginRepository loginRepository, + IAttachmentRepository attachmentRepository, ISettingsRepository settingsRepository, IAuthService authService, ICryptoService cryptoService, @@ -48,6 +50,7 @@ namespace Bit.App.Services _settingsApiRepository = settingsApiRepository; _folderRepository = folderRepository; _loginRepository = loginRepository; + _attachmentRepository = attachmentRepository; _settingsRepository = settingsRepository; _authService = authService; _cryptoService = cryptoService; @@ -79,6 +82,14 @@ namespace Bit.App.Services case Enums.CipherType.Login: var loginData = new LoginData(cipher.Result, _authService.UserId); await _loginRepository.UpsertAsync(loginData).ConfigureAwait(false); + if(cipher.Result.Attachments != null) + { + foreach(var attachment in cipher.Result.Attachments) + { + var attachmentData = new AttachmentData(attachment, loginData.Id); + await _attachmentRepository.UpsertAsync(attachmentData).ConfigureAwait(false); + } + } break; default: SyncCompleted(false); @@ -363,6 +374,15 @@ namespace Bit.App.Services { var data = new LoginData(serverLogin.Value, _authService.UserId); await _loginRepository.UpsertAsync(data).ConfigureAwait(false); + + if(serverLogin.Value.Attachments != null) + { + foreach(var attachment in serverLogin.Value.Attachments) + { + var attachmentData = new AttachmentData(attachment, data.Id); + await _attachmentRepository.UpsertAsync(attachmentData).ConfigureAwait(false); + } + } } catch(SQLite.SQLiteException) { } } diff --git a/src/iOS.Extension/LoadingViewController.cs b/src/iOS.Extension/LoadingViewController.cs index 4622e6094..333eee3e3 100644 --- a/src/iOS.Extension/LoadingViewController.cs +++ b/src/iOS.Extension/LoadingViewController.cs @@ -288,6 +288,7 @@ namespace Bit.iOS.Extension container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); + container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs index bdf7fd14a..81189b4a9 100644 --- a/src/iOS/AppDelegate.cs +++ b/src/iOS/AppDelegate.cs @@ -273,6 +273,7 @@ namespace Bit.iOS container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); + container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton();