sync and display attachments on view login

This commit is contained in:
Kyle Spearrin 2017-07-12 16:23:24 -04:00
parent 18a86d3f12
commit 0a7ad44d23
15 changed files with 169 additions and 5 deletions

View file

@ -227,6 +227,7 @@ namespace Bit.Android
container.RegisterSingleton<IFolderRepository, FolderRepository>();
container.RegisterSingleton<IFolderApiRepository, FolderApiRepository>();
container.RegisterSingleton<ILoginRepository, LoginRepository>();
container.RegisterSingleton<IAttachmentRepository, AttachmentRepository>();
container.RegisterSingleton<ILoginApiRepository, LoginApiRepository>();
container.RegisterSingleton<IConnectApiRepository, ConnectApiRepository>();
container.RegisterSingleton<IDeviceApiRepository, DeviceApiRepository>();

View file

@ -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<AttachmentData, string>
{
Task<IEnumerable<AttachmentData>> GetAllByLoginIdAsync(string loginId);
}
}

View file

@ -35,6 +35,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Compile Include="Abstractions\Repositories\IAttachmentRepository.cs" />
<Compile Include="Abstractions\Repositories\ITwoFactorApiRepository.cs" />
<Compile Include="Abstractions\Repositories\ISettingsApiRepository.cs" />
<Compile Include="Abstractions\Repositories\IAccountsApiRepository.cs" />
@ -161,6 +162,7 @@
<Compile Include="Pages\Vault\VaultAutofillListLoginsPage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Abstractions\Repositories\ILoginRepository.cs" />
<Compile Include="Repositories\AttachmentRepository.cs" />
<Compile Include="Repositories\TwoFactorApiRepository.cs" />
<Compile Include="Repositories\SettingsApiRepository.cs" />
<Compile Include="Repositories\ApiRepository.cs" />

View file

@ -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; }

View file

@ -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;
}

View file

@ -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<Attachment> _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<Attachment> 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<Attachment>();
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; }
}
}
}

View file

@ -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();
}
}
}
}

View file

@ -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<AttachmentData, string>, IAttachmentRepository
{
public AttachmentRepository(ISqlService sqlService)
: base(sqlService)
{ }
public Task<IEnumerable<AttachmentData>> GetAllByLoginIdAsync(string loginId)
{
var attachments = Connection.Table<AttachmentData>().Where(a => a.LoginId == loginId).Cast<AttachmentData>();
return Task.FromResult(attachments);
}
}
}

View file

@ -124,6 +124,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Add New Attachment.
/// </summary>
public static string AddNewAttachment {
get {
return ResourceManager.GetString("AddNewAttachment", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An error has occurred..
/// </summary>
@ -142,6 +151,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Attachments.
/// </summary>
public static string Attachments {
get {
return ResourceManager.GetString("Attachments", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Authenticator App.
/// </summary>

View file

@ -902,4 +902,10 @@
<value>YubiKey NEO Security Key</value>
<comment>"YubiKey NEO" is the product name and should not be translated.</comment>
</data>
<data name="AddNewAttachment" xml:space="preserve">
<value>Add New Attachment</value>
</data>
<data name="Attachments" xml:space="preserve">
<value>Attachments</value>
</data>
</root>

View file

@ -18,6 +18,7 @@ namespace Bit.App.Services
{
_connection.CreateTable<FolderData>();
_connection.CreateTable<LoginData>();
_connection.CreateTable<AttachmentData>();
_connection.CreateTable<SettingsData>();
}
}

View file

@ -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;
}

View file

@ -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) { }
}

View file

@ -288,6 +288,7 @@ namespace Bit.iOS.Extension
container.RegisterSingleton<IFolderRepository, FolderRepository>();
container.RegisterSingleton<IFolderApiRepository, FolderApiRepository>();
container.RegisterSingleton<ILoginRepository, LoginRepository>();
container.RegisterSingleton<IAttachmentRepository, AttachmentRepository>();
container.RegisterSingleton<ILoginApiRepository, LoginApiRepository>();
container.RegisterSingleton<IConnectApiRepository, ConnectApiRepository>();
container.RegisterSingleton<ISettingsRepository, SettingsRepository>();

View file

@ -273,6 +273,7 @@ namespace Bit.iOS
container.RegisterSingleton<IFolderRepository, FolderRepository>();
container.RegisterSingleton<IFolderApiRepository, FolderApiRepository>();
container.RegisterSingleton<ILoginRepository, LoginRepository>();
container.RegisterSingleton<IAttachmentRepository, AttachmentRepository>();
container.RegisterSingleton<ILoginApiRepository, LoginApiRepository>();
container.RegisterSingleton<IConnectApiRepository, ConnectApiRepository>();
container.RegisterSingleton<IDeviceApiRepository, DeviceApiRepository>();