mirror of
https://github.com/wismna/ModernKeePass.git
synced 2025-10-04 08:00:16 -04:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4ae02fc07b | ||
![]() |
047fca32bf | ||
![]() |
abbff449c0 | ||
![]() |
fba668860b | ||
![]() |
acb196d9c2 | ||
![]() |
dfa3a21e6b | ||
![]() |
7ff6bccbc4 | ||
![]() |
88e5b80778 | ||
![]() |
d127431396 |
33
ModernKeePass/Actions/NavigateToUrlAction.cs
Normal file
33
ModernKeePass/Actions/NavigateToUrlAction.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)' < '12.0' ">
|
||||
<VisualStudioVersion>12.0</VisualStudioVersion>
|
||||
|
@@ -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.11.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>
|
||||
|
@@ -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.11.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.11.0.0")]
|
||||
[assembly: AssemblyVersion("1.12.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.12.0.0")]
|
||||
[assembly: ComVisible(false)]
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
@@ -96,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() {}
|
||||
@@ -115,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)
|
||||
|
@@ -94,6 +94,7 @@ namespace ModernKeePass.ViewModels
|
||||
{
|
||||
_database = database;
|
||||
Groups = _database?.RootGroup.Groups;
|
||||
Groups?.Insert(0, new GroupVm());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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="" 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="" 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}" />
|
||||
|
@@ -92,20 +92,7 @@ namespace ModernKeePass.Views
|
||||
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityRestoredTitle"), resource.GetResourceValue("EntryRestored"));
|
||||
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);
|
||||
|
@@ -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"/>
|
||||
@@ -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,23 +162,31 @@
|
||||
</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>
|
||||
<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">
|
||||
@@ -212,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>
|
||||
@@ -224,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>
|
||||
@@ -341,13 +379,18 @@
|
||||
<SymbolIcon Symbol="Find" />
|
||||
<Button.Flyout>
|
||||
<Flyout>
|
||||
<Flyout.FlyoutPresenterStyle>
|
||||
<Style TargetType="FlyoutPresenter">
|
||||
<Setter Property="Padding" Value="0" />
|
||||
</Style>
|
||||
</Flyout.FlyoutPresenterStyle>
|
||||
<!--<controls:TextBoxWithButton x:Uid="GroupFilter" ButtonSymbol="" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Width="400" Style="{StaticResource TextBoxWithButtonStyle}" IsButtonEnabled="False" />-->
|
||||
<SearchBox x:Uid="GroupSearch" Width="350" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" VerticalContentAlignment="Center" />
|
||||
<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="" 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" Width="350" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" FontSize="18" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" VerticalContentAlignment="Center" />
|
||||
<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">
|
||||
|
@@ -84,9 +84,6 @@ namespace ModernKeePass.Views
|
||||
{
|
||||
case -1:
|
||||
return;
|
||||
case 0:
|
||||
group = Model.AddNewGroup();
|
||||
break;
|
||||
default:
|
||||
group = LeftListView.SelectedItem as GroupVm;
|
||||
break;
|
||||
@@ -145,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)
|
||||
{
|
||||
@@ -168,7 +169,6 @@ namespace ModernKeePass.Views
|
||||
Frame.Navigate(typeof(EntryDetailPage), entry);
|
||||
}
|
||||
|
||||
|
||||
private void GroupDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true);
|
||||
|
@@ -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>
|
||||
|
@@ -1,2 +1,2 @@
|
||||
Error when opening file from Explorer corrected
|
||||
Filter entries reverted to search entries because of regressions
|
||||
New inline menu on Entries to quickly copy login, password, and navigate to URL
|
||||
Design changes
|
@@ -1,6 +1,6 @@
|
||||
En avez-vous assez d'essayer de retenir des quantités de mots de passe ? Etes-vous soucieux que le fait d'utiliser le même mot de passe partout vous rend vulnérable ?
|
||||
ModernKeePass est un gestionnaire de mots de passe gratuit, libre, facile à utiliser mais néanmoins s<EFBFBD>r, basé sur la technologie certifiée et répandue KeePass 2.x et compatible avec celui-ci.
|
||||
Vous pouvez stocker ou générer vos mots de passe dans une base de données chiffrée, qui peut être placée n'importe o<EFBFBD> (ordinateur personnel/tablette, cloud, clé USB...)
|
||||
En avez-vous assez d'essayer de retenir des quantit&eacute;s de mots de passe ? Etes-vous soucieux que le fait d'utiliser le m&ecirc;me mot de passe partout vous rend vuln&eacute;rable ?
|
||||
ModernKeePass est un gestionnaire de mots de passe gratuit, libre, facile &agrave; utiliser mais n&eacute;anmoins s&ucirc;r, bas&eacute; sur la technologie certifi&eacute;e et r&eacute;pandue KeePass 2.x et compatible avec celui-ci.
|
||||
Vous pouvez stocker ou g&eacute;n&eacute;rer vos mots de passe dans une base de donn&eacute;es chiffr&eacute;e, qui peut &ecirc;tre plac&eacute;e n'importe o&ugrave; (ordinateur personnel/tablette, cloud, cl&eacute; USB...)
|
||||
|
||||
Bien que la sécurité informatique soit très importante, cette application essaie de rester simple à utiliser et à comprendre. Vos suggestions sont les bienvenues !
|
||||
Bien que la s&eacute;curit&eacute; informatique soit tr&egrave;s importante, cette application essaie de rester simple &agrave; utiliser et &agrave; comprendre. Vos suggestions sont les bienvenues !
|
||||
Fonctionne sur Windows 10, 8.1 et RT.
|
@@ -1 +1 @@
|
||||
Page d'entr<EFBFBD>e avec g<>n<EFBFBD>rateur de mot de passe
|
||||
Page d'entr&eacute;e avec g&eacute;n&eacute;rateur de mot de passe
|
@@ -1 +1 @@
|
||||
Filtrez vos entr<EFBFBD>es pour rapidement trouver celle qui vous int<EFBFBD>resse
|
||||
Filtrez vos entr&eacute;es pour rapidement trouver celle qui vous int&eacute;resse
|
@@ -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&eacute;es
|
@@ -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&eacute;ez de nouvelles bases de donn&eacute;es, en d&eacute;finissant un mot de passe et/ou un fichier de cl&eacute; existant ou nouveau
|
@@ -1 +1 @@
|
||||
Ouvrez une base de donn<EFBFBD>es avec mot de passe et/ou fichier de cl<EFBFBD>
|
||||
Ouvrez une base de donn&eacute;es avec mot de passe et/ou fichier de cl&eacute;
|
@@ -1 +1 @@
|
||||
Ouvrez les fichiers r<EFBFBD>cents
|
||||
Ouvrez les fichiers r&eacute;cents
|
@@ -1 +1 @@
|
||||
Vue d'ensemble avec le zoom s<EFBFBD>mantique
|
||||
Vue d'ensemble avec le zoom s&eacute;mantique
|
@@ -1 +1 @@
|
||||
Param<EFBFBD>tres de l'application
|
||||
Param&egrave;tres de l'application
|
@@ -1,2 +1,2 @@
|
||||
Correction d'un bug lors de l'ouverture d'un fichier par Explorer
|
||||
Rétablissement de la recherche d'entrées à la place du filtre à cause de régressions multiples
|
||||
Ajout d'un menu int&eacute;gr&eacute; &agrave; la liste d'entr&eacute;es afin de copier rapidement le login, mot de passe et de naviguer vers l'URL
|
||||
Quelques changements de design
|
@@ -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);
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@ namespace ModernKeePassApp.Test.Mock
|
||||
|
||||
public void Save()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// Do Nothing
|
||||
}
|
||||
|
||||
public void Save(StorageFile file)
|
||||
|
31
ModernKeePassApp.Test/Mock/LicenseServiceMock.cs
Normal file
31
ModernKeePassApp.Test/Mock/LicenseServiceMock.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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>
|
||||
|
@@ -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"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
ModernKeePassLib/Cryptography/ProtectedData.cs
Normal file
18
ModernKeePassLib/Cryptography/ProtectedData.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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" />
|
||||
|
@@ -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>
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user