Added lots of commands

Simplified KeePass client
This commit is contained in:
Geoffroy BONNEVILLE
2020-03-26 12:25:22 +01:00
parent 4b1210f414
commit a17d6b05ae
46 changed files with 1057 additions and 246 deletions

View File

@@ -65,8 +65,19 @@
<Compile Include="Database\Queries\IsDatabaseOpen\IsDatabaseOpenQuery.cs" />
<Compile Include="Database\Queries\OpenDatabase\OpenDatabaseQuery.cs" />
<Compile Include="Database\Queries\OpenDatabase\OpenDatabaseQueryValidator.cs" />
<Compile Include="Database\Queries\ReOpenDatabase\ReOpenDatabaseQuery.cs" />
<Compile Include="DependencyInjection.cs" />
<Compile Include="Entry\Models\EntryVm.cs" />
<Compile Include="Group\Commands\AddEntry\AddEntryCommand.cs" />
<Compile Include="Group\Commands\AddGroup\AddGroupCommand.cs" />
<Compile Include="Group\Commands\CreateEntry\CreateEntryCommand.cs" />
<Compile Include="Group\Commands\CreateGroup\CreateGroupCommand.cs" />
<Compile Include="Group\Commands\DeleteEntry\DeleteEntryCommand.cs" />
<Compile Include="Group\Commands\DeleteGroup\DeleteGroupCommand.cs" />
<Compile Include="Group\Commands\RemoveEntry\RemoveEntryCommand.cs" />
<Compile Include="Group\Commands\RemoveGroup\RemoveGroupCommand.cs" />
<Compile Include="Group\Commands\SortEntries\SortEntriesCommand.cs" />
<Compile Include="Group\Commands\SortGroups\SortGroupsCommand.cs" />
<Compile Include="Group\Models\GroupVm.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Content Include="Services\CryptographyService.cs" />
@@ -79,14 +90,13 @@
<Content Include="Services\SettingsService.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Entry\Commands\UpdateEntry\" />
<Folder Include="Entry\Queries\" />
<Folder Include="Group\Commands\CreateEntry\" />
<Folder Include="Group\Commands\CreateGroup\" />
<Folder Include="Group\Commands\DeleteEntry\" />
<Folder Include="Group\Commands\DeleteGroup\" />
<Folder Include="Entry\Commands\SetFieldValue\" />
<Folder Include="Entry\Queries\GetEntry\" />
<Folder Include="Group\Commands\UpdateGroup\" />
<Folder Include="Group\Queries\" />
<Folder Include="Group\Queries\GetGroup\" />
<Folder Include="Parameters\Commands\SetCipher\" />
<Folder Include="Parameters\Commands\SetCompression\" />
<Folder Include="Parameters\Commands\SetKeyDerivation\" />
<Folder Include="Resources\Commands\" />
<Folder Include="Resources\Queries\" />
<Folder Include="Settings\" />

View File

@@ -8,22 +8,34 @@ namespace ModernKeePass.Application.Common.Interfaces
{
bool IsOpen { get; }
string Name { get; }
GroupEntity RecycleBin { get; set; }
BaseEntity Cipher { get; set; }
BaseEntity KeyDerivation { get; set; }
string Compression { get; set; }
GroupEntity RootGroup { get; }
Task<DatabaseEntity> Open(FileInfo fileInfo, Credentials credentials);
Task<DatabaseEntity> Create(FileInfo fileInfo, Credentials credentials);
string RecycleBinId { get; set; }
string CipherId { get; set; }
string KeyDerivationId { get; set; }
string Compression { get; set; }
bool IsRecycleBinEnabled { get; }
Task<GroupEntity> Open(FileInfo fileInfo, Credentials credentials);
Task<GroupEntity> ReOpen();
Task<GroupEntity> Create(FileInfo fileInfo, Credentials credentials);
Task SaveDatabase();
Task SaveDatabase(FileInfo FileInfo);
Task UpdateCredentials(Credentials credentials);
void CloseDatabase();
Task AddEntry(GroupEntity parentGroup, EntryEntity entity);
Task AddGroup(GroupEntity parentGroup, GroupEntity entity);
Task UpdateEntry(EntryEntity entity);
Task UpdateGroup(GroupEntity entity);
Task DeleteEntry(EntryEntity entity);
Task DeleteGroup(GroupEntity entity);
Task AddEntry(string parentGroupId, string entryId);
Task AddGroup(string parentGroupId, string groupId);
Task UpdateEntry(string entryId);
Task UpdateGroup(string groupId);
Task RemoveEntry(string parentGroupId, string entryId);
Task RemoveGroup(string parentGroupId, string groupId);
EntryEntity CreateEntry(string parentGroupId);
GroupEntity CreateGroup(string parentGroupId, string nameId, bool isRecycleBin = false);
Task DeleteEntry(string entryId);
Task DeleteGroup(string groupId);
void SortEntries(string groupId);
void SortSubGroups(string groupId);
}
}

View File

@@ -9,7 +9,6 @@ namespace ModernKeePass.Application.Common.Mappings
{
public void ApplyMappings()
{
new DatabaseVm().Mapping(this);
new EntryVm().Mapping(this);
new GroupVm().Mapping(this);
}

View File

@@ -2,7 +2,6 @@
using System.Threading.Tasks;
using AutoMapper;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Models;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Dtos;
@@ -10,12 +9,12 @@ using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Database.Commands.CreateDatabase
{
public class CreateDatabaseCommand : IRequest<DatabaseVm>
public class CreateDatabaseCommand : IRequest<GroupVm>
{
public FileInfo FileInfo { get; set; }
public Credentials Credentials { get; set; }
public class CreateDatabaseCommandHandler : IAsyncRequestHandler<CreateDatabaseCommand, DatabaseVm>
public class CreateDatabaseCommandHandler : IAsyncRequestHandler<CreateDatabaseCommand, GroupVm>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
@@ -28,19 +27,13 @@ namespace ModernKeePass.Application.Database.Commands.CreateDatabase
_mapper = mapper;
}
public async Task<DatabaseVm> Handle(CreateDatabaseCommand message)
public async Task<GroupVm> Handle(CreateDatabaseCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (isDatabaseOpen) throw new DatabaseOpenException();
var database = await _database.Create(message.FileInfo, message.Credentials);
var databaseVm = new DatabaseVm
{
IsOpen = true,
Name = database.Name,
RootGroup = _mapper.Map<GroupVm>(database.RootGroupEntity)
};
return databaseVm;
var rootGroup = await _database.Create(message.FileInfo, message.Credentials);
return _mapper.Map<GroupVm>(rootGroup);
}
}

View File

@@ -1,21 +1,16 @@
using AutoMapper;
using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Application.Group.Models;
namespace ModernKeePass.Application.Database.Models
{
public class DatabaseVm: IMapFrom<DatabaseEntity>
public class DatabaseVm
{
public bool IsOpen { get; set; }
public string Name { get; set; }
public GroupVm RootGroup { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<DatabaseEntity, DatabaseVm>()
.ForMember(d => d.Name, opts => opts.MapFrom(s => s.Name))
.ForMember(d => d.RootGroup, opts => opts.MapFrom(s => s.RootGroupEntity));
}
public string RecycleBinId { get; set; }
public bool IsRecycleBinEnabled { get; set; }
public string Compression { get; set; }
public string CipherId { get; set; }
public string KeyDerivationId { get; set; }
}
}

View File

@@ -1,6 +1,8 @@
using MediatR;
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Models;
using ModernKeePass.Application.Group.Models;
namespace ModernKeePass.Application.Database.Queries.GetDatabase
{
@@ -9,10 +11,12 @@ namespace ModernKeePass.Application.Database.Queries.GetDatabase
public class GetDatabaseQueryHandler : IRequestHandler<GetDatabaseQuery, DatabaseVm>
{
private readonly IDatabaseProxy _databaseProxy;
private readonly IMapper _mapper;
public GetDatabaseQueryHandler(IDatabaseProxy databaseProxy)
public GetDatabaseQueryHandler(IDatabaseProxy databaseProxy, IMapper mapper)
{
_databaseProxy = databaseProxy;
_mapper = mapper;
}
public DatabaseVm Handle(GetDatabaseQuery request)
@@ -20,7 +24,13 @@ namespace ModernKeePass.Application.Database.Queries.GetDatabase
var database = new DatabaseVm
{
IsOpen = _databaseProxy.IsOpen,
Name = _databaseProxy.Name
Name = _databaseProxy.Name,
RootGroup = _mapper.Map<GroupVm>(_databaseProxy.RootGroup),
IsRecycleBinEnabled = _databaseProxy.IsRecycleBinEnabled,
RecycleBinId = _databaseProxy.RecycleBinId,
Compression = _databaseProxy.Compression,
CipherId = _databaseProxy.CipherId,
KeyDerivationId = _databaseProxy.CipherId
};
return database;
}

View File

@@ -1,9 +1,7 @@
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks;
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Models;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Dtos;
@@ -11,12 +9,12 @@ using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Database.Queries.OpenDatabase
{
public class OpenDatabaseQuery: IRequest<DatabaseVm>
public class OpenDatabaseQuery: IRequest<GroupVm>
{
public FileInfo FileInfo { get; set; }
public Credentials Credentials { get; set; }
public class OpenDatabaseQueryHandler : IAsyncRequestHandler<OpenDatabaseQuery, DatabaseVm>
public class OpenDatabaseQueryHandler : IAsyncRequestHandler<OpenDatabaseQuery, GroupVm>
{
private readonly IMapper _mapper;
private readonly IMediator _mediator;
@@ -29,19 +27,13 @@ namespace ModernKeePass.Application.Database.Queries.OpenDatabase
_databaseProxy = databaseProxy;
}
public async Task<DatabaseVm> Handle(OpenDatabaseQuery request)
public async Task<GroupVm> Handle(OpenDatabaseQuery request)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (isDatabaseOpen) throw new DatabaseOpenException();
var database = await _databaseProxy.Open(request.FileInfo, request.Credentials);
var databaseVm = new DatabaseVm
{
IsOpen = true,
Name = database.Name,
RootGroup = _mapper.Map<GroupVm>(database.RootGroupEntity)
};
return databaseVm;
var rootGroup = await _databaseProxy.Open(request.FileInfo, request.Credentials);
return _mapper.Map<GroupVm>(rootGroup);
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Threading.Tasks;
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Database.Queries.ReOpenDatabase
{
public class ReOpenDatabaseQuery: IRequest<GroupVm>
{
public class ReOpenDatabaseQueryHandler : IAsyncRequestHandler<ReOpenDatabaseQuery, GroupVm>
{
private readonly IMapper _mapper;
private readonly IMediator _mediator;
private readonly IDatabaseProxy _databaseProxy;
public ReOpenDatabaseQueryHandler(IMapper mapper, IMediator mediator, IDatabaseProxy databaseProxy)
{
_mapper = mapper;
_mediator = mediator;
_databaseProxy = databaseProxy;
}
public async Task<GroupVm> Handle(ReOpenDatabaseQuery request)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
var rootGroup = await _databaseProxy.ReOpen();
return _mapper.Map<GroupVm>(rootGroup);
}
}
}
}

View File

@@ -1,4 +1,6 @@
using System.Drawing;
using System;
using System.Collections.Generic;
using System.Drawing;
using AutoMapper;
using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Domain.Entities;
@@ -10,15 +12,33 @@ namespace ModernKeePass.Application.Entry.Models
{
public string Id { get; set; }
public string Title { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Notes { get; set; }
public Uri Url { get; set; }
public Dictionary<string, string> AdditionalFields { get; set; }
public IEnumerable<EntryEntity> History { get; set; }
public Icon Icon { get; set; }
public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; }
public bool HasExpirationDate { get; internal set; }
public DateTimeOffset ExpirationDate { get; internal set; }
public DateTimeOffset ModificationDate { get; internal set; }
public void Mapping(Profile profile)
{
profile.CreateMap<EntryEntity, EntryVm>()
.ForMember(d => d.Id, opts => opts.MapFrom(s => s.Id))
.ForMember(d => d.Title, opts => opts.MapFrom(s => s.Name))
.ForMember(d => d.Username, opts => opts.MapFrom(s => s.UserName))
.ForMember(d => d.Password, opts => opts.MapFrom(s => s.Password))
.ForMember(d => d.Url, opts => opts.MapFrom(s => s.Url))
.ForMember(d => d.Notes, opts => opts.MapFrom(s => s.Notes))
.ForMember(d => d.AdditionalFields, opts => opts.MapFrom(s => s.AdditionalFields))
.ForMember(d => d.History, opts => opts.MapFrom(s => s.History))
.ForMember(d => d.HasExpirationDate, opts => opts.MapFrom(s => s.HasExpirationDate))
.ForMember(d => d.ExpirationDate, opts => opts.MapFrom(s => s.ExpirationDate))
.ForMember(d => d.ModificationDate, opts => opts.MapFrom(s => s.LastModificationDate))
.ForMember(d => d.Icon, opts => opts.MapFrom(s => s.Icon))
.ForMember(d => d.ForegroundColor, opts => opts.MapFrom(s => s.ForegroundColor))
.ForMember(d => d.BackgroundColor, opts => opts.MapFrom(s => s.BackgroundColor));

View File

@@ -0,0 +1,54 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.AddEntry
{
public class AddEntryCommand : IRequest
{
public GroupVm ParentGroup { get; set; }
public EntryVm Entry { get; set; }
public class AddEntryCommandHandler : IAsyncRequestHandler<AddEntryCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
public AddEntryCommandHandler(IDatabaseProxy database, IMediator mediator)
{
_database = database;
_mediator = mediator;
}
public async Task Handle(AddEntryCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
/*var entryEntity = new EntryEntity
{
Id = message.Entry.Id,
Name = message.Entry.Title,
UserName = message.Entry.Username,
Password = message.Entry.Password,
Url = message.Entry.Url,
Notes = message.Entry.Notes,
HasExpirationDate = message.Entry.HasExpirationDate,
ExpirationDate = message.Entry.ExpirationDate,
LastModificationDate = message.Entry.ModificationDate,
BackgroundColor = message.Entry.BackgroundColor,
ForegroundColor = message.Entry.ForegroundColor,
Icon = message.Entry.Icon,
AdditionalFields = message.Entry.AdditionalFields,
History = message.Entry.History
};*/
await _database.AddEntry(message.ParentGroup.Id, message.Entry.Id);
message.ParentGroup.Entries.Add(message.Entry);
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.AddGroup
{
public class AddGroupCommand : IRequest
{
public GroupVm ParentGroup { get; set; }
public GroupVm Group { get; set; }
public class AddGroupCommandHandler : IAsyncRequestHandler<AddGroupCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
public AddGroupCommandHandler(IDatabaseProxy database, IMediator mediator)
{
_database = database;
_mediator = mediator;
}
public async Task Handle(AddGroupCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
await _database.AddGroup(message.ParentGroup.Id, message.Group.Id);
message.ParentGroup.SubGroups.Add(message.Group);
}
}
}
}

View File

@@ -0,0 +1,41 @@
using System.Threading.Tasks;
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.CreateEntry
{
public class CreateEntryCommand : IRequest<EntryVm>
{
public GroupVm ParentGroup { get; set; }
public class CreateEntryCommandHandler : IAsyncRequestHandler<CreateEntryCommand, EntryVm>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
private readonly IMapper _mapper;
public CreateEntryCommandHandler(IDatabaseProxy database, IMediator mediator, IMapper mapper)
{
_database = database;
_mediator = mediator;
_mapper = mapper;
}
public async Task<EntryVm> Handle(CreateEntryCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
var entry = _database.CreateEntry(message.ParentGroup.Id);
var entryVm = _mapper.Map<EntryVm>(entry);
message.ParentGroup.Entries.Add(entryVm);
return entryVm;
}
}
}
}

View File

@@ -0,0 +1,42 @@
using System.Threading.Tasks;
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.CreateGroup
{
public class CreateGroupCommand : IRequest<GroupVm>
{
public GroupVm ParentGroup { get; set; }
public string Name { get; set; }
public bool IsRecycleBin { get; set; }
public class CreateGroupCommandHandler : IAsyncRequestHandler<CreateGroupCommand, GroupVm>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
private readonly IMapper _mapper;
public CreateGroupCommandHandler(IDatabaseProxy database, IMediator mediator, IMapper mapper)
{
_database = database;
_mediator = mediator;
_mapper = mapper;
}
public async Task<GroupVm> Handle(CreateGroupCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
var group = _database.CreateGroup(message.ParentGroup.Id, message.Name, message.IsRecycleBin);
var groupVm = _mapper.Map<GroupVm>(group);
message.ParentGroup.SubGroups.Add(groupVm);
return groupVm;
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.DeleteEntry
{
public class DeleteEntryCommand : IRequest
{
public EntryVm Entry { get; set; }
public class DeleteEntryCommandHandler : IAsyncRequestHandler<DeleteEntryCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
public DeleteEntryCommandHandler(IDatabaseProxy database, IMediator mediator)
{
_database = database;
_mediator = mediator;
}
public async Task Handle(DeleteEntryCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
await _database.DeleteEntry(message.Entry.Id);
message.Entry = null;
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.DeleteGroup
{
public class DeleteGroupCommand : IRequest
{
public GroupVm Group { get; set; }
public class DeleteGroupCommandHandler : IAsyncRequestHandler<DeleteGroupCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
public DeleteGroupCommandHandler(IDatabaseProxy database, IMediator mediator)
{
_database = database;
_mediator = mediator;
}
public async Task Handle(DeleteGroupCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
await _database.DeleteGroup(message.Group.Id);
message.Group = null;
}
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.RemoveEntry
{
public class RemoveEntryCommand : IRequest
{
public GroupVm ParentGroup { get; set; }
public EntryVm Entry { get; set; }
public class RemoveEntryCommandHandler : IAsyncRequestHandler<RemoveEntryCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
public RemoveEntryCommandHandler(IDatabaseProxy database, IMediator mediator)
{
_database = database;
_mediator = mediator;
}
public async Task Handle(RemoveEntryCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
await _database.RemoveEntry(message.ParentGroup.Id, message.Entry.Id);
message.ParentGroup.Entries.Remove(message.Entry);
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.RemoveGroup
{
public class RemoveGroupCommand : IRequest
{
public GroupVm ParentGroup { get; set; }
public GroupVm Group { get; set; }
public class RemoveGroupCommandHandler : IAsyncRequestHandler<RemoveGroupCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
public RemoveGroupCommandHandler(IDatabaseProxy database, IMediator mediator)
{
_database = database;
_mediator = mediator;
}
public async Task Handle(RemoveGroupCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
await _database.RemoveGroup(message.ParentGroup.Id, message.Group.Id);
message.ParentGroup.SubGroups.Remove(message.Group);
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.SortEntries
{
public class SortEntriesCommand : IRequest
{
public GroupVm Group { get; set; }
public class SortEntriesCommandHandler : IAsyncRequestHandler<SortEntriesCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
public SortEntriesCommandHandler(IDatabaseProxy database, IMediator mediator)
{
_database = database;
_mediator = mediator;
}
public async Task Handle(SortEntriesCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
_database.SortEntries(message.Group.Id);
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Queries.IsDatabaseOpen;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.SortGroups
{
public class SortGroupsCommand : IRequest
{
public GroupVm Group { get; set; }
public class SortGroupsCommandHandler : IAsyncRequestHandler<SortGroupsCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMediator _mediator;
public SortGroupsCommandHandler(IDatabaseProxy database, IMediator mediator)
{
_database = database;
_mediator = mediator;
}
public async Task Handle(SortGroupsCommand message)
{
var isDatabaseOpen = await _mediator.Send(new IsDatabaseOpenQuery());
if (!isDatabaseOpen) throw new DatabaseClosedException();
_database.SortSubGroups(message.Group.Id);
}
}
}
}

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using AutoMapper;
using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Application.Entry.Models;
@@ -13,8 +13,6 @@ namespace ModernKeePass.Application.Group.Models
public string Id { get; set; }
public string Title { get; set; }
public Icon Icon { get; set; }
public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; }
public List<GroupVm> SubGroups { get; set; } = new List<GroupVm>();
public List<EntryVm> Entries { get; set; } = new List<EntryVm>();
@@ -24,9 +22,7 @@ namespace ModernKeePass.Application.Group.Models
.ForMember(d => d.Id, opts => opts.MapFrom(s => s.Id))
.ForMember(d => d.Title, opts => opts.MapFrom(s => s.Name))
.ForMember(d => d.Icon, opts => opts.MapFrom(s => s.Icon))
.ForMember(d => d.ForegroundColor, opts => opts.MapFrom(s => s.ForegroundColor))
.ForMember(d => d.BackgroundColor, opts => opts.MapFrom(s => s.BackgroundColor))
.ForMember(d => d.Entries, opts => opts.MapFrom(s => s.Entries))
.ForMember(d => d.Entries, opts => opts.MapFrom(s => s.Entries.OrderBy(e => e.Name)))
.ForMember(d => d.SubGroups, opts => opts.MapFrom(s => s.SubGroups));
}
}

View File

@@ -44,7 +44,6 @@
<Compile Include="Dtos\FileInfo.cs" />
<Compile Include="Dtos\PasswordGenerationOptions.cs" />
<Compile Include="Entities\BaseEntity.cs" />
<Compile Include="Entities\DatabaseEntity.cs" />
<Compile Include="Entities\EntryEntity.cs" />
<Compile Include="Entities\GroupEntity.cs" />
<Compile Include="Enums\CredentialStatusTypes.cs" />
@@ -54,8 +53,10 @@
<Compile Include="Exceptions\DatabaseOpenException.cs" />
<Compile Include="Exceptions\NavigationException.cs" />
<Compile Include="Exceptions\SaveException.cs" />
<Compile Include="Interfaces\IDateTime.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -1,5 +1,4 @@
using System;
using System.Drawing;
namespace ModernKeePass.Domain.Entities
{
@@ -7,8 +6,6 @@ namespace ModernKeePass.Domain.Entities
{
public string Id { get; set; }
public string Name { get; set; }
public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; }
public DateTimeOffset LastModificationDate { get; set; }
}
}

View File

@@ -1,9 +0,0 @@
namespace ModernKeePass.Domain.Entities
{
public class DatabaseEntity
{
public string Name { get; set; }
public GroupEntity RootGroupEntity { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Domain.Entities
@@ -12,8 +13,10 @@ namespace ModernKeePass.Domain.Entities
public string Notes { get; set; }
public DateTimeOffset ExpirationDate { get; set; }
public Dictionary<string, string> AdditionalFields { get; set; } = new Dictionary<string, string>();
public IEnumerable<EntryEntity> History { get; set; }
public IEnumerable<EntryEntity> History { get; set; } = new List<EntryEntity>();
public Icon Icon { get; set; }
public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; }
public bool HasExpirationDate { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace ModernKeePass.Domain.Interfaces
{
public interface IDateTime
{
DateTime Now { get; }
}
}

View File

@@ -0,0 +1,10 @@
using System;
using ModernKeePass.Domain.Interfaces;
namespace ModernKeePass.Infrastructure.Common
{
public class MachineDateTime: IDateTime
{
public DateTime Now => DateTime.Now;
}
}

View File

@@ -40,10 +40,12 @@
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Common\MachineDateTime.cs" />
<Compile Include="DependencyInjection.cs" />
<Compile Include="InfrastructureModule.cs" />
<Compile Include="File\CsvImportFormat.cs" />
<Compile Include="KeePass\EntryMappingProfile.cs" />
<Compile Include="KeePass\GroupMappingProfile.cs" />
<Compile Include="KeePass\IconMapper.cs" />
<Compile Include="KeePass\KeePassCryptographyClient.cs" />
<Compile Include="KeePass\KeePassDatabaseClient.cs" />
@@ -71,6 +73,7 @@
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -0,0 +1,31 @@
using AutoMapper;
using ModernKeePass.Domain.Entities;
using ModernKeePassLib;
namespace ModernKeePass.Infrastructure.KeePass
{
public class GroupMappingProfile : Profile
{
public GroupMappingProfile()
{
FromModelToDto();
FromDtoToModel();
}
private void FromDtoToModel()
{
CreateMap<PwGroup, GroupEntity>()
.ForMember(d => d.Id, opts => opts.MapFrom(s => s.Uuid.ToHexString()))
.ForMember(d => d.Name, opts => opts.MapFrom(s => s.Name))
.ForMember(d => d.Icon, opts => opts.MapFrom(s => IconMapper.MapPwIconToIcon(s.IconId)))
.ForMember(d => d.LastModificationDate, opts => opts.MapFrom(s => s.LastModificationTime))
.ForMember(d => d.Entries, opts => opts.MapFrom(s => s.Entries))
.ForMember(d => d.SubGroups, opts => opts.MapFrom(s => s.Groups));
}
private void FromModelToDto()
{
throw new System.NotImplementedException();
}
}
}

View File

@@ -7,7 +7,6 @@ using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Exceptions;
using ModernKeePassLib;
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Keys;
@@ -23,68 +22,41 @@ namespace ModernKeePass.Infrastructure.KeePass
private readonly IMapper _mapper;
private readonly PwDatabase _pwDatabase = new PwDatabase();
private string _fileAccessToken;
private CompositeKey _compositeKey;
private Credentials _credentials;
// Main information
public bool IsOpen => (_pwDatabase?.IsOpen).GetValueOrDefault();
public string Name => _pwDatabase?.Name;
public GroupEntity RootGroup { get; set; }
public GroupEntity RootGroup { get; private set; }
// Settings
public GroupEntity RecycleBin
public string RecycleBinId
{
get
{
if (_pwDatabase.RecycleBinEnabled)
{
var pwGroup = _pwDatabase.RootGroup.FindGroup(_pwDatabase.RecycleBinUuid, true);
var group = new GroupEntity
{
Id = pwGroup.Uuid.ToHexString(),
Name = pwGroup.Name,
Icon = IconMapper.MapPwIconToIcon(pwGroup.IconId),
Entries = pwGroup.Entries.Select(e => _mapper.Map<EntryEntity>(e)).ToList(),
SubGroups = pwGroup.Groups.Select(BuildHierarchy).ToList()
};
return group;
return pwGroup.Uuid.ToHexString();
}
return null;
}
set
{
set { _pwDatabase.RecycleBinUuid = BuildIdFromString(value); }
}
}
public BaseEntity Cipher
public string CipherId
{
get
{
var cipher = CipherPool.GlobalPool.GetCipher(_pwDatabase.DataCipherUuid);
return new BaseEntity
{
Id = cipher.CipherUuid.ToHexString(),
Name = cipher.DisplayName
};
}
set { _pwDatabase.DataCipherUuid = BuildIdFromString(value.Id); }
get { return _pwDatabase.DataCipherUuid.ToHexString(); }
set { _pwDatabase.DataCipherUuid = BuildIdFromString(value); }
}
public BaseEntity KeyDerivation
public string KeyDerivationId
{
get
{
var keyDerivation = KdfPool.Engines.First(e => e.Uuid.Equals(_pwDatabase.KdfParameters.KdfUuid));
return new BaseEntity
{
Id = keyDerivation.Uuid.ToHexString(),
Name = keyDerivation.Name
};
}
get { return _pwDatabase.KdfParameters.KdfUuid.ToHexString(); }
set
{
_pwDatabase.KdfParameters = KdfPool.Engines
.FirstOrDefault(e => e.Uuid.Equals(BuildIdFromString(value.Name)))?.GetDefaultParameters();
.FirstOrDefault(e => e.Uuid.Equals(BuildIdFromString(value)))?.GetDefaultParameters();
}
}
@@ -94,6 +66,8 @@ namespace ModernKeePass.Infrastructure.KeePass
set { _pwDatabase.Compression = (PwCompressionAlgorithm) Enum.Parse(typeof(PwCompressionAlgorithm), value); }
}
public bool IsRecycleBinEnabled => _pwDatabase.RecycleBinEnabled;
public KeePassDatabaseClient(ISettingsProxy settings, IFileProxy fileService, IMapper mapper)
{
_settings = settings;
@@ -101,22 +75,19 @@ namespace ModernKeePass.Infrastructure.KeePass
_mapper = mapper;
}
public async Task<DatabaseEntity> Open(FileInfo fileInfo, Credentials credentials)
public async Task<GroupEntity> Open(FileInfo fileInfo, Credentials credentials)
{
try
{
_compositeKey = await CreateCompositeKey(credentials);
var compositeKey = await CreateCompositeKey(credentials);
var ioConnection = await BuildConnectionInfo(fileInfo);
_pwDatabase.Open(ioConnection, _compositeKey, new NullStatusLogger());
_pwDatabase.Open(ioConnection, compositeKey, new NullStatusLogger());
_credentials = credentials;
_fileAccessToken = fileInfo.Path;
return new DatabaseEntity
{
Name = _pwDatabase.Name,
RootGroupEntity = BuildHierarchy(_pwDatabase.RootGroup)
};
return _mapper.Map<GroupEntity>(_pwDatabase.RootGroup);
}
catch (InvalidCompositeKeyException ex)
{
@@ -124,12 +95,17 @@ namespace ModernKeePass.Infrastructure.KeePass
}
}
public async Task<DatabaseEntity> Create(FileInfo fileInfo, Credentials credentials)
public async Task<GroupEntity> ReOpen()
{
_compositeKey = await CreateCompositeKey(credentials);
return await Open(new FileInfo {Path = _fileAccessToken}, _credentials);
}
public async Task<GroupEntity> Create(FileInfo fileInfo, Credentials credentials)
{
var compositeKey = await CreateCompositeKey(credentials);
var ioConnection = await BuildConnectionInfo(fileInfo);
_pwDatabase.New(ioConnection, _compositeKey);
_pwDatabase.New(ioConnection, compositeKey);
var fileFormat = _settings.GetSetting<string>("DefaultFileFormat");
switch (fileFormat)
@@ -142,10 +118,7 @@ namespace ModernKeePass.Infrastructure.KeePass
_fileAccessToken = fileInfo.Path;
// TODO: create sample data depending on settings
return new DatabaseEntity
{
RootGroupEntity = BuildHierarchy(_pwDatabase.RootGroup)
};
return _mapper.Map<GroupEntity>(_pwDatabase.RootGroup);
}
public async Task SaveDatabase()
@@ -185,49 +158,78 @@ namespace ModernKeePass.Infrastructure.KeePass
_fileService.ReleaseFile(_fileAccessToken);
}
public async Task AddEntry(GroupEntity parentGroupEntity, EntryEntity entry)
public async Task AddEntry(string parentGroupId, string entryId)
{
await Task.Run(() =>
{
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupEntity.Id), true);
var pwEntry = new PwEntry(true, true);
_mapper.Map(entry, pwEntry);
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
parentPwGroup.AddEntry(pwEntry, true);
entry.Id = pwEntry.Uuid.ToHexString();
});
}
public async Task AddGroup(GroupEntity parentGroupEntity, GroupEntity group)
public async Task AddGroup(string parentGroupId, string groupId)
{
await Task.Run(() =>
{
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupEntity.Id), true);
var pwGroup = new PwGroup(true, true)
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true);
parentPwGroup.Groups.Add(pwGroup);
});
}
public async Task RemoveEntry(string parentGroupId, string entryId)
{
Name = group.Name
};
parentPwGroup.AddGroup(pwGroup, true);
group.Id = pwGroup.Uuid.ToHexString();
await Task.Run(() =>
{
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
var pwEntry = parentPwGroup.FindEntry(BuildIdFromString(entryId), false);
parentPwGroup.Entries.Remove(pwEntry);
});
}
public Task UpdateEntry(EntryEntity entry)
public async Task RemoveGroup(string parentGroupId, string groupId)
{
await Task.Run(() =>
{
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
var pwGroup = parentPwGroup.FindGroup(BuildIdFromString(groupId), false);
parentPwGroup.Groups.Remove(pwGroup);
});
}
public Task UpdateEntry(string entry)
{
throw new NotImplementedException();
}
public Task UpdateGroup(GroupEntity group)
public Task UpdateGroup(string group)
{
throw new NotImplementedException();
}
public async Task DeleteEntry(EntryEntity entry)
public EntryEntity CreateEntry(string parentGroupId)
{
var pwEntry = new PwEntry(true, true);
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
parentPwGroup.Entries.Add(pwEntry);
return _mapper.Map<EntryEntity>(pwEntry);
}
public GroupEntity CreateGroup(string parentGroupId, string name, bool isRecycleBin = false)
{
var pwGroup = new PwGroup(true, true, name, isRecycleBin? PwIcon.TrashBin : PwIcon.Folder);
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
parentPwGroup.Groups.Add(pwGroup);
if (isRecycleBin) _pwDatabase.RecycleBinUuid = pwGroup.Uuid;
return _mapper.Map<GroupEntity>(pwGroup);
}
public async Task DeleteEntry(string entryId)
{
await Task.Run(() =>
{
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entry.Id), true);
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
var id = pwEntry.Uuid;
pwEntry.ParentGroup.Entries.Remove(pwEntry);
@@ -237,11 +239,12 @@ namespace ModernKeePass.Infrastructure.KeePass
}
});
}
public async Task DeleteGroup(GroupEntity group)
public async Task DeleteGroup(string groupId)
{
await Task.Run(() =>
{
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(group.Id), true);
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true);
var id = pwGroup.Uuid;
pwGroup.ParentGroup.Groups.Remove(pwGroup);
@@ -252,6 +255,19 @@ namespace ModernKeePass.Infrastructure.KeePass
});
}
public void SortEntries(string groupId)
{
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true);
var comparer = new PwEntryComparer(PwDefs.TitleField, true, false);
pwGroup.Entries.Sort(comparer);
}
public void SortSubGroups(string groupId)
{
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true);
pwGroup.SortSubGroups(false);
}
public async Task UpdateCredentials(Credentials credentials)
{
_pwDatabase.MasterKey = await CreateCompositeKey(credentials);
@@ -275,20 +291,6 @@ namespace ModernKeePass.Infrastructure.KeePass
return IOConnectionInfo.FromByteArray(fileContents);
}
private GroupEntity BuildHierarchy(PwGroup pwGroup)
{
// TODO: build entity hierarchy in an iterative way or implement lazy loading
var group = new GroupEntity
{
Id = pwGroup.Uuid.ToHexString(),
Name = pwGroup.Name,
Icon = IconMapper.MapPwIconToIcon(pwGroup.IconId),
Entries = pwGroup.Entries.Select(e => _mapper.Map<EntryEntity>(e)).ToList(),
SubGroups = pwGroup.Groups.Select(BuildHierarchy).ToList()
};
return group;
}
private PwUuid BuildIdFromString(string id)
{
return new PwUuid(MemUtil.HexStringToByteArray(id));

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using AutoMapper;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Infrastructure.KeePass;
using ModernKeePassLib;
using ModernKeePassLib.Security;
using NUnit.Framework;
namespace ModernKeePass.KeePassDatabaseTests
{
[TestFixture]
public class AutomapperProfilesTest
{
private IMapper _mapper;
[SetUp]
public void SetUp()
{
_mapper = new Mapper(new MapperConfiguration(conf => conf.AddProfile(new EntryMappingProfile())));
}
[Test]
public void Assert_Mapping_Configuration_Is_Valid()
{
_mapper.ConfigurationProvider.AssertConfigurationIsValid();
}
[Test]
public void FromDtoToModel_Should_Map_PwEntry_To_Entry()
{
var pwEntry = new PwEntry(true, true)
{
ExpiryTime = DateTime.Now,
BackgroundColor = Color.White,
ForegroundColor = Color.Black
};
pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(true, "Test"));
pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(true, "toto"));
pwEntry.Strings.Set(PwDefs.PasswordField, new ProtectedString(true, "password"));
pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(true, "http://google.com"));
pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(true, "blabla"));
pwEntry.Strings.Set("additional", new ProtectedString(true, "custom"));
var entry = _mapper.Map<PwEntry, EntryEntity>(pwEntry);
Assert.That(entry.ExpirationDate, Is.Not.EqualTo(default(DateTimeOffset)));
Assert.That(entry.BackgroundColor, Is.EqualTo(Color.White));
Assert.That(entry.ForegroundColor, Is.EqualTo(Color.Black));
Assert.That(entry.Name, Is.EqualTo("Test"));
Assert.That(entry.UserName, Is.EqualTo("toto"));
Assert.That(entry.Password, Is.EqualTo("password"));
Assert.That(entry.Url, Is.EqualTo(new Uri("http://google.com")));
Assert.That(entry.Notes, Is.EqualTo("blabla"));
Assert.That(entry.AdditionalFields, Is.Not.Empty);
Assert.That(entry.AdditionalFields["additional"], Is.EqualTo("custom"));
}
[Test]
public void FromModelToDto_Should_Map_Entry_To_PwEntry()
{
var entry = new EntryEntity
{
Id = "VGhlIHF1aWNrIGJyb3duIA==",
Name = "Test",
UserName = "toto",
Password = "password",
Url = new Uri("http://google.com"),
Notes = "blabla",
ExpirationDate = DateTimeOffset.Now,
BackgroundColor = Color.White,
ForegroundColor = Color.Black,
AdditionalFields = new Dictionary<string, string> {
{
"additional", "custom"
}
}
};
var pwEntry = new PwEntry(false, false);
_mapper.Map(entry, pwEntry);
Assert.That(pwEntry.ExpiryTime, Is.Not.EqualTo(default(DateTime)));
Assert.That(pwEntry.BackgroundColor, Is.EqualTo(Color.White));
Assert.That(pwEntry.ForegroundColor, Is.EqualTo(Color.Black));
Assert.That(pwEntry.Strings.GetSafe(PwDefs.TitleField).ReadString(), Is.EqualTo("Test"));
Assert.That(pwEntry.Strings.GetSafe(PwDefs.UserNameField).ReadString(), Is.EqualTo("toto"));
Assert.That(pwEntry.Strings.GetSafe(PwDefs.PasswordField).ReadString(), Is.EqualTo("password"));
Assert.That(pwEntry.Strings.GetSafe(PwDefs.UrlField).ReadString(), Is.EqualTo(new Uri("http://google.com")));
Assert.That(pwEntry.Strings.GetSafe(PwDefs.NotesField).ReadString(), Is.EqualTo("blabla"));
Assert.That(pwEntry.Strings.GetSafe("additional").ReadString(), Is.EqualTo("custom"));
}
}
}

View File

@@ -0,0 +1,155 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Services;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Interfaces;
using ModernKeePass.Infrastructure.KeePass;
using NSubstitute;
using NUnit.Framework;
using FileInfo = ModernKeePass.Domain.Dtos.FileInfo;
namespace ModernKeePass.KeePassDatabaseTests
{
[TestFixture]
public class DatabaseTests
{
private IDatabaseProxy _database;
private FileInfo _fileInfo;
private readonly Credentials _credentials = new Credentials
{
Password = "test"
};
[SetUp]
public void SetUp()
{
var settingsService = Substitute.For<ISettingsService>();
var fileProxy = Substitute.For<IFileProxy>();
fileProxy.OpenBinaryFile(Arg.Any<string>()).Returns(async parameters =>
{
await using var stream = File.Open((string) parameters[0], FileMode.OpenOrCreate);
var contents = new byte[stream.Length];
await stream.ReadAsync(contents, 0, (int) stream.Length);
return contents;
});
fileProxy.WriteBinaryContentsToFile(Arg.Any<string>(), Arg.Any<byte[]>()).Returns(async parameters =>
{
await using var stream = File.Open((string)parameters[0], FileMode.OpenOrCreate);
var contents = (byte[]) parameters[1];
await stream.WriteAsync(contents, 0, contents.Length);
});
var fileService = new FileService(fileProxy);
var mapper = new Mapper(new MapperConfiguration(cfg => { cfg.AddProfile(typeof(EntryMappingProfile)); }));
_database = new KeePassDatabaseClient(settingsService, fileService, mapper);
}
[TearDown]
public void TearDown()
{
_database.CloseDatabase();
if (!string.IsNullOrEmpty(_fileInfo?.Path)) File.Delete(_fileInfo.Path);
}
[Test]
public async Task TestOpen()
{
var FileInfo = new FileInfo
{
Path = Path.Combine(Directory.GetCurrentDirectory(), "Data", "TestDatabase.kdbx")
};
var rootGroup = await _database.Open(FileInfo, _credentials);
Assert.That(rootGroup.Name, Is.EqualTo("TestDatabase"));
Assert.That(rootGroup.Entries.Count(), Is.EqualTo(2));
}
[Test]
public async Task TestCreateAndSave()
{
_fileInfo = new FileInfo
{
Path = Path.Combine(Path.GetTempPath(), "NewDatabase.kdbx")
};
await _database.Create(_fileInfo, _credentials);
await _database.SaveDatabase();
_database.CloseDatabase();
Assert.DoesNotThrowAsync(async () => { await _database.Open(_fileInfo, _credentials); });
}
[Test]
public async Task TestSaveAs()
{
var originalFileInfo = new FileInfo
{
Path = Path.Combine(Directory.GetCurrentDirectory(), "Data", "TestDatabase.kdbx")
};
_fileInfo = new FileInfo
{
Path = Path.Combine(Path.GetTempPath(), "SavedDatabase.kdbx")
};
await _database.Open(originalFileInfo, _credentials);
await _database.SaveDatabase(_fileInfo);
_database.CloseDatabase();
Assert.DoesNotThrowAsync(async () => { await _database.Open(_fileInfo, _credentials); });
}
[Test]
public async Task TestAddGroup()
{
var originalFileInfo = new FileInfo
{
Path = Path.Combine(Directory.GetCurrentDirectory(), "Data", "TestDatabase.kdbx")
};
_fileInfo = new FileInfo
{
Path = Path.Combine(Path.GetTempPath(), "SavedDatabase.kdbx")
};
var newGroup = new GroupEntity {Name = "New Group Test"};
var rootGroup = await _database.Open(originalFileInfo, _credentials);
await _database.AddEntity(rootGroup, newGroup);
await _database.SaveDatabase(_fileInfo);
_database.CloseDatabase();
rootGroup = await _database.Open(_fileInfo, _credentials);
Assert.That(newGroup.Id, Is.Not.Empty);
Assert.That(rootGroup.SubGroups.Count, Is.EqualTo(7));
Assert.That(rootGroup.SubGroups.Last().Name, Is.EqualTo("New Group Test"));
}
[Test]
public async Task TestAddEntry()
{
var originalFileInfo = new FileInfo
{
Path = Path.Combine(Directory.GetCurrentDirectory(), "Data", "TestDatabase.kdbx")
};
_fileInfo = new FileInfo
{
Path = Path.Combine(Path.GetTempPath(), "SavedDatabase.kdbx")
};
var newEntry = new EntryEntity
{
Name = "New Entry Test"
};
var rootGroup = await _database.Open(originalFileInfo, _credentials);
await _database.AddEntity(rootGroup, newEntry);
await _database.SaveDatabase(_fileInfo);
_database.CloseDatabase();
rootGroup = await _database.Open(_fileInfo, _credentials);
Assert.That(newEntry.Id, Is.Not.Empty);
Assert.That(rootGroup.Entries.Count, Is.EqualTo(3));
Assert.That(rootGroup.Entries.Last().Name, Is.EqualTo("New Entry Test"));
}
}
}

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<None Remove="Data\TestDatabase.kdbx" />
</ItemGroup>
<ItemGroup>
<Content Include="Data\TestDatabase.kdbx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="9.0.0" />
<PackageReference Include="ModernKeePassLib" Version="2.44.1" />
<PackageReference Include="NSubstitute" Version="4.2.1" />
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Splat" Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ModernKeePass.Application\Application.csproj" />
<ProjectReference Include="..\ModernKeePass.Domain\Domain.csproj" />
<ProjectReference Include="..\ModernKeePass.Infrastructure\Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio Version 16
VisualStudioVersion = 16.0.29911.84
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Win81App", "ModernKeePass\Win81App.csproj", "{A0CFC681-769B-405A-8482-0CDEE595A91F}"
EndProject
@@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "ModernKeePass.Dom
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "ModernKeePass.Infrastructure\Infrastructure.csproj", "{09577E4C-4899-45B9-BF80-1803D617CCAE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModernKeePass.KeePassDatabaseTests", "ModernKeePass.KeePassDatabaseTests\ModernKeePass.KeePassDatabaseTests.csproj", "{52FEA1EE-2FA7-4862-85FE-CB05893D439E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -134,6 +136,22 @@ Global
{09577E4C-4899-45B9-BF80-1803D617CCAE}.Release|x64.Build.0 = Release|Any CPU
{09577E4C-4899-45B9-BF80-1803D617CCAE}.Release|x86.ActiveCfg = Release|Any CPU
{09577E4C-4899-45B9-BF80-1803D617CCAE}.Release|x86.Build.0 = Release|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Debug|ARM.Build.0 = Debug|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Debug|x64.ActiveCfg = Debug|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Debug|x64.Build.0 = Debug|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Debug|x86.ActiveCfg = Debug|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Debug|x86.Build.0 = Debug|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Release|Any CPU.Build.0 = Release|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Release|ARM.ActiveCfg = Release|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Release|ARM.Build.0 = Release|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Release|x64.ActiveCfg = Release|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Release|x64.Build.0 = Release|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Release|x86.ActiveCfg = Release|Any CPU
{52FEA1EE-2FA7-4862-85FE-CB05893D439E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -145,6 +163,7 @@ Global
{42353562-5E43-459C-8E3E-2F21E575261D} = {0B30588B-07B8-4A88-A268-F58D06EA1627}
{9A0759F1-9069-4841-99E3-3BEC44E17356} = {0B30588B-07B8-4A88-A268-F58D06EA1627}
{09577E4C-4899-45B9-BF80-1803D617CCAE} = {0B30588B-07B8-4A88-A268-F58D06EA1627}
{52FEA1EE-2FA7-4862-85FE-CB05893D439E} = {107C7C00-56F4-41B0-A8CC-0156C46A3650}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0ADC1BC6-B1CA-427D-A97C-3CA40AAB0428}

View File

@@ -10,14 +10,14 @@ namespace ModernKeePass.Actions
{
public class DeleteEntityAction : DependencyObject, IAction
{
public IPwEntity Entity
public IVmEntity Entity
{
get { return (IPwEntity)GetValue(EntityProperty); }
get { return (IVmEntity)GetValue(EntityProperty); }
set { SetValue(EntityProperty, value); }
}
public static readonly DependencyProperty EntityProperty =
DependencyProperty.Register("Entity", typeof(IPwEntity), typeof(DeleteEntityAction),
DependencyProperty.Register("Entity", typeof(IVmEntity), typeof(DeleteEntityAction),
new PropertyMetadata(null));
public ICommand Command

View File

@@ -17,6 +17,7 @@ using ModernKeePass.Application;
using ModernKeePass.Application.Database.Commands.CloseDatabase;
using ModernKeePass.Application.Database.Commands.SaveDatabase;
using ModernKeePass.Application.Database.Queries.GetDatabase;
using ModernKeePass.Application.Database.Queries.ReOpenDatabase;
using ModernKeePass.Common;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Exceptions;
@@ -157,9 +158,9 @@ namespace ModernKeePass
Window.Current.Content = rootFrame;
}
var lauchActivatedEventArgs = e as LaunchActivatedEventArgs;
if (lauchActivatedEventArgs != null && rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage), lauchActivatedEventArgs.Arguments);
var launchActivatedEventArgs = e as LaunchActivatedEventArgs;
if (launchActivatedEventArgs != null && rootFrame.Content == null)
rootFrame.Navigate(typeof(MainPage), launchActivatedEventArgs.Arguments);
// Ensure the current window is active
Window.Current.Activate();
@@ -171,7 +172,7 @@ namespace ModernKeePass
try
{
var database = await Mediator.Send(new GetDatabaseQuery());
var database = await Mediator.Send(new ReOpenDatabaseQuery());
#if DEBUG
ToastNotificationHelper.ShowGenericToast(database.Name, "Database reopened (changes were saved)");
#endif
@@ -207,7 +208,6 @@ namespace ModernKeePass
var deferral = e.SuspendingOperation.GetDeferral();
try
{
var database = await Mediator.Send(new GetDatabaseQuery());
if (SettingsService.Instance.GetSetting("SaveSuspend", true))
{
await Mediator.Send(new SaveDatabaseCommand());

View File

@@ -9,7 +9,7 @@ namespace ModernKeePass.Common
{
public static class ToastNotificationHelper
{
public static void ShowMovedToast(IPwEntity entity, string action, string text)
public static void ShowMovedToast(IVmEntity entity, string action, string text)
{
var notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var toastElements = notificationXml.GetElementsByTagName("text");

View File

@@ -1,5 +1,5 @@
using System;
using ModernKeePass.ViewModels;
using ModernKeePass.Application.Group.Models;
namespace ModernKeePass.Events
{

View File

@@ -1,17 +1,18 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Interfaces
{
public interface IPwEntity
public interface IVmEntity
{
GroupVm ParentGroup { get; }
GroupVm PreviousGroup { get; }
int IconId { get; }
string Id { get; }
string Name { get; set; }
IEnumerable<IPwEntity> BreadCrumb { get; }
IEnumerable<IVmEntity> BreadCrumb { get; }
bool IsEditMode { get; }
bool IsRecycleOnDelete { get; }
@@ -31,10 +32,10 @@ namespace ModernKeePass.Interfaces
/// <summary>
/// Delete from Model
/// </summary>
void CommitDelete();
Task CommitDelete();
/// <summary>
/// Delete from ViewModel
/// </summary>
void MarkForDelete(string recycleBinTitle);
Task MarkForDelete(string recycleBinTitle);
}
}

View File

@@ -99,7 +99,7 @@ namespace ModernKeePass.ViewModels
set { SetProperty(ref _keyFileText, value); }
}
public GroupVm RootGroup { get; set; }
public Application.Group.Models.GroupVm RootGroup { get; set; }
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray());
@@ -129,7 +129,7 @@ namespace ModernKeePass.ViewModels
try
{
_isOpening = true;
OnPropertyChanged("IsValid");;
OnPropertyChanged("IsValid");
var fileInfo = new FileInfo
{
Name = databaseFile.DisplayName,
@@ -137,7 +137,7 @@ namespace ModernKeePass.ViewModels
};
var database = await _mediator.Send(new OpenDatabaseQuery { FileInfo = fileInfo, Credentials = CreateCredentials()});
await Task.Run(() => RootGroup = new GroupVm(database.RootGroup));
await Task.Run(() => RootGroup = database.RootGroup);
return true;
}
catch (ArgumentException)

View File

@@ -13,7 +13,7 @@ using ModernKeePassLib.Cryptography;
namespace ModernKeePass.ViewModels
{
public class EntryVm : INotifyPropertyChanged, IPwEntity, ISelectableModel
public class EntryVm : INotifyPropertyChanged, IVmEntity, ISelectableModel
{
public GroupVm ParentGroup { get; private set; }
public GroupVm PreviousGroup { get; private set; }
@@ -32,7 +32,7 @@ namespace ModernKeePass.ViewModels
public PwUuid IdUuid => _pwEntry?.Uuid;
public string Id => _pwEntry?.Uuid.ToHexString();
public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !ParentGroup.IsSelected;
public IEnumerable<IPwEntity> BreadCrumb => new List<IPwEntity>(ParentGroup.BreadCrumb) {ParentGroup};
public IEnumerable<IVmEntity> BreadCrumb => new List<IVmEntity>(ParentGroup.BreadCrumb) {ParentGroup};
/// <summary>
/// Determines if the Entry is current or from history
/// </summary>
@@ -160,7 +160,7 @@ namespace ModernKeePass.ViewModels
}
}
public IEnumerable<IPwEntity> History
public IEnumerable<IVmEntity> History
{
get
{

View File

@@ -5,14 +5,17 @@ using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using MediatR;
using ModernKeePass.Application.Database.Commands.SaveDatabase;
using ModernKeePass.Application.Database.Queries.GetDatabase;
using ModernKeePass.Common;
using ModernKeePass.Domain.Enums;
using ModernKeePass.Interfaces;
using ModernKeePass.Services;
using ModernKeePassLib;
namespace ModernKeePass.ViewModels
{
public class GroupVm : NotifyPropertyChangedBase, IPwEntity, ISelectableModel
public class GroupVm : NotifyPropertyChangedBase, IVmEntity, ISelectableModel
{
public GroupVm ParentGroup { get; private set; }
public GroupVm PreviousGroup { get; private set; }
@@ -40,23 +43,22 @@ namespace ModernKeePass.ViewModels
public ObservableCollection<GroupVm> Groups { get; set; } = new ObservableCollection<GroupVm>();
public PwUuid IdUuid => _pwGroup?.Uuid;
public string Id => IdUuid?.ToHexString();
public string Id => _group.Id;
public bool IsNotRoot => ParentGroup != null;
public bool ShowRestore => IsNotRoot && ParentGroup.IsSelected;
public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !IsSelected && !ParentGroup.IsSelected;
public bool IsRecycleOnDelete => IsRecycleBinEnabled().GetAwaiter().GetResult() && !IsSelected && !ParentGroup.IsSelected;
/// <summary>
/// Is the Group the database Recycle Bin?
/// </summary>
public bool IsSelected
{
get { return _database != null && _database.RecycleBinEnabled && _database.RecycleBin?.Id == Id; }
get { return IsRecycleBinEnabled().GetAwaiter().GetResult() && _database.RecycleBin?.Id == Id; }
set
{
if (value && _pwGroup != null) _database.RecycleBin = this;
if (value && _group != null) _database.RecycleBin = this;
}
}
@@ -67,18 +69,18 @@ namespace ModernKeePass.ViewModels
public string Name
{
get { return _pwGroup == null ? string.Empty : _pwGroup.Name; }
set { _pwGroup.Name = value; }
get { return _group == null ? string.Empty : _group.Title; }
set { _group.Title = value; }
}
public int IconId
{
get
{
if (_pwGroup?.IconId != null) return (int) _pwGroup?.IconId;
if (_group?.Icon != null) return (int) _group?.Icon;
return -1;
}
set { _pwGroup.IconId = (PwIcon)value; }
set { _group.Icon = (Icon)value; }
}
public bool IsEditMode
@@ -98,7 +100,7 @@ namespace ModernKeePass.ViewModels
set { SetProperty(ref _isMenuClosed, value); }
}
public IEnumerable<IPwEntity> BreadCrumb
public IEnumerable<IVmEntity> BreadCrumb
{
get
{
@@ -119,36 +121,35 @@ namespace ModernKeePass.ViewModels
public ICommand SortGroupsCommand { get; }
public ICommand UndoDeleteCommand { get; }
private readonly PwGroup _pwGroup;
private readonly IDatabaseService _database;
private readonly Application.Group.Models.GroupVm _group;
private readonly IMediator _mediator;
private bool _isEditMode;
private PwEntry _reorderedEntry;
private Application.Entry.Models.EntryVm _reorderedEntry;
private ObservableCollection<EntryVm> _entries = new ObservableCollection<EntryVm>();
private bool _isMenuClosed = true;
public GroupVm() {}
internal GroupVm(PwGroup pwGroup, GroupVm parent, PwUuid recycleBinId = null) : this(pwGroup, parent,
DatabaseService.Instance, recycleBinId)
internal GroupVm(Application.Group.Models.GroupVm group, GroupVm parent, string recycleBinId = null) : this(group, parent, App.Mediator, recycleBinId)
{ }
public GroupVm(PwGroup pwGroup, GroupVm parent, IDatabaseService database, PwUuid recycleBinId = null)
public GroupVm(Application.Group.Models.GroupVm group, GroupVm parent, IMediator mediator, string recycleBinId = null)
{
_pwGroup = pwGroup;
_database = database;
_group = group;
_mediator = mediator;
ParentGroup = parent;
SaveCommand = new RelayCommand(() => _database.Save());
SaveCommand = new RelayCommand(async () => await _mediator.Send(new SaveDatabaseCommand()));
SortEntriesCommand = new RelayCommand(async () =>
await SortEntriesAsync().ConfigureAwait(false), () => IsEditMode);
SortGroupsCommand = new RelayCommand(async () =>
await SortGroupsAsync().ConfigureAwait(false), () => IsEditMode);
UndoDeleteCommand = new RelayCommand(() => Move(PreviousGroup), () => PreviousGroup != null);
if (recycleBinId != null && _pwGroup.Uuid.Equals(recycleBinId)) _database.RecycleBin = this;
Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this)));
if (recycleBinId != null && _group.Id.Equals(recycleBinId)) _database.RecycleBin = this;
Entries = new ObservableCollection<EntryVm>(group.Entries.Select(e => new EntryVm(e, this)));
Entries.CollectionChanged += Entries_CollectionChanged;
Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this, recycleBinId)));
Groups = new ObservableCollection<GroupVm>(group.SubGroups.Select(g => new GroupVm(g, this, recycleBinId)));
}
private void Entries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
@@ -157,12 +158,12 @@ namespace ModernKeePass.ViewModels
{
case NotifyCollectionChangedAction.Remove:
var oldIndex = (uint) e.OldStartingIndex;
_reorderedEntry = _pwGroup.Entries.GetAt(oldIndex);
_pwGroup.Entries.RemoveAt(oldIndex);
_reorderedEntry = _group.Entries.GetAt(oldIndex);
_group.Entries.RemoveAt(oldIndex);
break;
case NotifyCollectionChangedAction.Add:
if (_reorderedEntry == null) _pwGroup.AddEntry(((EntryVm) e.NewItems[0]).GetPwEntry(), true);
else _pwGroup.Entries.Insert((uint)e.NewStartingIndex, _reorderedEntry);
if (_reorderedEntry == null) _group.AddEntry(((EntryVm) e.NewItems[0]).GetPwEntry(), true);
else _group.Entries.Insert((uint)e.NewStartingIndex, _reorderedEntry);
break;
}
}
@@ -170,7 +171,7 @@ namespace ModernKeePass.ViewModels
public GroupVm AddNewGroup(string name = "")
{
var pwGroup = new PwGroup(true, true, name, PwIcon.Folder);
_pwGroup.AddGroup(pwGroup, true);
_group.AddGroup(pwGroup, true);
var newGroup = new GroupVm(pwGroup, this) {Name = name, IsEditMode = string.IsNullOrEmpty(name)};
Groups.Add(newGroup);
return newGroup;
@@ -185,11 +186,12 @@ namespace ModernKeePass.ViewModels
return newEntry;
}
public void MarkForDelete(string recycleBinTitle)
public async Task MarkForDelete(string recycleBinTitle)
{
if (_database.RecycleBinEnabled && _database.RecycleBin?.IdUuid == null)
var isRecycleBinEnabled = await IsRecycleBinEnabled();
if (isRecycleBinEnabled && _database.RecycleBin?.IdUuid == null)
_database.CreateRecycleBin(recycleBinTitle);
Move(_database.RecycleBinEnabled && !IsSelected ? _database.RecycleBin : null);
Move(isRecycleBinEnabled && !IsSelected ? _database.RecycleBin : null);
((RelayCommand)UndoDeleteCommand).RaiseCanExecuteChanged();
}
@@ -202,7 +204,7 @@ namespace ModernKeePass.ViewModels
{
PreviousGroup = ParentGroup;
PreviousGroup.Groups.Remove(this);
PreviousGroup._pwGroup.Groups.Remove(_pwGroup);
PreviousGroup._group.SubGroups.Remove(_group);
if (destination == null)
{
_database.AddDeletedItem(IdUuid);
@@ -210,13 +212,13 @@ namespace ModernKeePass.ViewModels
}
ParentGroup = destination;
ParentGroup.Groups.Add(this);
ParentGroup._pwGroup.AddGroup(_pwGroup, true);
ParentGroup._group.AddGroup(_group, true);
}
public void CommitDelete()
public async Task CommitDelete()
{
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
if (_database.RecycleBinEnabled && !PreviousGroup.IsSelected) _database.RecycleBin._pwGroup.AddGroup(_pwGroup, true);
_group.ParentGroup.Groups.Remove(_group);
if (await IsRecycleBinEnabled() && !PreviousGroup.IsSelected) _database.RecycleBin._group.AddGroup(_group, true);
else _database.AddDeletedItem(IdUuid);
}
@@ -230,7 +232,7 @@ namespace ModernKeePass.ViewModels
var comparer = new PwEntryComparer(PwDefs.TitleField, true, false);
try
{
_pwGroup.Entries.Sort(comparer);
_group.Entries.Sort(comparer);
Entries = new ObservableCollection<EntryVm>(Entries.OrderBy(e => e.Name));
}
catch (Exception e)
@@ -243,8 +245,8 @@ namespace ModernKeePass.ViewModels
{
try
{
_pwGroup.SortSubGroups(false);
Groups = new ObservableCollection<GroupVm>(Groups.OrderBy(g => g.Name).ThenBy(g => g._pwGroup == null));
_group.SortSubGroups(false);
Groups = new ObservableCollection<GroupVm>(Groups.OrderBy(g => g.Name).ThenBy(g => g._group == null));
OnPropertyChanged("Groups");
}
catch (Exception e)
@@ -253,5 +255,10 @@ namespace ModernKeePass.ViewModels
}
}
private async Task<bool> IsRecycleBinEnabled()
{
var database = await _mediator.Send(new GetDatabaseQuery());
return database.IsRecycleBinEnabled;
}
}
}

View File

@@ -47,7 +47,7 @@ namespace ModernKeePass.Views
{
NavigationHelper.OnNavigatedTo(e);
var args = e.Parameter as PasswordEventArgs;
/*var args = e.Parameter as PasswordEventArgs;
if (args != null)
DataContext = args.RootGroup;
else
@@ -55,7 +55,9 @@ namespace ModernKeePass.Views
var vm = e.Parameter as GroupVm;
if (vm != null)
DataContext = vm;
}
}*/
var args = e.Parameter as Application.Group.Models.GroupVm;
if (args != null) DataContext = new GroupVm(args);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)

View File

@@ -13,17 +13,17 @@ namespace ModernKeePass.Views.UserControls
InitializeComponent();
}
public IEnumerable<IPwEntity> ItemsSource
public IEnumerable<IVmEntity> ItemsSource
{
get { return (IEnumerable<IPwEntity>)GetValue(ItemsSourceProperty); }
get { return (IEnumerable<IVmEntity>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(IEnumerable<IPwEntity>),
typeof(IEnumerable<IVmEntity>),
typeof(BreadCrumbUserControl),
new PropertyMetadata(new Stack<IPwEntity>(), (o, args) => { }));
new PropertyMetadata(new Stack<IVmEntity>(), (o, args) => { }));
}
}

View File

@@ -75,18 +75,18 @@ namespace ModernKeePass.Views.UserControls
typeof(HamburgerMenuUserControl),
new PropertyMetadata(Visibility.Collapsed, (o, args) => { }));
public IEnumerable<IPwEntity> ItemsSource
public IEnumerable<IVmEntity> ItemsSource
{
get { return (IEnumerable<IPwEntity>)GetValue(ItemsSourceProperty); }
get { return (IEnumerable<IVmEntity>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(IEnumerable<IPwEntity>),
typeof(IEnumerable<IVmEntity>),
typeof(HamburgerMenuUserControl),
new PropertyMetadata(new List<IPwEntity>(), (o, args) => { }));
new PropertyMetadata(new List<IVmEntity>(), (o, args) => { }));
public object SelectedItem
{

View File

@@ -192,7 +192,7 @@
<Compile Include="Converters\TextToWidthConverter.cs" />
<Compile Include="Events\PasswordEventArgs.cs" />
<Compile Include="Interfaces\IIsEnabled.cs" />
<Compile Include="Interfaces\IPwEntity.cs" />
<Compile Include="Interfaces\IVmEntity.cs" />
<Compile Include="Views\MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>