124 Commits

Author SHA1 Message Date
Geoffroy BONNEVILLE
2ebd952982 Minor refactoring 2021-06-15 15:33:15 +02:00
Geoffroy BONNEVILLE
5387f1c5a1 Fix navigation issue
Applied some syntax style
2021-06-15 10:16:58 +02:00
Geoffroy BONNEVILLE
77b5927d46 Refactor NavigationHelper 2021-06-14 19:38:48 +02:00
Geoffroy BONNEVILLE
e917bd249f Unregister the messenger everywhere on unload/navigate from 2021-05-10 20:28:13 +02:00
Geoffroy BONNEVILLE
dec59b2378 Update certificates 2021-04-30 17:18:50 +02:00
Geoffroy BONNEVILLE
4153dc7344 Update store association
Update CommonServiceLocator
2021-04-30 16:37:54 +02:00
Geoffroy BONNEVILLE
a589e1c5b7 Fix multiple messenger instance registrations in entry view models 2021-04-27 15:48:45 +02:00
Geoffroy BONNEVILLE
fc8dde32cd Remove sonarqube files from source control 2020-06-26 15:37:10 +02:00
Geoffroy BONNEVILLE
80a255aa6f Restored stackpanel in breadcrumb 2020-06-26 15:21:06 +02:00
Geoffroy BONNEVILLE
1f06bf3ba7 Root group up button disabled
Going back to home when opening a file from explorer no longer displays open file
Inverted Breadcrumb order to avoid reordering items
Update release notes
Code cleanup
2020-06-26 01:16:44 +02:00
Geoffroy BONNEVILLE
7dcd5a4a57 Removed Breadcrumb service
Breadcrumb control handles breadcrumb status
Layout improvements
Added the ability to delete an entry from the group menu
2020-06-10 13:38:04 +02:00
Geoffroy BONNEVILLE
c62ed584dc New Breadcrumb user control
New Breadcrumb service
WIP icons and back button behavior
2020-06-09 20:18:17 +02:00
d6b17fe696 WIP Return of the Breadcrumb
Entry Title field added as part of the entry page
Code cleanup
2020-06-08 19:17:11 +02:00
f477828628 Changed some observable collections types for HamburgerMenu binding (issue with WinRT 8.1)
Restored CollectionViewSource for recent (issue with WinRT 8.1)
Forbid horizontal scrolling in Main Menu
Fixed an incorrect SetupFocusAction target object binding
2020-06-08 14:16:54 +02:00
Geoffroy BONNEVILLE
4a0bc1cb86 Add ARM debug target 2020-06-08 11:52:12 +02:00
Geoffroy BONNEVILLE
4e7aca5517 CSV Import command created 2020-06-05 19:08:29 +02:00
Geoffroy BONNEVILLE
1f04f941c2 Corrected issue in color picker user control when changing history
Use of commands instead of events in code-behind
Some refactoring
2020-06-04 16:29:26 +02:00
Geoffroy BONNEVILLE
5c1dfa1b0e Update version to 1.20
Clicking new group button while collapsed will expand the menu
Use of space freed by hidden hamburger menu
2020-06-04 15:31:38 +02:00
Geoffroy BONNEVILLE
1c6fb0f2bb Removed useless collectionviewsource
Updated some packages
2020-06-02 15:57:14 +02:00
Geoffroy BONNEVILLE
e5b35dc6ab SemanticView zoomed out layout and design improvements 2020-06-02 13:16:36 +02:00
Geoffroy BONNEVILLE
ce48850566 Fix Sonar issues 2020-06-01 23:03:57 +02:00
Geoffroy BONNEVILLE
5d8d996f44 Working ColorPickerUserControl 2020-06-01 10:32:06 +02:00
Geoffroy BONNEVILLE
4000d51f70 Update PasswordLength property on password generation 2020-05-26 19:06:59 +02:00
Geoffroy BONNEVILLE
0c70b5146f Create entry history only if DB is open
Fix issues in entry field names
Entry field names cleanup and refactoring
2020-05-26 13:38:07 +02:00
Geoffroy BONNEVILLE
3ecee4a821 Fix ClipboardAction so that it only clears Clipboard when Window is active 2020-05-26 12:30:52 +02:00
Geoffroy BONNEVILLE
3d436c56fa Password generation button with display toggle and indicator is now a user control
SetCredentials user controls now uses PasswordGenerationBox user control
Some layout improvements in EntryDetailsPage
WIP Clipboard suspend issues
2020-05-25 19:23:32 +02:00
Geoffroy BONNEVILLE
0e05e3fbca Fix Sonar issues 2020-05-20 19:03:31 +02:00
Geoffroy BONNEVILLE
45b5ae5630 ColorPickerControl finally doesn't set database to dirty when there is an initial value 2020-05-20 17:40:06 +02:00
Geoffroy BONNEVILLE
643fb9a3f2 Working protected fields (warning: check performance) 2020-05-20 11:59:40 +02:00
Geoffroy BONNEVILLE
b7f8853ef2 WIP Protect/Unprotect Additional Field on selection 2020-05-18 22:20:31 +02:00
Geoffroy BONNEVILLE
9126307b4c Cryptography service now handles random byte generation
Protected strings are now protected in memory
2020-05-18 14:14:28 +02:00
Geoffroy BONNEVILLE
ceaf7dabd3 Fix Sonar issues 2020-05-14 17:06:39 +02:00
Geoffroy BONNEVILLE
7a2ce30512 Design improvements 2020-05-14 16:09:06 +02:00
Geoffroy BONNEVILLE
d497f69a5e Updated deleted text information
Improved dirty status detection (restored removed variable...)
This corrected history creation on navigation when entry deleted
2020-05-14 13:32:44 +02:00
Geoffroy BONNEVILLE
72e5bf4ee1 Added a cryptography service to encrypt protected information (unused atm)
Corrected a bug when deleting recycle bin
2020-05-14 12:05:05 +02:00
Geoffroy BONNEVILLE
2e01fa2212 Changed tooltip styles
Removed useless isdirty field in entry
When an app can't be saved on suspend, don't reopen it to avoid possible de-sync
2020-05-13 18:59:26 +02:00
Geoffroy BONNEVILLE
0adb44bc81 Some design changes
Again fixed open url bug
2020-05-13 15:14:58 +02:00
Geoffroy BONNEVILLE
d38d6461bd Updated Settings page
Added new settings (history and clipboard)
Renamed and moved Settings Vms
2020-05-13 13:50:33 +02:00
Geoffroy BONNEVILLE
7ac1595aaa Clipboard action now sets an expiration timer
WIP Max History count (back-end done, front-end todo)
2020-05-12 18:43:37 +02:00
Geoffroy BONNEVILLE
f8f7c19f65 Display a big database size warning
Auto rename additional field when it matches standard
Treated all fields as new Field class
Added the Is Protected property
2020-05-12 17:14:30 +02:00
Geoffroy BONNEVILLE
d6dc6a74a3 Groups can now also be manually reordered
Design improvements
2020-05-11 19:22:41 +02:00
Geoffroy BONNEVILLE
bb2b99ed66 Additional fields Add, Update and Delete work (too well)
SelectableListView works when programmatically setting selection
2020-05-11 10:53:14 +02:00
Geoffroy BONNEVILLE
71b6009a29 Remove some useless code (again)
Improve some visuals
2020-05-07 19:10:25 +02:00
Geoffroy BONNEVILLE
fbcc354809 Additional fields rendering done
Removed lots of unused classes
2020-05-07 16:01:59 +02:00
Geoffroy BONNEVILLE
e901afaf29 Attachment Add and Delete commands implemented 2020-05-07 12:11:12 +02:00
Geoffroy BONNEVILLE
ca04a6c8ee Entry page is now a Hub
EntryDetailVM and GroupDetailVM are now singleton
Read-only Additional fields and Attachments
2020-05-06 18:54:39 +02:00
Geoffroy BONNEVILLE
1488c3244f Better exception handling 2020-05-05 19:26:38 +02:00
Geoffroy BONNEVILLE
2e22a2bd92 Added some translations in file pickers
Corrected key file creation picker issue (any not allowed)
Fixed some Sonar issues
2020-05-05 17:32:07 +02:00
Geoffroy BONNEVILLE
8fb468358e Hamburger button state is now correct (no more double clicks) but it's a bit hacky
Changed Help tooltip location in New Database Settings page
Suggest Save As when opening DB when another is opened and there is a save error
2020-05-05 16:59:49 +02:00
Geoffroy BONNEVILLE
5ce0262318 TextBoxWithButton control correctly updates field value
Create Group now allows inline input of the group name
2020-05-05 15:27:34 +02:00
Geoffroy BONNEVILLE
2f30389f6c Big redesign (closer to Win10 UWP)
Replaced breadcrumb with Up button
Search button now integrated in top menu
Hamburger menu make better use of visual states
Better visual states changes when size changes
2020-05-04 20:56:19 +02:00
Geoffroy BONNEVILLE
b3c7683c12 Send a message on save to update commands can execute 2020-05-04 14:29:52 +02:00
Geoffroy BONNEVILLE
1e7662def7 Save error is now handled via Messenger instead of unhandled exception handler (which didn't work)
Save as actually works now
WIP Styles
Code cleanup
2020-05-04 12:48:27 +02:00
Geoffroy BONNEVILLE
97b10baedc Resuming correctly re-opens the previsouly opened database 2020-05-02 14:39:42 +02:00
Geoffroy BONNEVILLE
654bd6b4e5 Corrected some Sonar issues
Changed a little bit the Open and Set credentials User controls
2020-05-02 14:21:59 +02:00
Geoffroy BONNEVILLE
7b88461333 *BoxWithButton data binding actually work 2020-04-30 22:04:08 +02:00
Geoffroy BONNEVILLE
8de493f987 Username is now correctly persisted
Set credentials validation works as intended
Getting settings has default values
Add parent group in Move searchbox
Moving entries work as intended
Removed unreferenced code files
2020-04-30 19:40:48 +02:00
Geoffroy BONNEVILLE
e5353478f4 Corrected issue where entry information was not showing anymore 2020-04-30 16:39:39 +02:00
Geoffroy BONNEVILLE
1b981b00d5 Update credentials better looking 2020-04-30 15:32:42 +02:00
Geoffroy BONNEVILLE
d2814c6c67 OpenDatabase control now handles status changes with VisualStateManager instead of Vm
Opening DB shows a progress ring instead
2020-04-30 11:10:10 +02:00
Geoffroy BONNEVILLE
9fdc727787 Minor code cleanup 2020-04-29 19:20:10 +02:00
Geoffroy BONNEVILLE
14cd3ab57a WIP Windows 10
Dependencies finally installed
Removal of useless code
Big cleanup in XAML styles (override colors the correct way)
2020-04-29 16:39:20 +02:00
Geoffroy BONNEVILLE
d6529646a8 Split ViewModelLocator 2020-04-29 09:44:32 +02:00
Geoffroy BONNEVILLE
7917a8b388 Updated release notes
Moved ViewModelLocator
WIP ModernKeePass10
2020-04-28 20:14:18 +02:00
Geoffroy BONNEVILLE
b8e1bbd9d7 Move finally works
Sort entries and groups refresh page info
Stopped using breadcrumb user control - for now
Some refactoring
2020-04-28 18:54:37 +02:00
Geoffroy BONNEVILLE
f158e5aced Finally a nicer looking and working TextBoxWithButton (inspired from the SearchButton)
SearchBox field style improved
2020-04-28 15:20:47 +02:00
Geoffroy BONNEVILLE
8e06bf4bb0 Removed half-baked import feature for now
No views depend on services anymore
Dirty status fully handled by behavior
2020-04-27 11:14:29 +02:00
Geoffroy BONNEVILLE
59ab43ca9c Remove entry from mru when it does not exist anymore 2020-04-25 22:34:50 +02:00
Geoffroy BONNEVILLE
7778e45deb All VMs use viewmodellocator 2020-04-25 22:03:47 +02:00
Geoffroy BONNEVILLE
df973c5f62 StorageFile client more intelligent
Save is working again
2020-04-24 16:16:48 +02:00
Geoffroy BONNEVILLE
3967db41b3 Open from Explorer works again
Updated ModernKeePassLib
2020-04-24 15:52:46 +02:00
Geoffroy BONNEVILLE
eacb3b182e Database files are added to the mru instead of the fal
Simplify opening database workflow
Corrected opening from recent bug
2020-04-24 13:58:30 +02:00
Geoffroy BONNEVILLE
d211453553 WIP ViewModelLocator - Messenger and Recent issues
Refactoring
Code cleanup
2020-04-23 19:00:38 +02:00
Geoffroy BONNEVILLE
2b8f9bd5f0 WIP ViewModelLocator - Messenger and Recent issues
Refactoring
Code cleanup
2020-04-23 18:59:56 +02:00
Geoffroy BONNEVILLE
722790e5e4 Removed useless package 2020-04-22 19:22:36 +02:00
Geoffroy BONNEVILLE
a9ed588c9a Creation of a notification service 2020-04-22 18:12:28 +02:00
Geoffroy BONNEVILLE
61f5e9df0b Update master key works
Key file creation works
Code cleanup
2020-04-22 17:06:16 +02:00
Geoffroy BONNEVILLE
a7da427ded Create database works with new Vm
Refactoring
2020-04-22 16:21:47 +02:00
Geoffroy BONNEVILLE
a88051bc0c Restore Main and Settings Page
Entry and Group delete events converted to commands
Code cleanup
2020-04-22 11:58:06 +02:00
Geoffroy BONNEVILLE
1df9cbce1c WIP DeleteCommand
WIP DataContexts binding (Main and Settings are broken)
2020-04-21 19:12:26 +02:00
Geoffroy BONNEVILLE
0b19d8d50a Opening Databases now use a Messenger service 2020-04-21 17:13:39 +02:00
Geoffroy BONNEVILLE
004f1a35a8 Detect if current group is the recycle bin for entry and group creation 2020-04-21 13:39:53 +02:00
Geoffroy BONNEVILLE
ac66faa9e2 GroupDetailPage uses navigation service 2020-04-21 13:33:15 +02:00
Geoffroy BONNEVILLE
c81f8bc835 SavePage uses nav service 2020-04-21 13:14:56 +02:00
Geoffroy BONNEVILLE
a1085b6010 Add MVVM Light library
Updated nuget packages
2020-04-21 13:07:17 +02:00
Geoffroy BONNEVILLE
75f6e2f840 OpenDatabaseUserControl works 2020-04-21 11:26:02 +02:00
Geoffroy BONNEVILLE
310bd777b2 WIP Split composite key user control
Some refactoring
2020-04-20 20:02:43 +02:00
Geoffroy BONNEVILLE
73670e8689 Create a shared project with all Win App common files (8.1 and 10)
Finally use the dependency injected Resource Service
2020-04-17 16:56:07 +02:00
Geoffroy BONNEVILLE
2fb5b14085 Updating group raise SaveCommand can execute
No need to click twice on history menu
Skipped first history entry as it is the same as the current entry
Stored SaveException innerexception as it is read more than once
2020-04-16 19:43:17 +02:00
Geoffroy BONNEVILLE
f950564000 Update groups finally implemented
Code cleanup
2020-04-16 17:16:03 +02:00
Geoffroy BONNEVILLE
9befdc321a Entry history delete and restore work 2020-04-16 14:08:50 +02:00
Geoffroy BONNEVILLE
98ac418f62 WIP History
Restore from history works
2020-04-15 19:06:13 +02:00
Geoffroy BONNEVILLE
0063ef1977 Create entries and groups put them in Edit mode (as before)
Search now uses KeePassLib search
Code cleanup
2020-04-15 12:02:30 +02:00
Geoffroy BONNEVILLE
b66e79f97c History *works*
WIP on save on entry page doesn't show last change
2020-04-14 19:59:19 +02:00
Geoffroy BONNEVILLE
9603c1ff01 Icons work again
Colors work again
2020-04-14 17:49:29 +02:00
Geoffroy BONNEVILLE
3def21bc7d Restored title group and entries icons
Editing adds a visible border
2020-04-14 14:09:31 +02:00
Geoffroy BONNEVILLE
a2eba91a3b Added dirty behavior
Removed restore action (-> Move action wip)
Added additional check on DB size before auto saving
Code cleanup
2020-04-14 13:44:07 +02:00
Geoffroy BONNEVILLE
d972b6cb5a Added the option to close DB without saving
Changed the way recent files are retrieved
Stopped showing the DB Closed exception on suspend
Reordering entries works
Moved code from infra to application
Cleanup
2020-04-09 19:43:06 +02:00
Geoffroy BONNEVILLE
14fd4634db Entry icons now correctly show up
Auto create new recycle bin works correctly
2020-04-08 20:02:13 +02:00
Geoffroy BONNEVILLE
752e96884d Code cleanup (ter) 2020-04-08 16:38:38 +02:00
Geoffroy BONNEVILLE
1046110dd2 Code cleanup (bis) 2020-04-08 16:23:15 +02:00
Geoffroy BONNEVILLE
d1047a92ba Update build configurations 2020-04-08 15:52:25 +02:00
Geoffroy BONNEVILLE
009382ea03 Cleanup code 2020-04-08 15:27:40 +02:00
Geoffroy BONNEVILLE
4863eb9fae Create DB works correctly
Sample data moved to application
Tests updated (still not working - splat)
WIP
2020-04-07 17:29:03 +02:00
Geoffroy BONNEVILLE
1fa799bdf8 FileInfo overhaul
Opening DB works again
2020-04-07 12:48:18 +02:00
Geoffroy BONNEVILLE
56d93a5187 Moved application code to the Application layer
Imported Win10 project
Code cleanup
WIP - Use common UWP services for Win8.1 and Win10
2020-04-06 20:20:45 +02:00
Geoffroy BONNEVILLE
e795a8c3c4 Database settings now work properly 2020-04-03 18:14:44 +02:00
Geoffroy BONNEVILLE
b875f3c89d Adding entries and groups works again
Entry history almost fully functional
Some refactoring
2020-04-03 17:33:53 +02:00
Geoffroy BONNEVILLE
36aa8914fa Handle entities with id
No hierarchy is built anymore
WIP save issues after delete
2020-04-02 19:12:16 +02:00
Geoffroy BONNEVILLE
b61a9652d1 WIP change to ids 2020-04-01 19:37:30 +02:00
Geoffroy BONNEVILLE
57be6bb917 Build hierarchy instead of using Automapper
Add entities before removing them
2020-04-01 12:48:36 +02:00
Geoffroy BONNEVILLE
90c592d7ee Lots of bug corrections
WIP - still lots left
2020-03-31 19:19:02 +02:00
Geoffroy BONNEVILLE
e4bd788ed3 1st working version in clean arch
WIP Parent group mapping issues
2020-03-30 19:43:04 +02:00
Geoffroy BONNEVILLE
d1ba73ee9d Don't use mediator for App services (recent, resource, settings)
WIP in View models
2020-03-28 16:13:17 +01:00
Geoffroy BONNEVILLE
45fcf7e8ab Removed ModernKeePassLib dependency
Code cleanup
WIP on service replacement and VM use
2020-03-27 18:45:13 +01:00
Geoffroy BONNEVILLE
e3638c2f5c More commands/queries
WIP on XAML EntryVm and GroupVm
2020-03-27 13:27:29 +01:00
Geoffroy BONNEVILLE
22072bb2fe Most services are implemented as command/queries
Code cleanup
2020-03-26 19:04:51 +01:00
Geoffroy BONNEVILLE
903e7649e4 More queries/commands 2020-03-26 15:38:29 +01:00
Geoffroy BONNEVILLE
a17d6b05ae Added lots of commands
Simplified KeePass client
2020-03-26 12:25:22 +01:00
Geoffroy BONNEVILLE
4b1210f414 WIP 2020-03-24 19:14:34 +01:00
Geoffroy BONNEVILLE
f208e2d0b6 Correct package version installed
Dependency injection works
Project renaming
WIP replacement of services with CQRS
2020-03-24 17:31:34 +01:00
Geoffroy BONNEVILLE
ba8bbe045b Cleanup 2020-03-24 13:09:00 +01:00
Geoffroy BONNEVILLE
7e44d47065 WIP Clean Architecture
Windows 8.1 App Uses keepasslib v2.44 (temporarily)
2020-03-24 13:01:14 +01:00
Geoffroy BONNEVILLE
34cd4ca3d8 Allow any file type for key selection 2020-03-20 17:05:46 +01:00
646 changed files with 20255 additions and 75133 deletions

5
.gitignore vendored
View File

@@ -38,4 +38,7 @@ packages/
project.lock.json
AppPackages/
BundleArtifacts/
*.DotSettings
*.DotSettings
/ModernKeePass/Win81App_TemporaryKey.pfx
/ModernKeePass/Win81App_StoreKey.pfx
.sonarqube/

View File

@@ -0,0 +1,176 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{42353562-5E43-459C-8E3E-2F21E575261D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ModernKeePass.Application</RootNamespace>
<AssemblyName>ModernKeePass.Application</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>ARM</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|ARM' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Common\Behaviors\DirtyStatusBehavior.cs" />
<Compile Include="Common\Interfaces\ICryptographyClient.cs" />
<Compile Include="Common\Interfaces\IDatabaseSettingsProxy.cs" />
<Compile Include="Common\Interfaces\IDatabaseProxy.cs" />
<Compile Include="Common\Interfaces\IEntityVm.cs" />
<Compile Include="Common\Interfaces\IFileProxy.cs" />
<Compile Include="Common\Interfaces\ICredentialsProxy.cs" />
<Compile Include="Common\Interfaces\ILogger.cs" />
<Compile Include="Common\Interfaces\INotificationService.cs" />
<Compile Include="Common\Interfaces\IRecentProxy.cs" />
<Compile Include="Common\Interfaces\IResourceProxy.cs" />
<Compile Include="Common\Interfaces\ISettingsProxy.cs" />
<Compile Include="Common\Mappings\IMapFrom.cs" />
<Compile Include="Common\Mappings\MappingProfile.cs" />
<Compile Include="Common\Models\BreadcrumbItem.cs" />
<Compile Include="Entry\Commands\AddAttachment\AddAttachmentCommand.cs" />
<Compile Include="Entry\Commands\AddHistory\AddHistoryCommand.cs" />
<Compile Include="Entry\Commands\DeleteAttachment\DeleteAttachmentCommand.cs" />
<Compile Include="Entry\Commands\DeleteField\DeleteFieldCommand.cs" />
<Compile Include="Entry\Commands\DeleteHistory\DeleteHistoryCommand.cs" />
<Compile Include="Entry\Commands\RestoreHistory\RestoreHistoryCommand.cs" />
<Compile Include="Entry\Models\FieldVm.cs" />
<Compile Include="Entry\Queries\GetEntry\GetEntryQuery.cs" />
<Compile Include="Group\Commands\DeleteEntry\DeleteEntryCommand.cs" />
<Compile Include="Group\Commands\DeleteGroup\DeleteGroupCommand.cs" />
<Compile Include="Group\Commands\MoveGroup\MoveGroupCommand.cs" />
<Compile Include="Group\Commands\UpdateGroup\UpdateGroupCommand.cs" />
<Compile Include="Group\Queries\GetAllGroups\GetAllGroupsQuery.cs" />
<Compile Include="Group\Queries\GetGroup\GetGroupQuery.cs" />
<Compile Include="Group\Queries\SearchEntries\SearchEntriesQuery.cs" />
<Compile Include="Import\Commands\ImportFromCsv\ImportFromCsvCommand.cs" />
<Compile Include="Import\Commands\ImportFromCsv\ImportFromCsvCommandValidator.cs" />
<Compile Include="Parameters\Commands\SetCipher\SetCipherCommand.cs" />
<Compile Include="Parameters\Commands\SetCompression\SetCompressionCommand.cs" />
<Compile Include="Parameters\Commands\SetHasRecycleBin\SetHasRecycleBinCommand.cs" />
<Compile Include="Parameters\Commands\SetMaxHistoryCount\SetHistoryCountCommand.cs" />
<Compile Include="Parameters\Commands\SetKeyDerivation\SetKeyDerivationCommand.cs" />
<Compile Include="Parameters\Commands\SetMaxHistorySize\SetMaxHistorySizeCommand.cs" />
<Compile Include="Parameters\Commands\SetRecycleBin\SetRecycleBinCommand.cs" />
<Compile Include="Parameters\Models\CipherVm.cs" />
<Compile Include="Parameters\Models\KeyDerivationVm.cs" />
<Compile Include="Parameters\Queries\GetCiphers\GetCiphersQuery.cs" />
<Compile Include="Parameters\Queries\GetCompressions\GetCompressionsQuery.cs" />
<Compile Include="Parameters\Queries\GetKeyDerivations\GetKeyDerivationsQuery.cs" />
<Compile Include="Database\Commands\CloseDatabase\CloseDatabaseCommand.cs" />
<Compile Include="Database\Commands\CreateDatabase\CreateDatabaseCommand.cs" />
<Compile Include="Database\Commands\CreateDatabase\CreateDatabaseCommandValidator.cs" />
<Compile Include="Database\Commands\SaveDatabase\SaveDatabaseCommand.cs" />
<Compile Include="Database\Commands\UpdateCredentials\UpdateCredentialsCommand.cs" />
<Compile Include="Database\Models\DatabaseVm.cs" />
<Compile Include="Database\Queries\GetDatabase\GetDatabaseQuery.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\Commands\UpsertField\UpsertFieldCommand.cs" />
<Compile Include="Entry\Commands\UpsertField\UpsertFieldCommandValidator.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\MoveEntry\MoveEntryCommand.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" />
<Compile Include="Security\Commands\GenerateKeyFile\GenerateKeyFileCommand.cs" />
<Compile Include="Security\Commands\GeneratePassword\GeneratePasswordCommand.cs" />
<Compile Include="Security\Queries\EstimatePasswordComplexity\EstimatePasswordComplexityQuery.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<ProjectReference Include="..\ModernKeePass.Domain\Domain.csproj">
<Project>{9a0759f1-9069-4841-99e3-3bec44e17356}</Project>
<Name>Domain</Name>
</ProjectReference>
</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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Commands.CloseDatabase;
using ModernKeePass.Application.Database.Commands.SaveDatabase;
namespace ModernKeePass.Application.Common.Behaviors
{
public class DirtyStatusBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly List<string> _excludedCommands = new List<string>
{nameof(SaveDatabaseCommand), nameof(CloseDatabaseCommand)};
private readonly IDatabaseProxy _database;
public DirtyStatusBehavior(IDatabaseProxy database)
{
_database = database;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
{
var response = await next();
var queryName = typeof(TRequest).Name;
if (queryName.Contains("Command"))
{
_database.IsDirty = !_excludedCommands.Contains(queryName);
}
return response;
}
}
}

View File

@@ -0,0 +1,11 @@
using ModernKeePass.Domain.Dtos;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface ICredentialsProxy
{
string GeneratePassword(PasswordGenerationOptions options);
int EstimatePasswordComplexity(string password);
byte[] GenerateKeyFile(byte[] additionalEntropy);
}
}

View File

@@ -0,0 +1,11 @@
using System.Threading.Tasks;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface ICryptographyClient
{
Task<string> Protect(string value);
Task<string> UnProtect(string value);
byte[] Random(uint length);
}
}

View File

@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IDatabaseProxy
{
// PW Database properties
bool IsOpen { get; }
string Name { get; }
string RootGroupId { get; }
string RecycleBinId { get; set; }
string CipherId { get; set; }
string KeyDerivationId { get; set; }
string Compression { get; set; }
bool IsRecycleBinEnabled { get; set; }
// Custom properties
string FileAccessToken { get; set; }
int Size { get; set; }
bool IsDirty { get; set; }
int MaxHistoryCount { get; set; }
long MaxHistorySize { get; set; }
Task Open(byte[] file, Credentials credentials);
Task ReOpen(byte[] file);
Task Create(Credentials credentials, string name, DatabaseVersion version = DatabaseVersion.V4);
Task<byte[]> SaveDatabase();
void UpdateCredentials(Credentials credentials);
void CloseDatabase();
EntryEntity GetEntry(string id);
Task AddEntry(string parentGroupId, string entryId);
Task MoveEntry(string parentGroupId, string entryId, int index);
Task UpdateEntry(string entryId, string fieldName, object fieldValue, bool isProtected);
void DeleteField(string entryId, string fieldName);
Task RemoveEntry(string parentGroupId, string entryId);
EntryEntity CreateEntry(string parentGroupId);
void SortEntries(string groupId);
GroupEntity GetGroup(string id);
Task AddGroup(string parentGroupId, string groupId);
Task MoveGroup(string parentGroupId, string groupId, int index);
void UpdateGroup(GroupEntity group);
Task RemoveGroup(string parentGroupId, string groupId);
void DeleteEntity(string entityId);
GroupEntity CreateGroup(string parentGroupId, string name, bool isRecycleBin = false);
void SortSubGroups(string groupId);
void AddAttachment(string entryId, string attachmentName, byte[] attachmentContent);
void DeleteAttachment(string entryId, string attachmentName);
EntryEntity AddHistory(string entryId);
EntryEntity RestoreFromHistory(string entryId, int historyIndex);
void DeleteHistory(string entryId, int historyIndex);
IEnumerable<EntryEntity> Search(string groupId, string text);
IEnumerable<BaseEntity> GetAllGroups(string groupId);
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using ModernKeePass.Domain.Entities;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IDatabaseSettingsProxy
{
IEnumerable<BaseEntity> Ciphers { get; }
IEnumerable<BaseEntity> KeyDerivations { get; }
IEnumerable<string> CompressionAlgorithms { get; }
}
}

View File

@@ -0,0 +1,12 @@
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IEntityVm
{
string Id { get; set; }
Icon Icon { get; set; }
string ParentGroupId { get; set; }
string ParentGroupName { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using ModernKeePass.Domain.Dtos;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IFileProxy
{
Task<FileInfo> OpenFile(string name, string extension, bool addToRecent);
Task<FileInfo> CreateFile(string name, string extension, string description, bool addToRecent);
Task<byte[]> ReadBinaryFile(string path);
Task<IList<string>> ReadTextFile(string path);
Task WriteToLogFile(IEnumerable<string> data);
Task WriteBinaryContentsToFile(string path, byte[] contents);
void ReleaseFile(string path);
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface ILogger
{
Task LogError(Exception exception);
void LogTrace(string message, Dictionary<string, string> values);
}
}

View File

@@ -0,0 +1,7 @@
namespace ModernKeePass.Application.Common.Interfaces
{
public interface INotificationService
{
void Show(string title, string text);
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using ModernKeePass.Domain.Dtos;
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IRecentProxy
{
int EntryCount { get; }
IEnumerable<FileInfo> GetAll();
void ClearAll();
}
}

View File

@@ -0,0 +1,7 @@
namespace ModernKeePass.Application.Common.Interfaces
{
public interface IResourceProxy
{
string GetResourceValue(string key);
}
}

View File

@@ -1,6 +1,6 @@
namespace ModernKeePass.Interfaces
namespace ModernKeePass.Application.Common.Interfaces
{
public interface ISettingsService
public interface ISettingsProxy
{
T GetSetting<T>(string property, T defaultValue = default(T));
void PutSetting<T>(string property, T value);

View File

@@ -0,0 +1,10 @@
using AutoMapper;
namespace ModernKeePass.Application.Common.Mappings
{
public interface IMapFrom<T>
{
void Mapping(Profile profile);
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Reflection;
using AutoMapper;
namespace ModernKeePass.Application.Common.Mappings
{
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(typeof(MappingProfile).GetTypeInfo().Assembly);
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.ExportedTypes
.Where(t => t.GetTypeInfo().ImplementedInterfaces.Any(i =>
i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetTypeInfo().GetDeclaredMethod("Mapping");
methodInfo?.Invoke(instance, new object[] { this });
}
}
}
}

View File

@@ -0,0 +1,12 @@
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Application.Common.Models
{
public class BreadcrumbItem
{
public string Path { get; set; }
public string Name { get; set; }
public Icon Icon { get; set; }
}
}

View File

@@ -0,0 +1,26 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Database.Commands.CloseDatabase
{
public class CloseDatabaseCommand: IRequest
{
public class CloseDatabaseCommandHandler : IRequestHandler<CloseDatabaseCommand>
{
private readonly IDatabaseProxy _database;
public CloseDatabaseCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(CloseDatabaseCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
// Prevent reopening the database due to possible de-synchronization between app and data
if (_database.IsDirty) _database.FileAccessToken = null;
_database.CloseDatabase();
}
}
}
}

View File

@@ -0,0 +1,82 @@
using MediatR;
using System.Threading.Tasks;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Exceptions;
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Application.Database.Commands.CreateDatabase
{
public class CreateDatabaseCommand : IRequest
{
public string FilePath { get; set; }
public string Password { get; set; }
public string KeyFilePath { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public bool CreateSampleData { get; set; }
public class CreateDatabaseCommandHandler : IAsyncRequestHandler<CreateDatabaseCommand>
{
private readonly IDatabaseProxy _database;
private readonly IFileProxy _file;
public CreateDatabaseCommandHandler(IDatabaseProxy database, IFileProxy file)
{
_database = database;
_file = file;
}
public async Task Handle(CreateDatabaseCommand message)
{
if (_database.IsDirty) throw new DatabaseOpenException();
var version = DatabaseVersion.V2;
switch (message.Version)
{
case "4":
version = DatabaseVersion.V4;
break;
case "3":
version = DatabaseVersion.V3;
break;
}
await _database.Create(new Credentials
{
KeyFileContents = !string.IsNullOrEmpty(message.KeyFilePath) ? await _file.ReadBinaryFile(message.KeyFilePath) : null,
Password = message.Password
}, message.Name, version);
_database.FileAccessToken = message.FilePath;
if (message.CreateSampleData)
{
var bankingGroup = _database.CreateGroup(_database.RootGroupId, "Banking");
bankingGroup.Icon = Icon.Shop;
_database.UpdateGroup(bankingGroup);
var emailGroup = _database.CreateGroup(_database.RootGroupId, "Email");
emailGroup.Icon = Icon.Mail;
_database.UpdateGroup(emailGroup);
var internetGroup = _database.CreateGroup(_database.RootGroupId, "Internet");
internetGroup.Icon = Icon.World;
_database.UpdateGroup(internetGroup);
var sample1 = _database.CreateEntry(_database.RootGroupId);
await _database.UpdateEntry(sample1.Id, EntryFieldName.Title, "Sample Entry", false);
await _database.UpdateEntry(sample1.Id, EntryFieldName.UserName, "Username", false);
await _database.UpdateEntry(sample1.Id, EntryFieldName.Password, "Password", true);
await _database.UpdateEntry(sample1.Id, EntryFieldName.Url, "https://keepass.info/", false);
await _database.UpdateEntry(sample1.Id, EntryFieldName.Notes, "You may safely delete this sample", false);
var sample2 = _database.CreateEntry(_database.RootGroupId);
await _database.UpdateEntry(sample2.Id, EntryFieldName.Title, "Sample Entry #2", false);
await _database.UpdateEntry(sample2.Id, EntryFieldName.UserName, "Michael321", false);
await _database.UpdateEntry(sample2.Id, EntryFieldName.Password, "12345", true);
await _database.UpdateEntry(sample2.Id, EntryFieldName.Url, "https://keepass.info/help/kb/testform.html", false);
}
}
}
}
}

View File

@@ -0,0 +1,22 @@
using FluentValidation;
namespace ModernKeePass.Application.Database.Commands.CreateDatabase
{
public class CreateDatabaseCommandValidator : AbstractValidator<CreateDatabaseCommand>
{
public CreateDatabaseCommandValidator()
{
RuleFor(v => v.FilePath)
.NotNull()
.NotEmpty();
RuleFor(v => v.Password)
.NotNull()
.NotEmpty()
.When(v => string.IsNullOrEmpty(v.KeyFilePath));
RuleFor(v => v.KeyFilePath)
.NotNull()
.NotEmpty()
.When(v => string.IsNullOrEmpty(v.Password));
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using MediatR;
using System.Threading.Tasks;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Database.Commands.SaveDatabase
{
public class SaveDatabaseCommand : IRequest
{
public string FilePath { get; set; }
public class SaveDatabaseCommandHandler : IAsyncRequestHandler<SaveDatabaseCommand>
{
private readonly IDatabaseProxy _database;
private readonly IFileProxy _file;
private readonly ILogger _logger;
public SaveDatabaseCommandHandler(IDatabaseProxy database, IFileProxy file, ILogger logger)
{
_database = database;
_file = file;
_logger = logger;
}
public async Task Handle(SaveDatabaseCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
try
{
var timeToSave = Stopwatch.StartNew();
if (!string.IsNullOrEmpty(message.FilePath))
{
_database.FileAccessToken = message.FilePath;
}
var contents = await _database.SaveDatabase();
// Test DB integrity
_database.CloseDatabase();
await _database.ReOpen(contents);
// Transactional write to file
await _file.WriteBinaryContentsToFile(_database.FileAccessToken, contents);
timeToSave.Stop();
_logger.LogTrace("SaveCommand", new Dictionary<string, string>
{
{ "duration", timeToSave.ElapsedMilliseconds.ToString()},
{ "size", _database.Size.ToString()}
});
}
catch (Exception exception)
{
throw new SaveException(exception);
}
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Database.Commands.UpdateCredentials
{
public class UpdateCredentialsCommand: IRequest
{
public string Password { get; set; }
public string KeyFilePath { get; set; }
public class UpdateCredentialsCommandHandler : IAsyncRequestHandler<UpdateCredentialsCommand>
{
private readonly IDatabaseProxy _database;
private readonly IFileProxy _file;
public UpdateCredentialsCommandHandler(IDatabaseProxy database, IFileProxy file)
{
_database = database;
_file = file;
}
public async Task Handle(UpdateCredentialsCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
_database.UpdateCredentials(new Credentials
{
KeyFileContents = !string.IsNullOrEmpty(message.KeyFilePath) ? await _file.ReadBinaryFile(message.KeyFilePath) : null,
Password = message.Password
});
}
}
}
}

View File

@@ -0,0 +1,18 @@
namespace ModernKeePass.Application.Database.Models
{
public class DatabaseVm
{
public bool IsOpen { get; set; }
public string Name { get; set; }
public string RootGroupId { get; set; }
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; }
public int Size { get; internal set; }
public bool IsDirty { get; internal set; }
public int MaxHistoryCount { get; set; }
public long MaxHistorySize { get; set; }
}
}

View File

@@ -0,0 +1,43 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Database.Models;
using ModernKeePass.Domain.Common;
namespace ModernKeePass.Application.Database.Queries.GetDatabase
{
public class GetDatabaseQuery: IRequest<DatabaseVm>
{
public class GetDatabaseQueryHandler : IRequestHandler<GetDatabaseQuery, DatabaseVm>
{
private readonly IDatabaseProxy _databaseProxy;
public GetDatabaseQueryHandler(IDatabaseProxy databaseProxy)
{
_databaseProxy = databaseProxy;
}
public DatabaseVm Handle(GetDatabaseQuery message)
{
var database = new DatabaseVm
{
IsOpen = _databaseProxy.IsOpen
};
if (database.IsOpen)
{
database.Name = _databaseProxy.Name;
database.RootGroupId = _databaseProxy.RootGroupId;
database.IsRecycleBinEnabled = _databaseProxy.IsRecycleBinEnabled;
database.RecycleBinId = _databaseProxy.RecycleBinId == Constants.EmptyId ? null : _databaseProxy.RecycleBinId;
database.Compression = _databaseProxy.Compression;
database.CipherId = _databaseProxy.CipherId;
database.KeyDerivationId = _databaseProxy.KeyDerivationId;
database.Size = _databaseProxy.Size;
database.IsDirty = _databaseProxy.IsDirty;
database.MaxHistoryCount = _databaseProxy.MaxHistoryCount;
database.MaxHistorySize = _databaseProxy.MaxHistorySize;
}
return database;
}
}
}
}

View File

@@ -0,0 +1,43 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Database.Queries.OpenDatabase
{
public class OpenDatabaseQuery: IRequest
{
public string FilePath { get; set; }
public string Password { get; set; }
public string KeyFilePath { get; set; }
public class OpenDatabaseQueryHandler : IAsyncRequestHandler<OpenDatabaseQuery>
{
private readonly IDatabaseProxy _database;
private readonly IFileProxy _file;
public OpenDatabaseQueryHandler(IDatabaseProxy database, IFileProxy file)
{
_database = database;
_file = file;
}
public async Task Handle(OpenDatabaseQuery message)
{
if (_database.IsDirty) throw new DatabaseOpenException();
var file = await _file.ReadBinaryFile(message.FilePath);
var hasKeyFile = !string.IsNullOrEmpty(message.KeyFilePath);
await _database.Open(file, new Credentials
{
KeyFileContents = hasKeyFile ? await _file.ReadBinaryFile(message.KeyFilePath): null,
Password = message.Password
});
if (hasKeyFile) _file.ReleaseFile(message.KeyFilePath);
_database.Size = file.Length;
_database.FileAccessToken = message.FilePath;
}
}
}
}

View File

@@ -0,0 +1,22 @@
using FluentValidation;
namespace ModernKeePass.Application.Database.Queries.OpenDatabase
{
public class OpenDatabaseQueryValidator : AbstractValidator<OpenDatabaseQuery>
{
public OpenDatabaseQueryValidator()
{
RuleFor(v => v.FilePath)
.NotNull()
.NotEmpty();
RuleFor(v => v.Password)
.NotNull()
.NotEmpty()
.When(v => string.IsNullOrEmpty(v.KeyFilePath));
RuleFor(v => v.KeyFilePath)
.NotNull()
.NotEmpty()
.When(v => string.IsNullOrEmpty(v.Password));
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Database.Queries.ReOpenDatabase
{
public class ReOpenDatabaseQuery: IRequest
{
public class ReOpenDatabaseQueryHandler : IAsyncRequestHandler<ReOpenDatabaseQuery>
{
private readonly IDatabaseProxy _database;
private readonly IFileProxy _file;
public ReOpenDatabaseQueryHandler(IDatabaseProxy database, IFileProxy file)
{
_database = database;
_file = file;
}
public async Task Handle(ReOpenDatabaseQuery message)
{
if (_database.IsOpen) throw new DatabaseOpenException();
if (string.IsNullOrEmpty(_database.FileAccessToken)) throw new DatabaseClosedException();
var file = await _file.ReadBinaryFile(_database.FileAccessToken);
await _database.ReOpen(file);
}
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Reflection;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using ModernKeePass.Application.Common.Behaviors;
namespace ModernKeePass.Application
{
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
var assembly = typeof(DependencyInjection).GetTypeInfo().Assembly;
services.AddMediatR(assembly);
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(DirtyStatusBehavior<,>));
return services;
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.AddAttachment
{
public class AddAttachmentCommand : IRequest
{
public EntryVm Entry { get; set; }
public string AttachmentName { get; set; }
public byte[] AttachmentContent { get; set; }
public class AddAttachmentCommandHandler : IRequestHandler<AddAttachmentCommand>
{
private readonly IDatabaseProxy _database;
public AddAttachmentCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(AddAttachmentCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
if (message.Entry.Attachments.ContainsKey(message.AttachmentName)) throw new ArgumentException("AttachmentAlreadyExists", nameof(message.AttachmentName));
_database.AddAttachment(message.Entry.Id, message.AttachmentName, message.AttachmentContent);
message.Entry.Attachments.Add(message.AttachmentName, message.AttachmentContent);
}
}
}
}

View File

@@ -0,0 +1,33 @@
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.AddHistory
{
public class AddHistoryCommand : IRequest
{
public EntryVm Entry { get; set; }
public class AddHistoryCommandHandler : IRequestHandler<AddHistoryCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public AddHistoryCommandHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public void Handle(AddHistoryCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
var history = _database.AddHistory(message.Entry.Id);
message.Entry.History.Add(_mapper.Map<EntryVm>(history));
}
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.DeleteAttachment
{
public class DeleteAttachmentCommand : IRequest
{
public EntryVm Entry { get; set; }
public string AttachmentName { get; set; }
public class DeleteAttachmentCommandHandler : IRequestHandler<DeleteAttachmentCommand>
{
private readonly IDatabaseProxy _database;
public DeleteAttachmentCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(DeleteAttachmentCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
if (!message.Entry.Attachments.ContainsKey(message.AttachmentName)) throw new KeyNotFoundException("AttachmentDoesntExist");
_database.DeleteAttachment(message.Entry.Id, message.AttachmentName);
message.Entry.Attachments.Remove(message.AttachmentName);
}
}
}
}

View File

@@ -0,0 +1,29 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.DeleteField
{
public class DeleteFieldCommand: IRequest
{
public string EntryId { get; set; }
public string FieldName { get; set; }
public class DeleteFieldCommandHandler : IRequestHandler<DeleteFieldCommand>
{
private readonly IDatabaseProxy _database;
public DeleteFieldCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(DeleteFieldCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
_database.DeleteField(message.EntryId, message.FieldName);
}
}
}
}

View File

@@ -0,0 +1,31 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.DeleteHistory
{
public class DeleteHistoryCommand : IRequest
{
public EntryVm Entry { get; set; }
public int HistoryIndex { get; set; }
public class DeleteHistoryCommandHandler : IRequestHandler<DeleteHistoryCommand>
{
private readonly IDatabaseProxy _database;
public DeleteHistoryCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(DeleteHistoryCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
_database.DeleteHistory(message.Entry.Id, message.HistoryIndex);
message.Entry.History.RemoveAt(message.HistoryIndex);
}
}
}
}

View File

@@ -0,0 +1,34 @@
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.RestoreHistory
{
public class RestoreHistoryCommand : IRequest
{
public EntryVm Entry { get; set; }
public int HistoryIndex { get; set; }
public class RestoreHistoryCommandHandler : IRequestHandler<RestoreHistoryCommand>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public RestoreHistoryCommandHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public void Handle(RestoreHistoryCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
var entry = _database.RestoreFromHistory(message.Entry.Id, message.HistoryIndex);
message.Entry = _mapper.Map<EntryVm>(entry);
}
}
}
}

View File

@@ -0,0 +1,32 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Commands.UpsertField
{
public class UpsertFieldCommand : IRequest
{
public string EntryId { get; set; }
public string FieldName { get; set; }
public object FieldValue { get; set; }
public bool IsProtected { get; set; } = true;
public class UpsertFieldCommandHandler : IAsyncRequestHandler<UpsertFieldCommand>
{
private readonly IDatabaseProxy _database;
public UpsertFieldCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(UpsertFieldCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
await _database.UpdateEntry(message.EntryId, message.FieldName, message.FieldValue, message.IsProtected);
}
}
}
}

View File

@@ -0,0 +1,17 @@
using FluentValidation;
namespace ModernKeePass.Application.Entry.Commands.UpsertField
{
public class UpsertFieldCommandValidator: AbstractValidator<UpsertFieldCommand>
{
public UpsertFieldCommandValidator()
{
RuleFor(v => v.EntryId)
.NotNull()
.NotEmpty();
RuleFor(v => v.FieldName)
.NotNull()
.NotEmpty();
}
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using AutoMapper;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Application.Entry.Models
{
public class EntryVm: IEntityVm, IMapFrom<EntryEntity>
{
public string ParentGroupId { get; set; }
public string ParentGroupName { get; set; }
public string Id { get; set; }
public FieldVm Title { get; set; }
public FieldVm Username { get; set; }
public FieldVm Password { get; set; }
public FieldVm Notes { get; set; }
public FieldVm Url { get; set; }
public bool IsValidUrl => Uri.IsWellFormedUriString(Url.Value, UriKind.Absolute);
public List<FieldVm> AdditionalFields { get; set; }
public List<EntryVm> History { get; set; }
public Icon Icon { get; set; }
public Color ForegroundColor { get; set; }
public Color BackgroundColor { get; set; }
public bool HasExpirationDate { get; set; }
public DateTimeOffset ExpirationDate { get; set; }
public DateTimeOffset ModificationDate { get; set; }
public Dictionary<string, byte[]> Attachments { get; set; }
public override string ToString()
{
return ModificationDate.ToString("g");
}
public void Mapping(Profile profile)
{
profile.CreateMap<EntryEntity, EntryVm>()
.ForMember(d => d.Title, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
f.Name.Equals(EntryFieldName.Title, StringComparison.Ordinal)) ?? new FieldEntity { Name = EntryFieldName.Title, IsProtected = false } ))
.ForMember(d => d.Username, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
f.Name.Equals(EntryFieldName.UserName, StringComparison.Ordinal)) ?? new FieldEntity { Name = EntryFieldName.UserName, IsProtected = false } ))
.ForMember(d => d.Password, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
f.Name.Equals(EntryFieldName.Password, StringComparison.Ordinal)) ?? new FieldEntity { Name = EntryFieldName.Password, IsProtected = true } ))
.ForMember(d => d.Url, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
f.Name.Equals(EntryFieldName.Url, StringComparison.Ordinal)) ?? new FieldEntity { Name = EntryFieldName.Url, IsProtected = false } ))
.ForMember(d => d.Notes, opts => opts.MapFrom(s => s.Fields.FirstOrDefault(f =>
f.Name.Equals(EntryFieldName.Notes, StringComparison.Ordinal)) ?? new FieldEntity { Name = EntryFieldName.Notes, IsProtected = false } ))
.ForMember(d => d.AdditionalFields, opts => opts.MapFrom(s =>
s.Fields.Where(f => !EntryFieldName.StandardFieldNames.Contains(f.Name, StringComparer.Ordinal))))
.ForMember(d => d.History, opts => opts.MapFrom(s => s.History.Reverse()))
.ForMember(d => d.Icon, opts => opts.MapFrom(s => s.HasExpirationDate && s.ExpirationDate < DateTimeOffset.Now ? Icon.ReportHacked : s.Icon));
}
}
}

View File

@@ -0,0 +1,20 @@
using AutoMapper;
using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Domain.Entities;
namespace ModernKeePass.Application.Entry.Models
{
public class FieldVm: IMapFrom<FieldEntity>
{
public string Name { get; set; }
public string Value { get; set; }
public bool IsProtected { get; set; }
public override string ToString() => Value;
public void Mapping(Profile profile)
{
profile.CreateMap<FieldEntity, FieldVm>();
}
}
}

View File

@@ -0,0 +1,31 @@
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Entry.Queries.GetEntry
{
public class GetEntryQuery: IRequest<EntryVm>
{
public string Id { get; set; }
public class GetEntryQueryHandler: IRequestHandler<GetEntryQuery, EntryVm>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public GetEntryQueryHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public EntryVm Handle(GetEntryQuery message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
return _mapper.Map<EntryVm>(_database.GetEntry(message.Id));
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.AddEntry
{
public class AddEntryCommand : IRequest
{
public string ParentGroupId { get; set; }
public string EntryId { get; set; }
public class AddEntryCommandHandler : IAsyncRequestHandler<AddEntryCommand>
{
private readonly IDatabaseProxy _database;
public AddEntryCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(AddEntryCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
await _database.AddEntry(message.ParentGroupId, message.EntryId);
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.AddGroup
{
public class AddGroupCommand : IRequest
{
public string ParentGroupId { get; set; }
public string GroupId { get; set; }
public class AddGroupCommandHandler : IAsyncRequestHandler<AddGroupCommand>
{
private readonly IDatabaseProxy _database;
public AddGroupCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(AddGroupCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
await _database.AddGroup(message.ParentGroupId, message.GroupId);
}
}
}
}

View File

@@ -0,0 +1,36 @@
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
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 : IRequestHandler<CreateEntryCommand, EntryVm>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public CreateEntryCommandHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public EntryVm Handle(CreateEntryCommand message)
{
if (!_database.IsOpen) 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,37 @@
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
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 : IRequestHandler<CreateGroupCommand, GroupVm>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public CreateGroupCommandHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public GroupVm Handle(CreateGroupCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
var group = _database.CreateGroup(message.ParentGroup.Id, message.Name, message.IsRecycleBin);
var groupVm = _mapper.Map<GroupVm>(group);
message.ParentGroup.Groups.Add(groupVm);
return groupVm;
}
}
}
}

View File

@@ -0,0 +1,46 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Common;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.DeleteEntry
{
public class DeleteEntryCommand : IRequest
{
public string ParentGroupId { get; set; }
public string EntryId { get; set; }
public string RecycleBinName { get; set; }
public class DeleteEntryCommandHandler : IAsyncRequestHandler<DeleteEntryCommand>
{
private readonly IDatabaseProxy _database;
public DeleteEntryCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(DeleteEntryCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
if (_database.IsRecycleBinEnabled && (string.IsNullOrEmpty(_database.RecycleBinId) || _database.RecycleBinId.Equals(Constants.EmptyId)))
{
_database.CreateGroup(_database.RootGroupId, message.RecycleBinName, true);
}
if (!_database.IsRecycleBinEnabled || message.ParentGroupId.Equals(_database.RecycleBinId))
{
_database.DeleteEntity(message.EntryId);
}
else
{
await _database.AddEntry(_database.RecycleBinId, message.EntryId);
}
await _database.RemoveEntry(message.ParentGroupId, message.EntryId);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Common;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.DeleteGroup
{
public class DeleteGroupCommand : IRequest
{
public string ParentGroupId { get; set; }
public string GroupId { get; set; }
public string RecycleBinName { get; set; }
public class DeleteGroupCommandHandler : IAsyncRequestHandler<DeleteGroupCommand>
{
private readonly IDatabaseProxy _database;
public DeleteGroupCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(DeleteGroupCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
var isRecycleBin = message.GroupId.Equals(_database.RecycleBinId);
if (_database.IsRecycleBinEnabled && (string.IsNullOrEmpty(_database.RecycleBinId) || _database.RecycleBinId.Equals(Constants.EmptyId)))
{
_database.CreateGroup(_database.RootGroupId, message.RecycleBinName, true);
}
if (!_database.IsRecycleBinEnabled || message.ParentGroupId.Equals(_database.RecycleBinId) || isRecycleBin)
{
_database.DeleteEntity(message.GroupId);
}
else
{
await _database.AddGroup(_database.RecycleBinId, message.GroupId);
}
await _database.RemoveGroup(message.ParentGroupId, message.GroupId);
if (isRecycleBin) _database.RecycleBinId = Constants.EmptyId;
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.MoveEntry
{
public class MoveEntryCommand : IRequest
{
public GroupVm ParentGroup { get; set; }
public EntryVm Entry { get; set; }
public int Index { get; set; }
public class MoveEntryCommandHandler : IAsyncRequestHandler<MoveEntryCommand>
{
private readonly IDatabaseProxy _database;
public MoveEntryCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(MoveEntryCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
await _database.MoveEntry(message.ParentGroup.Id, message.Entry.Id, message.Index);
message.ParentGroup.Entries.Insert(message.Index, message.Entry);
}
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.MoveGroup
{
public class MoveGroupCommand : IRequest
{
public GroupVm ParentGroup { get; set; }
public GroupVm Group { get; set; }
public int Index { get; set; }
public class MoveGroupCommandHandler : IAsyncRequestHandler<MoveGroupCommand>
{
private readonly IDatabaseProxy _database;
public MoveGroupCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(MoveGroupCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
await _database.MoveGroup(message.ParentGroup.Id, message.Group.Id, message.Index);
message.ParentGroup.Groups.Insert(message.Index, message.Group);
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.RemoveEntry
{
public class RemoveEntryCommand : IRequest
{
public string ParentGroupId { get; set; }
public string EntryId { get; set; }
public class RemoveEntryCommandHandler : IAsyncRequestHandler<RemoveEntryCommand>
{
private readonly IDatabaseProxy _database;
public RemoveEntryCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(RemoveEntryCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
await _database.RemoveEntry(message.ParentGroupId, message.EntryId);
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.RemoveGroup
{
public class RemoveGroupCommand : IRequest
{
public string ParentGroupId { get; set; }
public string GroupId { get; set; }
public class RemoveGroupCommandHandler : IAsyncRequestHandler<RemoveGroupCommand>
{
private readonly IDatabaseProxy _database;
public RemoveGroupCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public async Task Handle(RemoveGroupCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
await _database.RemoveGroup(message.ParentGroupId, message.GroupId);
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Linq;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
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 : IRequestHandler<SortEntriesCommand>
{
private readonly IDatabaseProxy _database;
public SortEntriesCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SortEntriesCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
_database.SortEntries(message.Group.Id);
message.Group.Entries = message.Group.Entries.OrderBy(e => e.Title.Value).ToList();
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Linq;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
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 : IRequestHandler<SortGroupsCommand>
{
private readonly IDatabaseProxy _database;
public SortGroupsCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SortGroupsCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
_database.SortSubGroups(message.Group.Id);
message.Group.Groups = message.Group.Groups.OrderBy(g => g.Title).ToList();
}
}
}
}

View File

@@ -0,0 +1,41 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Enums;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Commands.UpdateGroup
{
public class UpdateGroupCommand : IRequest
{
public GroupVm Group { get; set; }
public string Title { get; set; }
public Icon Icon { get; set; }
public class UpdateGroupCommandHandler : IRequestHandler<UpdateGroupCommand>
{
private readonly IDatabaseProxy _database;
public UpdateGroupCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(UpdateGroupCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
var group = new GroupEntity
{
Id = message.Group.Id,
Name = message.Title,
Icon = message.Icon
};
_database.UpdateGroup(group);
message.Group.Title = message.Title;
message.Group.Icon = message.Icon;
}
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using AutoMapper;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Common.Mappings;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Application.Group.Models
{
public class GroupVm: IEntityVm, IMapFrom<GroupEntity>
{
public string ParentGroupId { get; set; }
public string ParentGroupName { get; set; }
public string Id { get; set; }
public string Title { get; set; }
public Icon Icon { get; set; }
public List<GroupVm> Groups { get; set; }
public List<EntryVm> Entries { get; set; }
public override string ToString()
{
return Title;
}
public void Mapping(Profile profile)
{
profile.CreateMap<GroupEntity, GroupVm>()
.ForMember(d => d.Title, opts => opts.MapFrom(s => s.Name))
.MaxDepth(2);
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Queries.GetAllGroups
{
public class GetAllGroupsQuery : IRequest<IEnumerable<GroupVm>>
{
public string GroupId { get; set; }
public class GetAllGroupsQueryHandler : IRequestHandler<GetAllGroupsQuery, IEnumerable<GroupVm>>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public GetAllGroupsQueryHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public IEnumerable<GroupVm> Handle(GetAllGroupsQuery message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
var groups = new List<GroupVm> {_mapper.Map<GroupVm>(_database.GetGroup(message.GroupId))};
groups.AddRange(_database.GetAllGroups(message.GroupId).Select(g => _mapper.Map<GroupVm>(g)));
return groups;
}
}
}
}

View File

@@ -0,0 +1,31 @@
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Group.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Queries.GetGroup
{
public class GetGroupQuery : IRequest<GroupVm>
{
public string Id { get; set; }
public class GetGroupQueryHandler : IRequestHandler<GetGroupQuery, GroupVm>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public GetGroupQueryHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public GroupVm Handle(GetGroupQuery message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
return _mapper.Map<GroupVm>(_database.GetGroup(message.Id));
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Entry.Models;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Group.Queries.SearchEntries
{
public class SearchEntriesQuery : IRequest<IEnumerable<EntryVm>>
{
public string GroupId { get; set; }
public string SearchText { get; set; }
public class SearchEntriesQueryHandler : IRequestHandler<SearchEntriesQuery, IEnumerable<EntryVm>>
{
private readonly IDatabaseProxy _database;
private readonly IMapper _mapper;
public SearchEntriesQueryHandler(IDatabaseProxy database, IMapper mapper)
{
_database = database;
_mapper = mapper;
}
public IEnumerable<EntryVm> Handle(SearchEntriesQuery message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
return _database.Search(message.GroupId, message.SearchText).Select(e => _mapper.Map<EntryVm>(e));
}
}
}
}

View File

@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Enums;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Import.Commands.ImportFromCsv
{
public class ImportFromCsvCommand : IRequest
{
public string FilePath { get; set; }
public string DestinationGroupId { get; set; }
public bool HasHeaderRow { get; set; }
public char Delimiter { get; set; } = ';';
public Dictionary<int, string> FieldMappings { get; set; }
public class CreateDatabaseCommandHandler : IAsyncRequestHandler<ImportFromCsvCommand>
{
private readonly IDatabaseProxy _database;
private readonly IFileProxy _file;
public CreateDatabaseCommandHandler(IDatabaseProxy database, IFileProxy file)
{
_database = database;
_file = file;
}
public async Task Handle(ImportFromCsvCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
var fileContents = await _file.ReadTextFile(message.FilePath);
for (var index = message.HasHeaderRow ? 1 : 0; index < fileContents.Count; index++)
{
var line = fileContents[index];
var fields = line.Split(message.Delimiter);
var entry = _database.CreateEntry(message.DestinationGroupId);
for (var i = 0; i < fields.Length; i++)
{
var fieldMapping = message.FieldMappings[i];
await _database.UpdateEntry(entry.Id, fieldMapping, fields[i], fieldMapping == EntryFieldName.Password);
}
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
using FluentValidation;
namespace ModernKeePass.Application.Import.Commands.ImportFromCsv
{
public class ImportFromCsvCommandValidator : AbstractValidator<ImportFromCsvCommand>
{
public ImportFromCsvCommandValidator()
{
RuleFor(v => v.FilePath)
.NotNull()
.NotEmpty();
RuleFor(v => v.DestinationGroupId)
.NotNull()
.NotEmpty();
}
}
}

View File

@@ -0,0 +1,27 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Parameters.Commands.SetCipher
{
public class SetCipherCommand : IRequest
{
public string CipherId { get; set; }
public class SetCipherCommandHandler : IRequestHandler<SetCipherCommand>
{
private readonly IDatabaseProxy _database;
public SetCipherCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetCipherCommand message)
{
if (_database.IsOpen) _database.CipherId = message.CipherId;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -0,0 +1,27 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Parameters.Commands.SetCompression
{
public class SetCompressionCommand : IRequest
{
public string Compression { get; set; }
public class SetCompressionCommandHandler : IRequestHandler<SetCompressionCommand>
{
private readonly IDatabaseProxy _database;
public SetCompressionCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetCompressionCommand message)
{
if (_database.IsOpen) _database.Compression = message.Compression;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -0,0 +1,27 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Parameters.Commands.SetHasRecycleBin
{
public class SetHasRecycleBinCommand : IRequest
{
public bool HasRecycleBin { get; set; }
public class SetHasRecycleBinCommandHandler : IRequestHandler<SetHasRecycleBinCommand>
{
private readonly IDatabaseProxy _database;
public SetHasRecycleBinCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetHasRecycleBinCommand message)
{
if (_database.IsOpen) _database.IsRecycleBinEnabled = message.HasRecycleBin;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -0,0 +1,27 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Parameters.Commands.SetKeyDerivation
{
public class SetKeyDerivationCommand : IRequest
{
public string KeyDerivationId { get; set; }
public class SetKeyDerivationCommandHandler : IRequestHandler<SetKeyDerivationCommand>
{
private readonly IDatabaseProxy _database;
public SetKeyDerivationCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetKeyDerivationCommand message)
{
if (_database.IsOpen) _database.KeyDerivationId = message.KeyDerivationId;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -0,0 +1,27 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Parameters.Commands.SetMaxHistoryCount
{
public class SetMaxHistoryCountCommand : IRequest
{
public int MaxHistoryCount { get; set; }
public class SetMaxHistoryCountCommandHandler : IRequestHandler<SetMaxHistoryCountCommand>
{
private readonly IDatabaseProxy _database;
public SetMaxHistoryCountCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetMaxHistoryCountCommand message)
{
if (_database.IsOpen) _database.MaxHistoryCount = message.MaxHistoryCount;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -0,0 +1,28 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Parameters.Commands.SetMaxHistorySize
{
public class SetMaxHistorySizeCommand : IRequest
{
public long MaxHistorySize { get; set; }
public class SetMaxHistorySizeCommandHandler : IRequestHandler<SetMaxHistorySizeCommand>
{
private readonly IDatabaseProxy _database;
public SetMaxHistorySizeCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetMaxHistorySizeCommand message)
{
if (_database.IsOpen) _database.MaxHistorySize = message.MaxHistorySize;
else throw new DatabaseClosedException();
}
}
}
}

View File

@@ -0,0 +1,28 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Common;
using ModernKeePass.Domain.Exceptions;
namespace ModernKeePass.Application.Parameters.Commands.SetRecycleBin
{
public class SetRecycleBinCommand : IRequest
{
public string RecycleBinId { get; set; }
public class SetRecycleBinCommandHandler : IRequestHandler<SetRecycleBinCommand>
{
private readonly IDatabaseProxy _database;
public SetRecycleBinCommandHandler(IDatabaseProxy database)
{
_database = database;
}
public void Handle(SetRecycleBinCommand message)
{
if (!_database.IsOpen) throw new DatabaseClosedException();
_database.RecycleBinId = message.RecycleBinId ?? Constants.EmptyId;
}
}
}
}

View File

@@ -0,0 +1,8 @@
namespace ModernKeePass.Application.Parameters.Models
{
public class CipherVm
{
public string Id { get; set; }
public string Name { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
namespace ModernKeePass.Application.Parameters.Models
{
public class KeyDerivationVm
{
public string Id { get; set; }
public string Name { get; set; }
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Parameters.Models;
namespace ModernKeePass.Application.Parameters.Queries.GetCiphers
{
public class GetCiphersQuery: IRequest<IEnumerable<CipherVm>>
{
public class GetCiphersQueryHandler: IRequestHandler<GetCiphersQuery, IEnumerable<CipherVm>>
{
private readonly IDatabaseSettingsProxy _databaseSettings;
public GetCiphersQueryHandler(IDatabaseSettingsProxy databaseSettings)
{
_databaseSettings = databaseSettings;
}
public IEnumerable<CipherVm> Handle(GetCiphersQuery message)
{
return _databaseSettings.Ciphers.Select(c => new CipherVm
{
Id = c.Id,
Name = c.Name
});
}
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Linq;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
namespace ModernKeePass.Application.Parameters.Queries.GetCompressions
{
public class GetCompressionsQuery : IRequest<IEnumerable<string>>
{
public class GetCompressionsQueryHandler : IRequestHandler<GetCompressionsQuery, IEnumerable<string>>
{
private readonly IDatabaseSettingsProxy _databaseSettings;
public GetCompressionsQueryHandler(IDatabaseSettingsProxy databaseSettings)
{
_databaseSettings = databaseSettings;
}
public IEnumerable<string> Handle(GetCompressionsQuery message)
{
return _databaseSettings.CompressionAlgorithms.OrderBy(c => c);
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Application.Parameters.Models;
namespace ModernKeePass.Application.Parameters.Queries.GetKeyDerivations
{
public class GetKeyDerivationsQuery : IRequest<IEnumerable<KeyDerivationVm>>
{
public class GetKeyDerivationsQueryHandler : IRequestHandler<GetKeyDerivationsQuery, IEnumerable<KeyDerivationVm>>
{
private readonly IDatabaseSettingsProxy _databaseSettings;
public GetKeyDerivationsQueryHandler(IDatabaseSettingsProxy databaseSettings)
{
_databaseSettings = databaseSettings;
}
public IEnumerable<KeyDerivationVm> Handle(GetKeyDerivationsQuery message)
{
return _databaseSettings.KeyDerivations.Select(c => new KeyDerivationVm
{
Id = c.Id,
Name = c.Name
});
}
}
}
}

View File

@@ -1,17 +1,15 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ModernKeePass.Shared")]
[assembly: AssemblyTitle("ModernKeePass.Application")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ModernKeePass.Shared")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyProduct("ModernKeePass.Application")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]

View File

@@ -0,0 +1,37 @@
using System.Threading.Tasks;
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
namespace ModernKeePass.Application.Security.Commands.GenerateKeyFile
{
public class GenerateKeyFileCommand : IRequest
{
public string KeyFilePath { get; set; }
public bool AddAdditionalEntropy { get; set; }
public class GenerateKeyFileCommandHandler : IAsyncRequestHandler<GenerateKeyFileCommand>
{
private readonly ICredentialsProxy _security;
private readonly IFileProxy _file;
private readonly ICryptographyClient _cryptography;
public GenerateKeyFileCommandHandler(ICredentialsProxy security, IFileProxy file, ICryptographyClient cryptography)
{
_security = security;
_file = file;
_cryptography = cryptography;
}
public async Task Handle(GenerateKeyFileCommand message)
{
byte[] entropy = null;
if (message.AddAdditionalEntropy)
{
entropy = _cryptography.Random(10);
}
var keyFile = _security.GenerateKeyFile(entropy);
await _file.WriteBinaryContentsToFile(message.KeyFilePath, keyFile);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Domain.Dtos;
namespace ModernKeePass.Application.Security.Commands.GeneratePassword
{
public class GeneratePasswordCommand: IRequest<string>
{
public int PasswordLength { get; set; }
public bool UpperCasePatternSelected { get; set; }
public bool LowerCasePatternSelected { get; set; }
public bool DigitsPatternSelected { get; set; }
public bool SpecialPatternSelected { get; set; }
public bool MinusPatternSelected { get; set; }
public bool UnderscorePatternSelected { get; set; }
public bool SpacePatternSelected { get; set; }
public bool BracketsPatternSelected { get; set; }
public string CustomChars { get; set; }
public class GeneratePasswordCommandHandler: IRequestHandler<GeneratePasswordCommand, string>
{
private readonly ICredentialsProxy _security;
public GeneratePasswordCommandHandler(ICredentialsProxy security)
{
_security = security;
}
public string Handle(GeneratePasswordCommand message)
{
var options = new PasswordGenerationOptions
{
PasswordLength = message.PasswordLength,
BracketsPatternSelected = message.BracketsPatternSelected,
CustomChars = message.CustomChars,
DigitsPatternSelected = message.DigitsPatternSelected,
LowerCasePatternSelected = message.LowerCasePatternSelected,
MinusPatternSelected = message.MinusPatternSelected,
SpacePatternSelected = message.SpacePatternSelected,
SpecialPatternSelected = message.SpecialPatternSelected,
UnderscorePatternSelected = message.UnderscorePatternSelected,
UpperCasePatternSelected = message.UpperCasePatternSelected
};
return _security.GeneratePassword(options);
}
}
}
}

View File

@@ -0,0 +1,25 @@
using MediatR;
using ModernKeePass.Application.Common.Interfaces;
namespace ModernKeePass.Application.Security.Queries.EstimatePasswordComplexity
{
public class EstimatePasswordComplexityQuery : IRequest<int>
{
public string Password { get; set; }
public class EstimatePasswordComplexityQueryHandler : IRequestHandler<EstimatePasswordComplexityQuery, int>
{
private readonly ICredentialsProxy _security;
public EstimatePasswordComplexityQueryHandler(ICredentialsProxy security)
{
_security = security;
}
public int Handle(EstimatePasswordComplexityQuery message)
{
return _security.EstimatePasswordComplexity(message.Password);
}
}
}
}

View File

@@ -0,0 +1,16 @@
{
"supports": {},
"dependencies": {
"AutoMapper": "5.2.0",
"FluentValidation": "8.6.2",
"MediatR": "3.0.1",
"MediatR.Extensions.Microsoft.DependencyInjection": "2.0.0",
"Microsoft.Extensions.DependencyInjection": "1.1.1",
"Microsoft.NETCore.Portable.Compatibility": "1.0.2",
"NETStandard.Library": "2.0.3",
"Splat": "3.0.0"
},
"frameworks": {
"netstandard1.2": {}
}
}

View File

@@ -0,0 +1,14 @@
namespace ModernKeePass.Domain.Common
{
public static class Constants
{
public static string EmptyId => "00000000000000000000000000000000";
public static class Extensions
{
public static string Any => "*";
public static string Kdbx => ".kdbx";
public static string Key => ".key";
}
}
}

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9A0759F1-9069-4841-99E3-3BEC44E17356}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ModernKeePass.Domain</RootNamespace>
<AssemblyName>ModernKeePass.Domain</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|ARM' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>ARM</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|ARM' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Common\Constants.cs" />
<Compile Include="Dtos\Attachment.cs" />
<Compile Include="Dtos\Credentials.cs" />
<Compile Include="Dtos\FileInfo.cs" />
<Compile Include="Dtos\PasswordGenerationOptions.cs" />
<Compile Include="Entities\BaseEntity.cs" />
<Compile Include="Entities\EntryEntity.cs" />
<Compile Include="Entities\FieldEntity.cs" />
<Compile Include="Entities\GroupEntity.cs" />
<Compile Include="Enums\CredentialStatusTypes.cs" />
<Compile Include="Enums\DatabaseVersion.cs" />
<Compile Include="Enums\EntryFieldName.cs" />
<Compile Include="Enums\Icon.cs" />
<Compile Include="Enums\ImportFormat.cs" />
<Compile Include="Exceptions\DatabaseClosedException.cs" />
<Compile Include="Exceptions\DatabaseOpenException.cs" />
<Compile Include="Exceptions\DatabaseTooBigException.cs" />
<Compile Include="Exceptions\NavigationException.cs" />
<Compile Include="Exceptions\SaveException.cs" />
<Compile Include="Interfaces\IDateTime.cs" />
<Compile Include="Interfaces\IHasSelectableObject.cs" />
<Compile Include="Interfaces\IImportFormat.cs" />
<Compile Include="Interfaces\IIsEnabled.cs" />
<Compile Include="Interfaces\ISelectableModel.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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,8 @@
namespace ModernKeePass.Domain.Dtos
{
public class Attachment
{
public string Name { get; set; }
public byte[] Content { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
namespace ModernKeePass.Domain.Dtos
{
public class Credentials
{
public string Password { get; set; }
public byte[] KeyFileContents { get; set; }
// TODO: add Windows Hello
}
}

View File

@@ -0,0 +1,9 @@
namespace ModernKeePass.Domain.Dtos
{
public class FileInfo
{
public string Id { get; set; }
public string Name { get; set; }
public string Path { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
namespace ModernKeePass.Domain.Dtos
{
public class PasswordGenerationOptions
{
public int PasswordLength { get; set; }
public bool UpperCasePatternSelected { get; set; }
public bool LowerCasePatternSelected { get; set; }
public bool DigitsPatternSelected { get; set; }
public bool SpecialPatternSelected { get; set; }
public bool MinusPatternSelected { get; set; }
public bool UnderscorePatternSelected { get; set; }
public bool SpacePatternSelected { get; set; }
public bool BracketsPatternSelected { get; set; }
public string CustomChars { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace ModernKeePass.Domain.Entities
{
public class BaseEntity
{
public string Id { get; set; }
public string Name { get; set; }
public string ParentGroupId { get; set; }
public string ParentGroupName { get; set; }
public DateTimeOffset ModificationDate { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Domain.Entities
{
public class EntryEntity: BaseEntity
{
public IEnumerable<FieldEntity> Fields { get; set; } = new List<FieldEntity>();
public IEnumerable<EntryEntity> History { get; set; } = new List<EntryEntity>();
public Dictionary<string, byte[]> Attachments { get; set; } = new Dictionary<string, byte[]>();
public DateTimeOffset ExpirationDate { get; set; }
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 @@
namespace ModernKeePass.Domain.Entities
{
public class FieldEntity
{
public string Name { get; set; }
public string Value { get; set; }
public bool IsProtected { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
using ModernKeePass.Domain.Enums;
namespace ModernKeePass.Domain.Entities
{
public class GroupEntity : BaseEntity
{
public List<GroupEntity> Groups { get; set; } = new List<GroupEntity>();
public List<EntryEntity> Entries { get; set; } = new List<EntryEntity>();
public Icon Icon { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
namespace ModernKeePass.Domain.Enums
{
public enum CredentialStatusTypes
{
Normal = 0,
Error = 1,
Warning = 3,
Success = 5
}
}

View File

@@ -0,0 +1,9 @@
namespace ModernKeePass.Domain.Enums
{
public enum DatabaseVersion
{
V2,
V3,
V4
}
}

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace ModernKeePass.Domain.Enums
{
public static class EntryFieldName
{
public const string Title = nameof(Title);
public const string UserName = nameof(UserName);
public const string Password = nameof(Password);
public const string Url = "URL";
public const string Notes = nameof(Notes);
public const string Icon = nameof(Icon);
public const string ExpirationDate = nameof(ExpirationDate);
public const string HasExpirationDate = nameof(HasExpirationDate);
public const string BackgroundColor = nameof(BackgroundColor);
public const string ForegroundColor = nameof(ForegroundColor);
public static IEnumerable<string> StandardFieldNames => new[]
{
Title,
UserName,
Password,
Url,
Notes,
Icon,
ExpirationDate,
HasExpirationDate,
BackgroundColor,
ForegroundColor
};
}
}

View File

@@ -0,0 +1,54 @@
namespace ModernKeePass.Domain.Enums
{
public enum Icon
{
Delete,
Edit,
Save,
Cancel,
Accept,
Home,
Camera,
Setting,
Mail,
Find,
Help,
Clock,
Crop,
World,
Flag,
PreviewLink,
Document,
ProtectedDocument,
ContactInfo,
ViewAll,
Rotate,
List,
Shop,
BrowsePhotos,
Caption,
Repair,
Page,
Paste,
Important,
SlideShow,
MapDrive,
ContactPresence,
Contact,
Folder,
View,
Permissions,
Map,
CellPhone,
OutlineStar,
Calculator,
Library,
SyncFolder,
GoToStart,
ZeroBars,
FourBars,
Scan,
ReportHacked,
Stop
}
}

View File

@@ -0,0 +1,7 @@
namespace ModernKeePass.Domain.Enums
{
public enum ImportFormat
{
CSV
}
}

View File

@@ -0,0 +1,7 @@
using System;
namespace ModernKeePass.Domain.Exceptions
{
public class DatabaseClosedException: Exception
{ }
}

View File

@@ -0,0 +1,7 @@
using System;
namespace ModernKeePass.Domain.Exceptions
{
public class DatabaseOpenException: Exception
{ }
}

View File

@@ -0,0 +1,6 @@
using System;
namespace ModernKeePass.Domain.Exceptions
{
public class DatabaseTooBigException: Exception { }
}

View File

@@ -1,6 +1,6 @@
using System;
namespace ModernKeePass.Exceptions
namespace ModernKeePass.Domain.Exceptions
{
public class NavigationException: Exception
{

View File

@@ -0,0 +1,16 @@
using System;
namespace ModernKeePass.Domain.Exceptions
{
public class SaveException : Exception
{
public new string Message { get; }
public new string Source { get; }
public SaveException(Exception exception)
{
Message = exception.Message;
Source = exception.Source;
}
}
}

View File

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

View File

@@ -1,4 +1,4 @@
namespace ModernKeePass.Interfaces
namespace ModernKeePass.Domain.Interfaces
{
public interface IHasSelectableObject
{

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ModernKeePass.Domain.Interfaces
{
public interface IImportFormat
{
Task<List<Dictionary<string, string>>> Import(string path);
}
}

View File

@@ -1,4 +1,4 @@
namespace ModernKeePass.Interfaces
namespace ModernKeePass.Domain.Interfaces
{
public interface IIsEnabled
{

View File

@@ -1,4 +1,4 @@
namespace ModernKeePass.Interfaces
namespace ModernKeePass.Domain.Interfaces
{
public interface ISelectableModel
{

Some files were not shown because too many files have changed in this diff Show More