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\IsDatabaseOpen\IsDatabaseOpenQuery.cs" />
<Compile Include="Database\Queries\OpenDatabase\OpenDatabaseQuery.cs" /> <Compile Include="Database\Queries\OpenDatabase\OpenDatabaseQuery.cs" />
<Compile Include="Database\Queries\OpenDatabase\OpenDatabaseQueryValidator.cs" /> <Compile Include="Database\Queries\OpenDatabase\OpenDatabaseQueryValidator.cs" />
<Compile Include="Database\Queries\ReOpenDatabase\ReOpenDatabaseQuery.cs" />
<Compile Include="DependencyInjection.cs" /> <Compile Include="DependencyInjection.cs" />
<Compile Include="Entry\Models\EntryVm.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="Group\Models\GroupVm.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Content Include="Services\CryptographyService.cs" /> <Content Include="Services\CryptographyService.cs" />
@@ -79,14 +90,13 @@
<Content Include="Services\SettingsService.cs" /> <Content Include="Services\SettingsService.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Entry\Commands\UpdateEntry\" /> <Folder Include="Entry\Commands\SetFieldValue\" />
<Folder Include="Entry\Queries\" /> <Folder Include="Entry\Queries\GetEntry\" />
<Folder Include="Group\Commands\CreateEntry\" />
<Folder Include="Group\Commands\CreateGroup\" />
<Folder Include="Group\Commands\DeleteEntry\" />
<Folder Include="Group\Commands\DeleteGroup\" />
<Folder Include="Group\Commands\UpdateGroup\" /> <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\Commands\" />
<Folder Include="Resources\Queries\" /> <Folder Include="Resources\Queries\" />
<Folder Include="Settings\" /> <Folder Include="Settings\" />

View File

@@ -8,22 +8,34 @@ namespace ModernKeePass.Application.Common.Interfaces
{ {
bool IsOpen { get; } bool IsOpen { get; }
string Name { get; } string Name { get; }
GroupEntity RecycleBin { get; set; } GroupEntity RootGroup { get; }
BaseEntity Cipher { get; set; }
BaseEntity KeyDerivation { get; set; }
string Compression { get; set; }
Task<DatabaseEntity> Open(FileInfo fileInfo, Credentials credentials); string RecycleBinId { get; set; }
Task<DatabaseEntity> Create(FileInfo fileInfo, Credentials credentials); 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();
Task SaveDatabase(FileInfo FileInfo); Task SaveDatabase(FileInfo FileInfo);
Task UpdateCredentials(Credentials credentials); Task UpdateCredentials(Credentials credentials);
void CloseDatabase(); void CloseDatabase();
Task AddEntry(GroupEntity parentGroup, EntryEntity entity);
Task AddGroup(GroupEntity parentGroup, GroupEntity entity); Task AddEntry(string parentGroupId, string entryId);
Task UpdateEntry(EntryEntity entity); Task AddGroup(string parentGroupId, string groupId);
Task UpdateGroup(GroupEntity entity); Task UpdateEntry(string entryId);
Task DeleteEntry(EntryEntity entity); Task UpdateGroup(string groupId);
Task DeleteGroup(GroupEntity entity); 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() public void ApplyMappings()
{ {
new DatabaseVm().Mapping(this);
new EntryVm().Mapping(this); new EntryVm().Mapping(this);
new GroupVm().Mapping(this); new GroupVm().Mapping(this);
} }

View File

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

View File

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

View File

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

View File

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

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 AutoMapper;
using ModernKeePass.Application.Common.Mappings; using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Domain.Entities; using ModernKeePass.Domain.Entities;
@@ -10,15 +12,33 @@ namespace ModernKeePass.Application.Entry.Models
{ {
public string Id { get; set; } public string Id { get; set; }
public string Title { 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 Icon Icon { get; set; }
public Color ForegroundColor { get; set; } public Color ForegroundColor { get; set; }
public Color BackgroundColor { 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) public void Mapping(Profile profile)
{ {
profile.CreateMap<EntryEntity, EntryVm>() profile.CreateMap<EntryEntity, EntryVm>()
.ForMember(d => d.Id, opts => opts.MapFrom(s => s.Id)) .ForMember(d => d.Id, opts => opts.MapFrom(s => s.Id))
.ForMember(d => d.Title, opts => opts.MapFrom(s => s.Name)) .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.Icon, opts => opts.MapFrom(s => s.Icon))
.ForMember(d => d.ForegroundColor, opts => opts.MapFrom(s => s.ForegroundColor)) .ForMember(d => d.ForegroundColor, opts => opts.MapFrom(s => s.ForegroundColor))
.ForMember(d => d.BackgroundColor, opts => opts.MapFrom(s => s.BackgroundColor)); .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.Collections.Generic;
using System.Drawing; using System.Linq;
using AutoMapper; using AutoMapper;
using ModernKeePass.Application.Common.Mappings; using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Application.Entry.Models; using ModernKeePass.Application.Entry.Models;
@@ -13,8 +13,6 @@ namespace ModernKeePass.Application.Group.Models
public string Id { get; set; } public string Id { get; set; }
public string Title { get; set; } public string Title { get; set; }
public Icon Icon { 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<GroupVm> SubGroups { get; set; } = new List<GroupVm>();
public List<EntryVm> Entries { get; set; } = new List<EntryVm>(); 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.Id, opts => opts.MapFrom(s => s.Id))
.ForMember(d => d.Title, opts => opts.MapFrom(s => s.Name)) .ForMember(d => d.Title, opts => opts.MapFrom(s => s.Name))
.ForMember(d => d.Icon, opts => opts.MapFrom(s => s.Icon)) .ForMember(d => d.Icon, opts => opts.MapFrom(s => s.Icon))
.ForMember(d => d.ForegroundColor, opts => opts.MapFrom(s => s.ForegroundColor)) .ForMember(d => d.Entries, opts => opts.MapFrom(s => s.Entries.OrderBy(e => e.Name)))
.ForMember(d => d.BackgroundColor, opts => opts.MapFrom(s => s.BackgroundColor))
.ForMember(d => d.Entries, opts => opts.MapFrom(s => s.Entries))
.ForMember(d => d.SubGroups, opts => opts.MapFrom(s => s.SubGroups)); .ForMember(d => d.SubGroups, opts => opts.MapFrom(s => s.SubGroups));
} }
} }

View File

@@ -44,7 +44,6 @@
<Compile Include="Dtos\FileInfo.cs" /> <Compile Include="Dtos\FileInfo.cs" />
<Compile Include="Dtos\PasswordGenerationOptions.cs" /> <Compile Include="Dtos\PasswordGenerationOptions.cs" />
<Compile Include="Entities\BaseEntity.cs" /> <Compile Include="Entities\BaseEntity.cs" />
<Compile Include="Entities\DatabaseEntity.cs" />
<Compile Include="Entities\EntryEntity.cs" /> <Compile Include="Entities\EntryEntity.cs" />
<Compile Include="Entities\GroupEntity.cs" /> <Compile Include="Entities\GroupEntity.cs" />
<Compile Include="Enums\CredentialStatusTypes.cs" /> <Compile Include="Enums\CredentialStatusTypes.cs" />
@@ -54,8 +53,10 @@
<Compile Include="Exceptions\DatabaseOpenException.cs" /> <Compile Include="Exceptions\DatabaseOpenException.cs" />
<Compile Include="Exceptions\NavigationException.cs" /> <Compile Include="Exceptions\NavigationException.cs" />
<Compile Include="Exceptions\SaveException.cs" /> <Compile Include="Exceptions\SaveException.cs" />
<Compile Include="Interfaces\IDateTime.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Drawing;
namespace ModernKeePass.Domain.Entities namespace ModernKeePass.Domain.Entities
{ {
@@ -7,8 +6,6 @@ namespace ModernKeePass.Domain.Entities
{ {
public string Id { get; set; } public string Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; }
public DateTimeOffset LastModificationDate { 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using ModernKeePass.Domain.Enums; using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Domain.Entities namespace ModernKeePass.Domain.Entities
@@ -12,8 +13,10 @@ namespace ModernKeePass.Domain.Entities
public string Notes { get; set; } public string Notes { get; set; }
public DateTimeOffset ExpirationDate { get; set; } public DateTimeOffset ExpirationDate { get; set; }
public Dictionary<string, string> AdditionalFields { get; set; } = new Dictionary<string, string>(); 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 Icon Icon { get; set; }
public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; }
public bool HasExpirationDate { 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" /> <None Include="project.json" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Common\MachineDateTime.cs" />
<Compile Include="DependencyInjection.cs" /> <Compile Include="DependencyInjection.cs" />
<Compile Include="InfrastructureModule.cs" /> <Compile Include="InfrastructureModule.cs" />
<Compile Include="File\CsvImportFormat.cs" /> <Compile Include="File\CsvImportFormat.cs" />
<Compile Include="KeePass\EntryMappingProfile.cs" /> <Compile Include="KeePass\EntryMappingProfile.cs" />
<Compile Include="KeePass\GroupMappingProfile.cs" />
<Compile Include="KeePass\IconMapper.cs" /> <Compile Include="KeePass\IconMapper.cs" />
<Compile Include="KeePass\KeePassCryptographyClient.cs" /> <Compile Include="KeePass\KeePassCryptographyClient.cs" />
<Compile Include="KeePass\KeePassDatabaseClient.cs" /> <Compile Include="KeePass\KeePassDatabaseClient.cs" />
@@ -71,6 +73,7 @@
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <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. <!-- 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. 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.Entities;
using ModernKeePass.Domain.Exceptions; using ModernKeePass.Domain.Exceptions;
using ModernKeePassLib; using ModernKeePassLib;
using ModernKeePassLib.Cryptography.Cipher;
using ModernKeePassLib.Cryptography.KeyDerivation; using ModernKeePassLib.Cryptography.KeyDerivation;
using ModernKeePassLib.Interfaces; using ModernKeePassLib.Interfaces;
using ModernKeePassLib.Keys; using ModernKeePassLib.Keys;
@@ -23,68 +22,41 @@ namespace ModernKeePass.Infrastructure.KeePass
private readonly IMapper _mapper; private readonly IMapper _mapper;
private readonly PwDatabase _pwDatabase = new PwDatabase(); private readonly PwDatabase _pwDatabase = new PwDatabase();
private string _fileAccessToken; private string _fileAccessToken;
private CompositeKey _compositeKey; private Credentials _credentials;
// Main information // Main information
public bool IsOpen => (_pwDatabase?.IsOpen).GetValueOrDefault(); public bool IsOpen => (_pwDatabase?.IsOpen).GetValueOrDefault();
public string Name => _pwDatabase?.Name; public string Name => _pwDatabase?.Name;
public GroupEntity RootGroup { get; set; } public GroupEntity RootGroup { get; private set; }
// Settings // Settings
public GroupEntity RecycleBin public string RecycleBinId
{ {
get get
{ {
if (_pwDatabase.RecycleBinEnabled) if (_pwDatabase.RecycleBinEnabled)
{ {
var pwGroup = _pwDatabase.RootGroup.FindGroup(_pwDatabase.RecycleBinUuid, true); var pwGroup = _pwDatabase.RootGroup.FindGroup(_pwDatabase.RecycleBinUuid, true);
var group = new GroupEntity return pwGroup.Uuid.ToHexString();
{
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 null; return null;
} }
set set { _pwDatabase.RecycleBinUuid = BuildIdFromString(value); }
{
}
} }
public BaseEntity Cipher public string CipherId
{ {
get get { return _pwDatabase.DataCipherUuid.ToHexString(); }
{ set { _pwDatabase.DataCipherUuid = BuildIdFromString(value); }
var cipher = CipherPool.GlobalPool.GetCipher(_pwDatabase.DataCipherUuid);
return new BaseEntity
{
Id = cipher.CipherUuid.ToHexString(),
Name = cipher.DisplayName
};
}
set { _pwDatabase.DataCipherUuid = BuildIdFromString(value.Id); }
} }
public BaseEntity KeyDerivation public string KeyDerivationId
{ {
get get { return _pwDatabase.KdfParameters.KdfUuid.ToHexString(); }
{
var keyDerivation = KdfPool.Engines.First(e => e.Uuid.Equals(_pwDatabase.KdfParameters.KdfUuid));
return new BaseEntity
{
Id = keyDerivation.Uuid.ToHexString(),
Name = keyDerivation.Name
};
}
set set
{ {
_pwDatabase.KdfParameters = KdfPool.Engines _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); } set { _pwDatabase.Compression = (PwCompressionAlgorithm) Enum.Parse(typeof(PwCompressionAlgorithm), value); }
} }
public bool IsRecycleBinEnabled => _pwDatabase.RecycleBinEnabled;
public KeePassDatabaseClient(ISettingsProxy settings, IFileProxy fileService, IMapper mapper) public KeePassDatabaseClient(ISettingsProxy settings, IFileProxy fileService, IMapper mapper)
{ {
_settings = settings; _settings = settings;
@@ -101,22 +75,19 @@ namespace ModernKeePass.Infrastructure.KeePass
_mapper = mapper; _mapper = mapper;
} }
public async Task<DatabaseEntity> Open(FileInfo fileInfo, Credentials credentials) public async Task<GroupEntity> Open(FileInfo fileInfo, Credentials credentials)
{ {
try try
{ {
_compositeKey = await CreateCompositeKey(credentials); var compositeKey = await CreateCompositeKey(credentials);
var ioConnection = await BuildConnectionInfo(fileInfo); var ioConnection = await BuildConnectionInfo(fileInfo);
_pwDatabase.Open(ioConnection, _compositeKey, new NullStatusLogger()); _pwDatabase.Open(ioConnection, compositeKey, new NullStatusLogger());
_credentials = credentials;
_fileAccessToken = fileInfo.Path; _fileAccessToken = fileInfo.Path;
return new DatabaseEntity return _mapper.Map<GroupEntity>(_pwDatabase.RootGroup);
{
Name = _pwDatabase.Name,
RootGroupEntity = BuildHierarchy(_pwDatabase.RootGroup)
};
} }
catch (InvalidCompositeKeyException ex) 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); var ioConnection = await BuildConnectionInfo(fileInfo);
_pwDatabase.New(ioConnection, _compositeKey); _pwDatabase.New(ioConnection, compositeKey);
var fileFormat = _settings.GetSetting<string>("DefaultFileFormat"); var fileFormat = _settings.GetSetting<string>("DefaultFileFormat");
switch (fileFormat) switch (fileFormat)
@@ -142,10 +118,7 @@ namespace ModernKeePass.Infrastructure.KeePass
_fileAccessToken = fileInfo.Path; _fileAccessToken = fileInfo.Path;
// TODO: create sample data depending on settings // TODO: create sample data depending on settings
return new DatabaseEntity return _mapper.Map<GroupEntity>(_pwDatabase.RootGroup);
{
RootGroupEntity = BuildHierarchy(_pwDatabase.RootGroup)
};
} }
public async Task SaveDatabase() public async Task SaveDatabase()
@@ -185,49 +158,78 @@ namespace ModernKeePass.Infrastructure.KeePass
_fileService.ReleaseFile(_fileAccessToken); _fileService.ReleaseFile(_fileAccessToken);
} }
public async Task AddEntry(GroupEntity parentGroupEntity, EntryEntity entry) public async Task AddEntry(string parentGroupId, string entryId)
{ {
await Task.Run(() => await Task.Run(() =>
{ {
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupEntity.Id), true); var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
var pwEntry = new PwEntry(true, true);
_mapper.Map(entry, pwEntry);
parentPwGroup.AddEntry(pwEntry, 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(() => await Task.Run(() =>
{ {
var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupEntity.Id), true); var parentPwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(parentGroupId), true);
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true);
var pwGroup = new PwGroup(true, true) parentPwGroup.Groups.Add(pwGroup);
{ });
Name = group.Name }
}; public async Task RemoveEntry(string parentGroupId, string entryId)
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(); throw new NotImplementedException();
} }
public Task UpdateGroup(GroupEntity group) public Task UpdateGroup(string group)
{ {
throw new NotImplementedException(); 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(() => await Task.Run(() =>
{ {
var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entry.Id), true); var pwEntry = _pwDatabase.RootGroup.FindEntry(BuildIdFromString(entryId), true);
var id = pwEntry.Uuid; var id = pwEntry.Uuid;
pwEntry.ParentGroup.Entries.Remove(pwEntry); 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(() => await Task.Run(() =>
{ {
var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(group.Id), true); var pwGroup = _pwDatabase.RootGroup.FindGroup(BuildIdFromString(groupId), true);
var id = pwGroup.Uuid; var id = pwGroup.Uuid;
pwGroup.ParentGroup.Groups.Remove(pwGroup); 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) public async Task UpdateCredentials(Credentials credentials)
{ {
_pwDatabase.MasterKey = await CreateCompositeKey(credentials); _pwDatabase.MasterKey = await CreateCompositeKey(credentials);
@@ -275,20 +291,6 @@ namespace ModernKeePass.Infrastructure.KeePass
return IOConnectionInfo.FromByteArray(fileContents); 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) private PwUuid BuildIdFromString(string id)
{ {
return new PwUuid(MemUtil.HexStringToByteArray(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 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio Version 16
VisualStudioVersion = 14.0.25420.1 VisualStudioVersion = 16.0.29911.84
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Win81App", "ModernKeePass\Win81App.csproj", "{A0CFC681-769B-405A-8482-0CDEE595A91F}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Win81App", "ModernKeePass\Win81App.csproj", "{A0CFC681-769B-405A-8482-0CDEE595A91F}"
EndProject EndProject
@@ -26,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Domain", "ModernKeePass.Dom
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "ModernKeePass.Infrastructure\Infrastructure.csproj", "{09577E4C-4899-45B9-BF80-1803D617CCAE}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "ModernKeePass.Infrastructure\Infrastructure.csproj", "{09577E4C-4899-45B9-BF80-1803D617CCAE}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ModernKeePass.KeePassDatabaseTests", "ModernKeePass.KeePassDatabaseTests\ModernKeePass.KeePassDatabaseTests.csproj", "{52FEA1EE-2FA7-4862-85FE-CB05893D439E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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|x64.Build.0 = Release|Any CPU
{09577E4C-4899-45B9-BF80-1803D617CCAE}.Release|x86.ActiveCfg = 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 {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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -145,6 +163,7 @@ Global
{42353562-5E43-459C-8E3E-2F21E575261D} = {0B30588B-07B8-4A88-A268-F58D06EA1627} {42353562-5E43-459C-8E3E-2F21E575261D} = {0B30588B-07B8-4A88-A268-F58D06EA1627}
{9A0759F1-9069-4841-99E3-3BEC44E17356} = {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} {09577E4C-4899-45B9-BF80-1803D617CCAE} = {0B30588B-07B8-4A88-A268-F58D06EA1627}
{52FEA1EE-2FA7-4862-85FE-CB05893D439E} = {107C7C00-56F4-41B0-A8CC-0156C46A3650}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0ADC1BC6-B1CA-427D-A97C-3CA40AAB0428} SolutionGuid = {0ADC1BC6-B1CA-427D-A97C-3CA40AAB0428}

View File

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

View File

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

View File

@@ -9,7 +9,7 @@ namespace ModernKeePass.Common
{ {
public static class ToastNotificationHelper 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 notificationXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
var toastElements = notificationXml.GetElementsByTagName("text"); var toastElements = notificationXml.GetElementsByTagName("text");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,17 +13,17 @@ namespace ModernKeePass.Views.UserControls
InitializeComponent(); 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); } set { SetValue(ItemsSourceProperty, value); }
} }
public static readonly DependencyProperty ItemsSourceProperty = public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register( DependencyProperty.Register(
"ItemsSource", "ItemsSource",
typeof(IEnumerable<IPwEntity>), typeof(IEnumerable<IVmEntity>),
typeof(BreadCrumbUserControl), 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), typeof(HamburgerMenuUserControl),
new PropertyMetadata(Visibility.Collapsed, (o, args) => { })); 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); } set { SetValue(ItemsSourceProperty, value); }
} }
public static readonly DependencyProperty ItemsSourceProperty = public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register( DependencyProperty.Register(
"ItemsSource", "ItemsSource",
typeof(IEnumerable<IPwEntity>), typeof(IEnumerable<IVmEntity>),
typeof(HamburgerMenuUserControl), typeof(HamburgerMenuUserControl),
new PropertyMetadata(new List<IPwEntity>(), (o, args) => { })); new PropertyMetadata(new List<IVmEntity>(), (o, args) => { }));
public object SelectedItem public object SelectedItem
{ {

View File

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