12 Commits
V1.10 ... V1.12

Author SHA1 Message Date
BONNEVILLE Geoffroy
4ae02fc07b Corrected test case to reflect page removal 2018-01-03 11:37:09 +01:00
BONNEVILLE Geoffroy
047fca32bf Hid donations pages (need to implement 3rd party API) 2018-01-03 11:29:51 +01:00
BONNEVILLE Geoffroy
abbff449c0 Removed User Account from composite key (probably never going to work as intended)
Changed copy URL to navigate to URL in entry quick menu
2017-12-26 17:54:13 +01:00
BONNEVILLE Geoffroy
fba668860b WIP user accounts - not working at all 2017-12-21 18:24:01 +01:00
BONNEVILLE Geoffroy
acb196d9c2 WIP Windows User Accounts Composite Key integration 2017-12-20 18:49:11 +01:00
BONNEVILLE Geoffroy
dfa3a21e6b Removed an useless converter
Groups in menu now use instead a Template selector (depending on IIsSelected)
2017-12-19 18:44:35 +01:00
BONNEVILLE Geoffroy
7ff6bccbc4 Added some tests
Removed false first group, replaced it a button in the header
Code refactor
2017-12-18 18:53:42 +01:00
BONNEVILLE Geoffroy
88e5b80778 Version bump to 1.12
Added small menu on entries list to copy login, password and URL
2017-12-18 14:09:04 +01:00
BONNEVILLE Geoffroy
d127431396 Third try on store special character escaping 2017-12-18 11:52:49 +01:00
BONNEVILLE Geoffroy
588703ecd6 Added a small text explaining how to reorder entries
Code cleanup
2017-12-14 17:54:14 +01:00
BONNEVILLE Geoffroy
223c9b641a Updated release notes
HTML encoded french text
2017-12-14 17:28:22 +01:00
BONNEVILLE Geoffroy
7db34d6517 Corrected critical error when opening file from explorer
Reverted filter mechanisme to search box because of numerous regressions (on ordering, refresh etc.)
2017-12-14 17:15:28 +01:00
47 changed files with 450 additions and 276 deletions

View File

@@ -0,0 +1,33 @@
using System;
using Windows.UI.Xaml;
using Microsoft.Xaml.Interactivity;
using ModernKeePass.Common;
namespace ModernKeePass.Actions
{
public class NavigateToUrlAction : DependencyObject, IAction
{
public string Url
{
get { return (string)GetValue(UrlProperty); }
set { SetValue(UrlProperty, value); }
}
public static readonly DependencyProperty UrlProperty =
DependencyProperty.Register("Url", typeof(string), typeof(NavigateToUrlAction), new PropertyMetadata(string.Empty));
public object Execute(object sender, object parameter)
{
try
{
var uri = new Uri(Url);
return Windows.System.Launcher.LaunchUriAsync(uri).GetAwaiter().GetResult();
}
catch (Exception ex)
{
MessageDialogHelper.ShowErrorDialog(ex);
return false;
}
}
}
}

View File

@@ -31,6 +31,7 @@ namespace ModernKeePass
InitializeComponent();
Suspending += OnSuspending;
UnhandledException += OnUnhandledException;
Database = new DatabaseService();
}
#region Event Handlers
@@ -122,7 +123,6 @@ namespace ModernKeePass
}*/
// Ensure the current window is active
Window.Current.Activate();
Database = new DatabaseService();
}
/// <summary>

View File

@@ -1,20 +0,0 @@
using System;
using Windows.UI.Text;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
public class BooleanToFontStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var boolean = value is bool ? (bool)value : false;
return boolean ? FontStyle.Italic : FontStyle.Normal;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -109,6 +109,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="Actions\ClipboardAction.cs" />
<Compile Include="Actions\NavigateToUrlAction.cs" />
<Compile Include="Actions\SetupFocusAction.cs" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
@@ -117,6 +118,7 @@
<Compile Include="Interfaces\IRecent.cs" />
<Compile Include="Interfaces\IRecentItem.cs" />
<Compile Include="Interfaces\IResource.cs" />
<Compile Include="TemplateSelectors\SelectableDataTemplateSelector.cs" />
<Compile Include="ViewModels\DonateVm.cs" />
<Compile Include="Views\MainPageFrames\DonatePage.xaml.cs">
<DependentUpon>DonatePage.xaml</DependentUpon>
@@ -165,7 +167,6 @@
<Compile Include="Converters\ColorToBrushConverter.cs" />
<Compile Include="Converters\DoubleToSolidColorBrushConverter.cs" />
<Compile Include="Converters\InverseBooleanToVisibilityConverter.cs" />
<Compile Include="Converters\BooleanToFontStyleConverter.cs" />
<Compile Include="Converters\PluralizationConverter.cs" />
<Compile Include="Converters\ProgressBarLegalValuesConverter.cs" />
<Compile Include="Converters\TextToWidthConverter.cs" />
@@ -434,9 +435,6 @@
<Content Include="Assets\Wide310x150Logo.scale-140.png" />
<Content Include="Assets\Wide310x150Logo.scale-180.png" />
<Content Include="Assets\Wide310x150Logo.scale-80.png" />
<Content Include="Data\WindowsStoreProxy.xml">
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '12.0' ">
<VisualStudioVersion>12.0</VisualStudioVersion>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest" xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.10.0.31" />
<Identity Name="wismna.ModernKeePass" Publisher="CN=0719A91A-C322-4EE0-A257-E60733EECF06" Version="1.12.0.32" />
<Properties>
<DisplayName>ModernKeePass</DisplayName>
<PublisherDisplayName>wismna</PublisherDisplayName>

View File

@@ -24,6 +24,6 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.10.0.0")]
[assembly: AssemblyFileVersion("1.10.0.0")]
[assembly: AssemblyVersion("1.12.0.0")]
[assembly: AssemblyFileVersion("1.12.0.0")]
[assembly: ComVisible(false)]

View File

@@ -27,7 +27,6 @@ namespace ModernKeePass.Services
}
private readonly PwDatabase _pwDatabase = new PwDatabase();
private readonly ISettings _settings;
private readonly IResource _resource;
private StorageFile _databaseFile;
private GroupVm _recycleBin;

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Store;
using ModernKeePass.Interfaces;
@@ -27,34 +26,13 @@ namespace ModernKeePass.Services
public LicenseService()
{
// Initialize the license info for use in the app that is uploaded to the Store.
// Uncomment the following line in the release version of your app.
//_licenseInformation = CurrentApp.LicenseInformation;
// Initialize the license info for testing.
// Comment the following line in the release version of your app.
//_licenseInformation = CurrentAppSimulator.LicenseInformation;
#if DEBUG
try
{
var proxyFile = Package.Current.InstalledLocation.GetFileAsync("data\\WindowsStoreProxy.xml").GetAwaiter().GetResult();
CurrentAppSimulator.ReloadSimulatorAsync(proxyFile).GetAwaiter().GetResult();
}
catch { }
var listing = CurrentAppSimulator.LoadListingInformationAsync().GetAwaiter().GetResult();
#else
var listing = CurrentApp.LoadListingInformationAsync().GetAwaiter().GetResult();
#endif
Products = listing.ProductListings;
}
public async Task<int> Purchase(string addOn)
{
#if DEBUG
var purchaseResults = await CurrentAppSimulator.RequestProductPurchaseAsync(addOn);
#else
var purchaseResults = await CurrentApp.RequestProductPurchaseAsync(addOn);
#endif
switch (purchaseResults.Status)
{
case ProductPurchaseStatus.Succeeded:
@@ -79,11 +57,7 @@ namespace ModernKeePass.Services
private async Task<PurchaseResult> ReportFulfillmentAsync(Guid transactionId, string productName)
{
#if DEBUG
var result = await CurrentAppSimulator.ReportConsumableFulfillmentAsync(productName, transactionId);
#else
var result = await CurrentApp.ReportConsumableFulfillmentAsync(productName, transactionId);
#endif
return (PurchaseResult) result;
}

View File

@@ -270,4 +270,7 @@
<data name="SettingsMenuItemSecurity" xml:space="preserve">
<value>Security</value>
</data>
<data name="CompositeKeyErrorUserAccount" xml:space="preserve">
<value>user account</value>
</data>
</root>

View File

@@ -171,11 +171,14 @@
<data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve">
<value>Password</value>
</data>
<data name="CompositeKeyUserAccount.Text" xml:space="preserve">
<value>Windows User Account</value>
</data>
<data name="DonateButton.Content" xml:space="preserve">
<value>Donate</value>
</data>
<data name="DonateDesc.Text" xml:space="preserve">
<value>Like this app? Why not make a small donation to support my work and help this app stay ad-free :) ?</value>
<value>Like this app? Why not make a small donation to support my work and help me keep it ad-free :) ?</value>
</data>
<data name="EntryExpirationDate.Content" xml:space="preserve">
<value>Expiration date</value>
@@ -183,6 +186,15 @@
<data name="EntryExpirationTooltip.Content" xml:space="preserve">
<value>Password has expired</value>
</data>
<data name="EntryItemCopyLogin.Text" xml:space="preserve">
<value>Copy login</value>
</data>
<data name="EntryItemCopyPassword.Text" xml:space="preserve">
<value>Copy password</value>
</data>
<data name="EntryItemCopyUrl.Text" xml:space="preserve">
<value>Navigate to URL</value>
</data>
<data name="EntryLogin.Text" xml:space="preserve">
<value>User name or login</value>
</data>
@@ -270,6 +282,9 @@
<data name="RecentClear.Text" xml:space="preserve">
<value>Clear all</value>
</data>
<data name="ReorderEntriesLabel.Text" xml:space="preserve">
<value>Drag and drop entries to reorder them</value>
</data>
<data name="SaveAsButton.Content" xml:space="preserve">
<value>Save as...</value>
</data>

View File

@@ -271,4 +271,7 @@
<data name="SettingsMenuItemSecurity" xml:space="preserve">
<value>Sécurité</value>
</data>
<data name="CompositeKeyErrorUserAccount" xml:space="preserve">
<value>compte utilisateur</value>
</data>
</root>

View File

@@ -171,6 +171,9 @@
<data name="CompositeKeyPassword.PlaceholderText" xml:space="preserve">
<value>Mot de passe</value>
</data>
<data name="CompositeKeyUserAccount.Text" xml:space="preserve">
<value>Compte Utilisateur Windows</value>
</data>
<data name="DonateButton.Content" xml:space="preserve">
<value>Donner</value>
</data>
@@ -183,6 +186,15 @@
<data name="EntryExpirationTooltip.Content" xml:space="preserve">
<value>Le mot de passe a expiré</value>
</data>
<data name="EntryItemCopyLogin.Text" xml:space="preserve">
<value>Copier le login</value>
</data>
<data name="EntryItemCopyPassword.Text" xml:space="preserve">
<value>Copier le mot de passe</value>
</data>
<data name="EntryItemCopyUrl.Text" xml:space="preserve">
<value>Naviguer vers l'URL</value>
</data>
<data name="EntryLogin.Text" xml:space="preserve">
<value>Nom d'utilisateur ou login</value>
</data>
@@ -270,6 +282,9 @@
<data name="RecentClear.Text" xml:space="preserve">
<value>Supprimer tout</value>
</data>
<data name="ReorderEntriesLabel.Text" xml:space="preserve">
<value>Glissez-déposez les entrées pour les réorganiser</value>
</data>
<data name="SaveAsButton.Content" xml:space="preserve">
<value>Sauvegarder sous...</value>
</data>

View File

@@ -0,0 +1,18 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Interfaces;
namespace ModernKeePass.TemplateSelectors
{
public class SelectableDataTemplateSelector: DataTemplateSelector
{
public DataTemplate TrueItem { get; set; }
public DataTemplate FalseItem { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var isSelectableItem = item as ISelectableModel;
return isSelectableItem != null && isSelectableItem.IsSelected ? TrueItem : FalseItem;
}
}
}

View File

@@ -44,7 +44,17 @@ namespace ModernKeePass.ViewModels
}
}
public bool IsValid => !_isOpening && (HasPassword || HasKeyFile && KeyFile != null);
public bool HasUserAccount
{
get { return _hasUserAccount; }
set
{
SetProperty(ref _hasUserAccount, value);
OnPropertyChanged("IsValid");
}
}
public bool IsValid => !_isOpening && (HasPassword || HasKeyFile && KeyFile != null || HasUserAccount);
public string Status
{
@@ -91,8 +101,10 @@ namespace ModernKeePass.ViewModels
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password?.ToCharArray());
private bool _hasPassword;
private bool _hasKeyFile;
private bool _hasUserAccount;
private bool _isOpening;
private string _password = string.Empty;
private string _status;
@@ -136,6 +148,7 @@ namespace ModernKeePass.ViewModels
if (HasPassword) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserPassword"));
if (HasPassword && HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserOr"));
if (HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserKeyFile"));
if (HasKeyFile) errorMessage.Append(_resource.GetResourceValue("CompositeKeyErrorUserAccount"));
UpdateStatus(errorMessage.ToString(), StatusTypes.Error);
break;
case DatabaseService.DatabaseStatus.Error:
@@ -169,6 +182,7 @@ namespace ModernKeePass.ViewModels
var compositeKey = new CompositeKey();
if (HasPassword) compositeKey.AddUserKey(new KcpPassword(Password));
if (HasKeyFile && KeyFile != null) compositeKey.AddUserKey(new KcpKeyFile(IOConnectionInfo.FromFile(KeyFile)));
if (HasUserAccount) compositeKey.AddUserKey(new KcpUserAccount());
return compositeKey;
}
}

View File

@@ -5,7 +5,6 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Interfaces;
using ModernKeePass.Mappings;
using ModernKeePass.Services;
using ModernKeePassLib;
using ModernKeePassLib.Cryptography.PasswordGenerator;
using ModernKeePassLib.Security;
@@ -117,6 +116,16 @@ namespace ModernKeePass.ViewModels
}
}
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
NotifyPropertyChanged("IsVisible");
}
}
public bool IsRecycleOnDelete => _database.RecycleBinEnabled && !ParentGroup.IsSelected;
public bool IsRevealPassword
@@ -155,6 +164,7 @@ namespace ModernKeePass.ViewModels
private bool _isEditMode;
private bool _isRevealPassword;
private double _passwordLength = 25;
private bool _isVisible = true;
private void NotifyPropertyChanged(string propertyName)
{

View File

@@ -61,7 +61,7 @@ namespace ModernKeePass.ViewModels
{
get
{
if (_pwGroup == null) return Symbol.Add;
//if (_pwGroup == null) return Symbol.Add;
var result = PwIconToSegoeMapping.GetSymbolFromIcon(_pwGroup.IconId);
return result == Symbol.More ? Symbol.Folder : result;
}
@@ -80,19 +80,6 @@ namespace ModernKeePass.ViewModels
set { SetProperty(ref _isMenuClosed, value); }
}
public string Filter
{
get { return _filter; }
set
{
SetProperty(ref _filter, value);
OnPropertyChanged("EntriesFiltered");
}
}
public ObservableCollection<EntryVm> EntriesFiltered =>
new ObservableCollection<EntryVm>(Entries.Where(e => e.Name.IndexOf(Filter, StringComparison.OrdinalIgnoreCase) >= 0));
public string Path
{
get
@@ -109,7 +96,6 @@ namespace ModernKeePass.ViewModels
private bool _isEditMode;
private PwEntry _reorderedEntry;
private ObservableCollection<EntryVm> _entries = new ObservableCollection<EntryVm>();
private string _filter = string.Empty;
private bool _isMenuClosed = true;
public GroupVm() {}
@@ -128,7 +114,6 @@ namespace ModernKeePass.ViewModels
Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this)));
Entries.CollectionChanged += Entries_CollectionChanged;
Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this, recycleBinId)));
Groups.Insert(0, new GroupVm());
}
private void Entries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

View File

@@ -94,6 +94,7 @@ namespace ModernKeePass.ViewModels
{
_database = database;
Groups = _database?.RootGroup.Groups;
Groups?.Insert(0, new GroupVm());
}
}
}

View File

@@ -107,14 +107,14 @@ namespace ModernKeePass.ViewModels
PageType = typeof(AboutPage),
Destination = destinationFrame,
SymbolIcon = Symbol.Help
},
}/*,
new MainMenuItemVm
{
Title = resource.GetResourceValue("MainMenuItemDonate"),
PageType = typeof(DonatePage),
Destination = destinationFrame,
SymbolIcon = Symbol.Shop
}
}*/
};
// Auto-select the Recent Items menu item if the conditions are met
SelectedItem = mainMenuItems.FirstOrDefault(m => m.IsSelected);

View File

@@ -458,7 +458,13 @@
<ProgressBar Value="{Binding PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}" Maximum="128" Width="350" HorizontalAlignment="Left" Foreground="{Binding PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushComplexityConverter}}" />
<CheckBox x:Uid="EntryShowPassword" HorizontalAlignment="Left" Margin="-3,0,0,0" IsChecked="{Binding IsRevealPassword, Mode=TwoWay}" IsEnabled="{Binding IsRevealPasswordEnabled}" />
<TextBlock TextWrapping="Wrap" Text="URL" FontSize="18"/>
<local:TextBoxWithButton x:Name="UrlTextBox" HorizontalAlignment="Left" Text="{Binding Url, Mode=TwoWay}" Height="32" Width="350" MaxLength="256" Style="{StaticResource TextBoxWithButtonStyle}" ButtonClick="UrlButton_Click" ButtonSymbol="&#xE111;" ButtonTooltip="Navigate to URL" />
<local:TextBoxWithButton x:Name="UrlTextBox" HorizontalAlignment="Left" Text="{Binding Url, Mode=TwoWay}" Height="32" Width="350" MaxLength="256" Style="{StaticResource TextBoxWithButtonStyle}" ButtonSymbol="&#xE111;" ButtonTooltip="Navigate to URL">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:NavigateToUrlAction Url="{Binding Url}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<TextBlock x:Uid="EntryNotes" TextWrapping="Wrap" FontSize="18" />
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Notes, Mode=TwoWay}" Width="350" Height="200" AcceptsReturn="True" IsSpellCheckEnabled="True" />
<CheckBox x:Uid="EntryExpirationDate" FontSize="18" IsChecked="{Binding HasExpirationDate, Mode=TwoWay}" />

View File

@@ -93,19 +93,6 @@ namespace ModernKeePass.Views
if (Frame.CanGoBack) Frame.GoBack();
}
private async void UrlButton_Click(object sender, RoutedEventArgs e)
{
try
{
var uri = new Uri(UrlTextBox.Text);
await Windows.System.Launcher.LaunchUriAsync(uri);
}
catch (Exception ex)
{
MessageDialogHelper.ShowErrorDialog(ex);
}
}
private void EntryDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true);

View File

@@ -8,7 +8,6 @@
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
xmlns:controls="using:ModernKeePass.Controls"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
x:Name="PageRoot"
x:Class="ModernKeePass.Views.GroupDetailPage"
@@ -16,7 +15,6 @@
SizeChanged="GroupDetailPage_OnSizeChanged">
<Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToFontStyleConverter x:Key="BooleanToFontStyleConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
@@ -94,7 +92,7 @@
Source="{Binding Groups}" />
<CollectionViewSource
x:Name="EntriesViewSource"
Source="{Binding EntriesFiltered}" />
Source="{Binding Entries}" />
<CollectionViewSource
x:Name="EntriesZoomedOutViewSource"
Source="{Binding EntriesZoomedOut}" IsSourceGrouped="True"/>
@@ -127,27 +125,30 @@
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}">
<ListView.Resources>
<DataTemplate x:Name="GroupOtherItem">
<DataTemplate x:Name="IsRecycleBin">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Name}" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock Text="{Binding Name}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="{Binding IsSelected, Converter={StaticResource BooleanToFontStyleConverter}}" />
<TextBlock Text="{Binding Name}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="Italic" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Name="GroupFirstItem">
<DataTemplate x:Name="IsNotRecycleBin">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip>
<ToolTip x:Uid="GroupNewItemTooltip" />
<ToolTip Content="{Binding Name}" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock x:Name="GroupTextBlock" x:Uid="GroupNewItemTextBox" FontWeight="SemiBold" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" />
</SymbolIcon>
<TextBlock Text="{Binding Name}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
</DataTemplate>
</ListView.Resources>
<ListView.ItemTemplateSelector>
<templateSelectors:SelectableDataTemplateSelector FalseItem="{StaticResource IsNotRecycleBin}" TrueItem="{StaticResource IsRecycleBin}" />
</ListView.ItemTemplateSelector>
<ListView.ItemsSource>
<Binding Source="{StaticResource GroupsViewSource}"/>
</ListView.ItemsSource>
@@ -161,25 +162,33 @@
</ListView.ItemContainerStyle>
<ListView.HeaderTemplate>
<DataTemplate>
<ToggleButton Style="{StaticResource HamburgerToggleButton}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:ChangePropertyAction PropertyName="Width" Value="Auto" TargetObject="{Binding ElementName=LeftListViewColumn}"/>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:ChangePropertyAction PropertyName="Width" Value="50" TargetObject="{Binding ElementName=LeftListViewColumn}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ToggleButton>
<StackPanel Orientation="Vertical">
<ToggleButton Style="{StaticResource HamburgerToggleButton}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:ChangePropertyAction PropertyName="Width" Value="Auto" TargetObject="{Binding ElementName=LeftListViewColumn}"/>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:ChangePropertyAction PropertyName="Width" Value="50" TargetObject="{Binding ElementName=LeftListViewColumn}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ToggleButton>
<Button Padding="0" Height="50" Margin="0" Style="{StaticResource NoBorderButtonStyle}" Background="Transparent" BorderThickness="0" Click="CreateGroup_ButtonClick">
<StackPanel Orientation="Horizontal" Margin="13,0,5,0">
<SymbolIcon Symbol="Add">
<ToolTipService.ToolTip>
<ToolTip x:Uid="GroupNewItemTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock x:Uid="GroupNewItemTextBox" FontWeight="SemiBold" TextWrapping="NoWrap" FontSize="16" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
</Button>
<Border BorderBrush="White" BorderThickness="0,0,0,1" />
</StackPanel>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplateSelector>
<templateSelectors:FirstItemDataTemplateSelector
FirstItem="{StaticResource GroupFirstItem}"
OtherItem="{StaticResource GroupOtherItem}" />
</ListView.ItemTemplateSelector>
</ListView>
<!-- Horizontal scrolling grid -->
<TextBlock Grid.Column="1" x:Uid="ReorderEntriesLabel" Margin="20,20,0,0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource BodyTextBlockStyle}" />
<HyperlinkButton Grid.Column="1" VerticalAlignment="Top" Margin="40,10,0,0" Click="CreateEntry_ButtonClick" Visibility="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}" HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Add"></SymbolIcon>
@@ -188,6 +197,7 @@
</HyperlinkButton>
<SemanticZoom Grid.Column="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" Margin="20,60,0,0">
<SemanticZoom.ZoomedInView>
<!-- Horizontal scrolling grid -->
<GridView
x:Name="GridView"
AutomationProperties.AutomationId="ItemGridView"
@@ -211,6 +221,7 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform>
@@ -223,6 +234,34 @@
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
</StackPanel>
<Button Grid.Column="2" Style="{StaticResource NoBorderButtonStyle}" VerticalAlignment="Bottom">
<SymbolIcon Symbol="More" />
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="EntryItemCopyLogin">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding UserName}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemCopyPassword">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding Password}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemCopyUrl">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:NavigateToUrlAction Url="{Binding Url}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
@@ -336,15 +375,22 @@
</TextBox>
<TextBlock FontSize="12" Text="{Binding Path}" />
</StackPanel>
<Button Grid.Column="2" x:Name="FilterButton" Style="{StaticResource NoBorderButtonStyle}" Height="50">
<SymbolIcon Symbol="Filter" />
<Button Grid.Column="2" x:Name="SearchButton" Style="{StaticResource NoBorderButtonStyle}" Height="50">
<SymbolIcon Symbol="Find" />
<Button.Flyout>
<Flyout>
<controls:TextBoxWithButton x:Uid="GroupFilter" ButtonSymbol="&#xE16E;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" />
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0" />
</Style>
</Flyout.FlyoutPresenterStyle>
<!--<controls:TextBoxWithButton x:Uid="GroupFilter" ButtonSymbol="&#xE16E;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" />-->
<SearchBox x:Uid="GroupSearch" Width="350" Padding="12" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" />
</Flyout>
</Button.Flyout>
</Button>
<controls:TextBoxWithButton Grid.Column="2" x:Name="FilterBox" x:Uid="GroupFilter" ButtonSymbol="&#xE16E;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" />
<!--<controls:TextBoxWithButton Grid.Column="2" x:Name="FilterBox" x:Uid="GroupFilter" ButtonSymbol="&#xE16E;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" />-->
<SearchBox Grid.Column="2" x:Uid="GroupSearch" x:Name="SearchBox" Padding="12" Width="350" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" />
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DragDropGroup">
@@ -363,23 +409,23 @@
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FilterGroup">
<VisualStateGroup x:Name="SearchGroup">
<VisualState x:Name="Small">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FilterButton" Storyboard.TargetProperty="Visibility">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FilterBox" Storyboard.TargetProperty="Visibility">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Large">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FilterButton" Storyboard.TargetProperty="Visibility">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FilterBox" Storyboard.TargetProperty="Visibility">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>

View File

@@ -1,5 +1,7 @@
using System;
using System.Linq;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
@@ -82,9 +84,6 @@ namespace ModernKeePass.Views
{
case -1:
return;
case 0:
group = Model.AddNewGroup();
break;
default:
group = LeftListView.SelectedItem as GroupVm;
break;
@@ -143,6 +142,10 @@ namespace ModernKeePass.Views
{
Frame.Navigate(typeof(EntryDetailPage), Model.AddNewEntry());
}
private void CreateGroup_ButtonClick(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(GroupDetailPage), Model.AddNewGroup());
}
private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
@@ -150,6 +153,22 @@ namespace ModernKeePass.Views
e.Data.RequestedOperation = DataPackageOperation.Move;
}
private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx://Assets/ModernKeePass-SmallLogo.scale-80.png"));
var results = Model.Entries.Where(e => e.Name.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5);
foreach (var result in results)
{
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Name, result.ParentGroup.Name, result.Id, imageUri, string.Empty);
}
}
private void SearchBox_OnResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args)
{
var entry = Model.Entries.FirstOrDefault(e => e.Id == args.Tag);
Frame.Navigate(typeof(EntryDetailPage), entry);
}
private void GroupDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true);

View File

@@ -11,7 +11,6 @@
xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d" >
<UserControl.Resources>
<SolidColorBrush x:Key="ErrorColorBrush" Color="Red"/>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroungBrushConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
@@ -55,7 +54,7 @@
</ToolTipService.ToolTip>
</SymbolIcon>
</HyperlinkButton>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="28" FontSize="14" FontWeight="Light" Text="{Binding Status}" Foreground="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" Visibility="{Binding Status, Converter={StaticResource EmptyStringToVisibilityConverter}}" />
<Button Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" Content="{Binding ButtonLabel, ElementName=UserControl}" Click="OpenButton_OnClick" Background="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" IsEnabled="{Binding IsValid}" />
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="28" FontSize="14" FontWeight="Light" Text="{Binding Status}" Foreground="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" Visibility="{Binding Status, Converter={StaticResource EmptyStringToVisibilityConverter}}" />
</Grid>
</UserControl>

View File

@@ -1,11 +1,2 @@
You can now reorder entries
Entries and groups are sortable
New Home button that allows returning to the main menu from anywhere
Change default database creation settings
Settings is accessible from main menu
Clear Recent databases
New French translation
Better layout whith small screen size
Search becomes Filter
Design and usability improvements
Bugs correction
New inline menu on Entries to quickly copy login, password, and navigate to URL
Design changes

View File

@@ -1,6 +1,6 @@
En avez-vous assez d'essayer de retenir des quantit<EFBFBD>s de mots de passe ? Etes-vous soucieux que le fait d'utiliser le m<EFBFBD>me mot de passe partout vous rend vuln<EFBFBD>rable ?
ModernKeePass est un gestionnaire de mots de passe gratuit, libre, facile <EFBFBD> utiliser mais n<EFBFBD>anmoins s<>r, bas<61> sur la technologie certifi<66>e et r<>pandue KeePass 2.x et compatible avec celui-ci.
Vous pouvez stocker ou g<EFBFBD>n<EFBFBD>rer vos mots de passe dans une base de donn<EFBFBD>es chiffr<66>e, qui peut <20>tre plac<61>e n'importe o<EFBFBD> (ordinateur personel/tablette, cloud, cl<EFBFBD> USB...)
En avez-vous assez d'essayer de retenir des quantit&amp;eacute;s de mots de passe ? Etes-vous soucieux que le fait d'utiliser le m&amp;ecirc;me mot de passe partout vous rend vuln&amp;eacute;rable ?
ModernKeePass est un gestionnaire de mots de passe gratuit, libre, facile &amp;agrave; utiliser mais n&amp;eacute;anmoins s&amp;ucirc;r, bas&amp;eacute; sur la technologie certifi&amp;eacute;e et r&amp;eacute;pandue KeePass 2.x et compatible avec celui-ci.
Vous pouvez stocker ou g&amp;eacute;n&amp;eacute;rer vos mots de passe dans une base de donn&amp;eacute;es chiffr&amp;eacute;e, qui peut &amp;ecirc;tre plac&amp;eacute;e n'importe o&amp;ugrave; (ordinateur personnel/tablette, cloud, cl&amp;eacute; USB...)
Bien que la s<EFBFBD>curit<EFBFBD> informatique soit tr<EFBFBD>s importante, cette application essaie de rester simple <EFBFBD> utiliser et <EFBFBD> comprendre. Vos suggestions sont les bienvenues !
Bien que la s&amp;eacute;curit&amp;eacute; informatique soit tr&amp;egrave;s importante, cette application essaie de rester simple &amp;agrave; utiliser et &amp;agrave; comprendre. Vos suggestions sont les bienvenues !
Fonctionne sur Windows 10, 8.1 et RT.

View File

@@ -1 +1 @@
Page d'entr<EFBFBD>e avec g<>n<EFBFBD>rateur de mot de passe
Page d'entr&amp;eacute;e avec g&amp;eacute;n&amp;eacute;rateur de mot de passe

View File

@@ -1 +1 @@
Filtrez vos entr<EFBFBD>es pour rapidement trouver celle qui vous int<EFBFBD>resse
Filtrez vos entr&amp;eacute;es pour rapidement trouver celle qui vous int&amp;eacute;resse

View File

@@ -1 +1 @@
Vue d'un groupe, avec ses sous-groupes et ses entr<EFBFBD>es
Vue d'un groupe, avec ses sous-groupes et ses entr&amp;eacute;es

View File

@@ -1 +1 @@
Cr<EFBFBD>ez de nouvelles bases de donn<EFBFBD>es, en d<>finissant un mot de passe et/ou un fichier de cl<EFBFBD> existant ou nouveau
Cr&amp;eacute;ez de nouvelles bases de donn&amp;eacute;es, en d&amp;eacute;finissant un mot de passe et/ou un fichier de cl&amp;eacute; existant ou nouveau

View File

@@ -1 +1 @@
Ouvrez une base de donn<EFBFBD>es avec mot de passe et/ou fichier de cl<EFBFBD>
Ouvrez une base de donn&amp;eacute;es avec mot de passe et/ou fichier de cl&amp;eacute;

View File

@@ -1 +1 @@
Ouvrez les fichiers r<EFBFBD>cents
Ouvrez les fichiers r&amp;eacute;cents

View File

@@ -1 +1 @@
Vue d'ensemble avec le zoom s<EFBFBD>mantique
Vue d'ensemble avec le zoom s&amp;eacute;mantique

View File

@@ -1 +1 @@
Param<EFBFBD>tres de l'application
Param&amp;egrave;tres de l'application

View File

@@ -1,11 +1,2 @@
Vous pouvez d<>sormais r<>organiser vos entr<74>es
Les entr<74>es et les groupes peuvent <20>tre tri<72>s
Nouveau bouton d'Accueil qui permet de retourner au menu principal
Changez les options de cr<63>ation des nouvelles bases de donn<6E>es
Les param<61>tres sont accessibles depuis le menu principal
Effacez les <20>l<EFBFBD>ments r<>cents
Traduction fran<61>aise
Meilleur rendu en petit <20>cran
La recherche devient Filtre
Am<EFBFBD>liorations de design et d'ergonomie
Corrections de bogues
Ajout d'un menu int&amp;eacute;gr&amp;eacute; &amp;agrave; la liste d'entr&amp;eacute;es afin de copier rapidement le login, mot de passe et de naviguer vers l'URL
Quelques changements de design

View File

@@ -28,7 +28,7 @@ namespace ModernKeePassApp.Test
public void TestOpen()
{
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Closed, _database.Status);
_database.DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx").GetAwaiter().GetResult();
_database.DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx").GetAwaiter().GetResult();
Assert.AreEqual((int)DatabaseService.DatabaseStatus.Opening, _database.Status);
OpenOrCreateDatabase(false);
}

View File

@@ -50,7 +50,7 @@ namespace ModernKeePassApp.Test.Mock
public void Save()
{
throw new NotImplementedException();
// Do Nothing
}
public void Save(StorageFile file)

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Store;
using ModernKeePass.Interfaces;
namespace ModernKeePassApp.Test.Mock
{
public class LicenseServiceMock: ILicenseService
{
public IReadOnlyDictionary<string, ProductListing> Products { get; }
public LicenseServiceMock()
{
try
{
var proxyFile = Package.Current.InstalledLocation.GetFileAsync("data\\WindowsStoreProxy.xml").GetAwaiter().GetResult();
CurrentAppSimulator.ReloadSimulatorAsync(proxyFile).GetAwaiter().GetResult();
}
catch { }
var listing = CurrentAppSimulator.LoadListingInformationAsync().GetAwaiter().GetResult();
Products = listing.ProductListings;
}
public Task<int> Purchase(string addOn)
{
throw new System.NotImplementedException();
}
}
}

View File

@@ -121,6 +121,7 @@
<ItemGroup>
<Compile Include="DatabaseTests.cs" />
<Compile Include="Mock\DatabaseServiceMock.cs" />
<Compile Include="Mock\LicenseServiceMock.cs" />
<Compile Include="Mock\RecentServiceMock.cs" />
<Compile Include="Mock\ResourceServiceMock.cs" />
<Compile Include="Mock\SettingsServiceMock.cs" />
@@ -131,11 +132,14 @@
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
<Content Include="Databases\TestDatabase.kdbx" />
<Content Include="Data\TestDatabase.kdbx" />
<None Include="ModernKeePassApp.Test_TemporaryKey.pfx" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Data\WindowsStoreProxy.xml">
<SubType>Designer</SubType>
</Content>
<Content Include="Images\UnitTestLogo.scale-100.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View File

@@ -6,6 +6,7 @@ using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using ModernKeePass.ViewModels;
using ModernKeePass.Views;
using ModernKeePassApp.Test.Mock;
using ModernKeePassLib;
namespace ModernKeePassApp.Test
{
@@ -30,7 +31,7 @@ namespace ModernKeePassApp.Test
var mainVm = new MainVm(null, null, database, _resource, _recent);
Assert.AreEqual(1, mainVm.MainMenuItems.Count());
var firstGroup = mainVm.MainMenuItems.FirstOrDefault();
Assert.AreEqual(7, firstGroup.Count());
Assert.AreEqual(6, firstGroup.Count());
database.Status = 1;
mainVm = new MainVm(null, null, database, _resource, _recent);
@@ -56,13 +57,20 @@ namespace ModernKeePassApp.Test
Assert.AreEqual(15.0, compositeKeyVm.PasswordComplexityIndicator);
}
[TestMethod]
public void TestDonateVm()
{
var donateVm = new DonateVm(new LicenseServiceMock());
Assert.AreEqual(4, donateVm.Donations.Count);
}
[TestMethod]
public void TestOpenVm()
{
var database = new DatabaseServiceMock
{
Status = 1,
DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx")
DatabaseFile = Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx")
.GetAwaiter().GetResult()
};
var openVm = new OpenVm(database);
@@ -79,7 +87,7 @@ namespace ModernKeePassApp.Test
public void TestRecentVm()
{
var mru = StorageApplicationPermissions.MostRecentlyUsedList;
mru.Add(Package.Current.InstalledLocation.GetFileAsync(@"Databases\TestDatabase.kdbx")
mru.Add(Package.Current.InstalledLocation.GetFileAsync(@"Data\TestDatabase.kdbx")
.GetAwaiter().GetResult(), "MockDatabase");
var recentVm = new RecentVm();
Assert.IsTrue(recentVm.RecentItems.Count == 1);
@@ -88,10 +96,19 @@ namespace ModernKeePassApp.Test
mru.Clear();
}
/*[TestMethod]
[TestMethod]
public void TestSaveVm()
{
}*/
var database = new DatabaseServiceMock
{
Status = 2
};
var saveVm = new SaveVm(database);
saveVm.Save(false);
Assert.AreEqual(2, database.Status);
saveVm.Save();
Assert.AreEqual(0, database.Status);
}
[TestMethod]
public void TestSettingsVm()
@@ -105,5 +122,33 @@ namespace ModernKeePassApp.Test
var selectedItem = (ListMenuItemVm) settingsVm.SelectedItem;
Assert.AreEqual(typeof(SettingsNewDatabasePage), selectedItem.PageType);
}
[TestMethod]
public void TestEntryVm()
{
var database = new DatabaseServiceMock
{
Status = 2
};
var entryVm = new EntryVm(new PwEntry(true, true), new GroupVm(), database)
{
Name = "Test",
UserName = "login",
Password = "password"
};
}
[TestMethod]
public void TestGroupVm()
{
var database = new DatabaseServiceMock
{
Status = 2
};
var entryVm = new GroupVm(new PwGroup(true, true), new GroupVm(), database)
{
Name = "Test"
};
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using ModernKeePassLib.Native;
namespace ModernKeePassLib.Cryptography
{
public static class ProtectedData
{
public static byte[] Unprotect(byte[] pbEnc, byte[] mPbOptEnt, DataProtectionScope currentUser)
{
throw new NotImplementedException();
}
public static byte[] Protect(byte[] pbPlain, byte[] mPbOptEnt, DataProtectionScope currentUser)
{
throw new NotImplementedException();
}
}
}

View File

@@ -35,132 +35,132 @@ using ModernKeePassLib.Utility;
namespace ModernKeePassLib.Keys
{
/// <summary>
/// A user key depending on the currently logged on Windows user account.
/// </summary>
public sealed class KcpUserAccount : IUserKey
{
private ProtectedBinary m_pbKeyData = null;
/// <summary>
/// A user key depending on the currently logged on Windows user account.
/// </summary>
public sealed class KcpUserAccount : IUserKey
{
private ProtectedBinary m_pbKeyData = null;
// Constant initialization vector (unique for KeePass)
private static readonly byte[] m_pbEntropy = new byte[] {
0xDE, 0x13, 0x5B, 0x5F, 0x18, 0xA3, 0x46, 0x70,
0xB2, 0x57, 0x24, 0x29, 0x69, 0x88, 0x98, 0xE6
};
// Constant initialization vector (unique for KeePass)
private static readonly byte[] m_pbEntropy = new byte[] {
0xDE, 0x13, 0x5B, 0x5F, 0x18, 0xA3, 0x46, 0x70,
0xB2, 0x57, 0x24, 0x29, 0x69, 0x88, 0x98, 0xE6
};
private const string UserKeyFileName = "ProtectedUserKey.bin";
private const string UserKeyFileName = "ProtectedUserKey.bin";
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
public ProtectedBinary KeyData
{
get { return m_pbKeyData; }
}
/// <summary>
/// Get key data. Querying this property is fast (it returns a
/// reference to a cached <c>ProtectedBinary</c> object).
/// If no key data is available, <c>null</c> is returned.
/// </summary>
public ProtectedBinary KeyData
{
get { return m_pbKeyData; }
}
/// <summary>
/// Construct a user account key.
/// </summary>
public KcpUserAccount()
{
// Test if ProtectedData is supported -- throws an exception
// when running on an old system (Windows 98 / ME).
byte[] pbDummyData = new byte[128];
ProtectedData.Protect(pbDummyData, m_pbEntropy,
DataProtectionScope.CurrentUser);
/// <summary>
/// Construct a user account key.
/// </summary>
public KcpUserAccount()
{
// Test if ProtectedData is supported -- throws an exception
// when running on an old system (Windows 98 / ME).
byte[] pbDummyData = new byte[128];
ProtectedData.Protect(pbDummyData, m_pbEntropy,
DataProtectionScope.CurrentUser);
byte[] pbKey = LoadUserKey(false);
if(pbKey == null) pbKey = CreateUserKey();
if(pbKey == null) // Should never happen
{
Debug.Assert(false);
throw new SecurityException(KLRes.UserAccountKeyError);
}
byte[] pbKey = LoadUserKey(false);
if (pbKey == null) pbKey = CreateUserKey();
if (pbKey == null) // Should never happen
{
Debug.Assert(false);
throw new SecurityException(KLRes.UserAccountKeyError);
}
m_pbKeyData = new ProtectedBinary(true, pbKey);
MemUtil.ZeroByteArray(pbKey);
}
m_pbKeyData = new ProtectedBinary(true, pbKey);
MemUtil.ZeroByteArray(pbKey);
}
// public void Clear()
// {
// m_pbKeyData = null;
// }
// public void Clear()
// {
// m_pbKeyData = null;
// }
private static string GetUserKeyFilePath(bool bCreate)
{
private static string GetUserKeyFilePath(bool bCreate)
{
#if ModernKeePassLib
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
string strUserDir = Windows.Storage.ApplicationData.Current.RoamingFolder.Path;
#else
string strUserDir = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
#endif
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
strUserDir += PwDefs.ShortProductName;
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
strUserDir += PwDefs.ShortProductName;
#if !ModernKeePassLib
if(bCreate && !Directory.Exists(strUserDir))
Directory.CreateDirectory(strUserDir);
#endif
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
return (strUserDir + UserKeyFileName);
}
strUserDir = UrlUtil.EnsureTerminatingSeparator(strUserDir, false);
return (strUserDir + UserKeyFileName);
}
private static byte[] LoadUserKey(bool bThrow)
{
byte[] pbKey = null;
private static byte[] LoadUserKey(bool bThrow)
{
byte[] pbKey = null;
#if !KeePassLibSD
try
{
string strFilePath = GetUserKeyFilePath(false);
try
{
string strFilePath = GetUserKeyFilePath(false);
#if ModernKeePassLib
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForReadAsync().GetAwaiter().GetResult();
var pbProtectedKey = new byte[(int)fileStream.Length];
fileStream.Read(pbProtectedKey, 0, (int)fileStream.Length);
var pbProtectedKey = new byte[(int)fileStream.Length];
fileStream.Read(pbProtectedKey, 0, (int)fileStream.Length);
fileStream.Dispose();
#else
byte[] pbProtectedKey = File.ReadAllBytes(strFilePath);
#endif
pbKey = ProtectedData.Unprotect(pbProtectedKey, m_pbEntropy,
DataProtectionScope.CurrentUser);
}
catch(Exception)
{
if(bThrow) throw;
pbKey = null;
}
pbKey = ProtectedData.Unprotect(pbProtectedKey, m_pbEntropy,
DataProtectionScope.CurrentUser);
}
catch (Exception)
{
if (bThrow) throw;
pbKey = null;
}
#endif
return pbKey;
}
return pbKey;
}
private static byte[] CreateUserKey()
{
private static byte[] CreateUserKey()
{
#if KeePassLibSD
return null;
#else
string strFilePath = GetUserKeyFilePath(true);
string strFilePath = GetUserKeyFilePath(true);
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
byte[] pbProtectedKey = ProtectedData.Protect(pbRandomKey,
m_pbEntropy, DataProtectionScope.CurrentUser);
byte[] pbRandomKey = CryptoRandom.Instance.GetRandomBytes(64);
byte[] pbProtectedKey = ProtectedData.Protect(pbRandomKey,
m_pbEntropy, DataProtectionScope.CurrentUser);
#if ModernKeePassLib
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForWriteAsync().GetAwaiter().GetResult();
fileStream.Write(pbProtectedKey, 0, (int)fileStream.Length);
fileStream.Dispose();
var fileStream = StorageFile.GetFileFromPathAsync(strFilePath).GetAwaiter().GetResult().OpenStreamForWriteAsync().GetAwaiter().GetResult();
fileStream.Write(pbProtectedKey, 0, (int)fileStream.Length);
fileStream.Dispose();
#else
File.WriteAllBytes(strFilePath, pbProtectedKey);
#endif
byte[] pbKey = LoadUserKey(true);
Debug.Assert(MemUtil.ArraysEqual(pbKey, pbRandomKey));
byte[] pbKey = LoadUserKey(true);
Debug.Assert(MemUtil.ArraysEqual(pbKey, pbRandomKey));
MemUtil.ZeroByteArray(pbRandomKey);
return pbKey;
MemUtil.ZeroByteArray(pbRandomKey);
return pbKey;
#endif
}
}
}
}
}

View File

@@ -81,6 +81,7 @@
<Compile Include="Cryptography\PasswordGenerator\PwCharSet.cs" />
<Compile Include="Cryptography\PasswordGenerator\PwProfile.cs" />
<Compile Include="Cryptography\PopularPasswords.cs" />
<Compile Include="Cryptography\ProtectedData.cs" />
<Compile Include="Cryptography\QualityEstimation.cs" />
<Compile Include="Cryptography\SelfTest.cs" />
<Compile Include="Interfaces\IStructureItem.cs" />

View File

@@ -10,7 +10,7 @@
<projectUrl>https://github.com/wismna/ModernKeePass</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Portable KeePass Password Management Library that targets .Net Standard and WinRT. Allows reading, editing and writing to KeePass 2.x databases.</description>
<releaseNotes>Code cleanup</releaseNotes>
<releaseNotes></releaseNotes>
<copyright>Copyright © 2017 Geoffroy Bonneville</copyright>
<tags>KeePass KeePassLib Portable PCL NetStandard</tags>
<dependencies>

View File

@@ -54,25 +54,12 @@ namespace ModernKeePassLib.Native
}
}
internal enum DataProtectionScope
public enum DataProtectionScope
{
CurrentUser,
LocalMachine
}
internal static class ProtectedData
{
public static byte[] Protect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope)
{
throw new NotImplementedException();
}
public static byte[] Unprotect(byte[] userData, byte[] optionalEntropy, DataProtectionScope scope)
{
throw new NotImplementedException();
}
}
internal enum MemoryProtectionScope
{
CrossProcess,

View File

@@ -34,6 +34,7 @@ using System.Security.Cryptography;
#endif
using ModernKeePassLib.Collections;
using ModernKeePassLib.Cryptography;
using ModernKeePassLib.Cryptography.PasswordGenerator;
using ModernKeePassLib.Native;
using ModernKeePassLib.Security;