Skip to content

Commit 3bcacd1

Browse files
committed
Merge branch '_workshop1' into _workshop0
# Conflicts: # .gitignore # Core/Models/Account.cs # Core/Models/Activity.cs # Core/Models/AutoSave.cs # Core/Models/Change.cs # Core/Models/Database.cs # Core/Models/Service.cs # Core/Models/User.cs # Core/Models/Warning.cs # Core/Upsilon.Apps.Passkey.Core.csproj # Core/Utils/ActivityCenter.cs # Core/Utils/CryptographyCenter.cs # Core/Utils/FileLocker.cs # Core/Utils/ImportExportHelper.cs # Core/Utils/JsonSerializationCenter.cs # Core/Utils/PasswordFactory.cs # Core/Utils/QrCode.cs # Core/Utils/StaticMethods.cs # GUI/WPF/App.xaml # GUI/WPF/App.xaml.cs # GUI/WPF/AssemblyInfo.cs # GUI/WPF/Helper/EnumHelper.cs # GUI/WPF/Helper/HotKeyHelper.cs # GUI/WPF/Helper/IItemHelper.cs # GUI/WPF/Helper/NumericTextBoxHelper.cs # GUI/WPF/Helper/PropertyHelper.cs # GUI/WPF/Helper/WindowHelper.cs # GUI/WPF/MainWindow.xaml # GUI/WPF/MainWindow.xaml.cs # GUI/WPF/OSSpecific/ClipboardManager.cs # GUI/WPF/Properties/PublishProfiles/FolderProfile.pubxml # GUI/WPF/Properties/launchSettings.json # GUI/WPF/Themes/DarkMode.cs # GUI/WPF/Themes/DarkTheme.xaml # GUI/WPF/Upsilon.Apps.Passkey.GUI.WPF.csproj # GUI/WPF/ViewModels/AccountPasswordsWarningViewModel.cs # GUI/WPF/ViewModels/Controls/AccountPasswordWarningViewModel.cs # GUI/WPF/ViewModels/Controls/AccountViewModel.cs # GUI/WPF/ViewModels/Controls/ActivityViewModel.cs # GUI/WPF/ViewModels/Controls/DuplicatedPasswordWarningViewModel.cs # GUI/WPF/ViewModels/Controls/IdentifiantViewModel.cs # GUI/WPF/ViewModels/Controls/PasswordViewModel.cs # GUI/WPF/ViewModels/Controls/ServiceViewModel.cs # GUI/WPF/ViewModels/Controls/UserPasswordItemViewModel.cs # GUI/WPF/ViewModels/Controls/VisiblePasswordBoxViewModel.cs # GUI/WPF/ViewModels/DuplicatedPasswordsWarningViewModel.cs # GUI/WPF/ViewModels/InsertIdentifierViewModel.cs # GUI/WPF/ViewModels/MainViewModel.cs # GUI/WPF/ViewModels/PasswordGeneratorViewModel.cs # GUI/WPF/ViewModels/UserActivitiesViewModel.cs # GUI/WPF/ViewModels/UserServicesViewModel.cs # GUI/WPF/ViewModels/UserSettingsViewModel.cs # GUI/WPF/Views/AccountPasswordsWarningView.xaml # GUI/WPF/Views/AccountPasswordsWarningView.xaml.cs # GUI/WPF/Views/Controls/AccountView.xaml # GUI/WPF/Views/Controls/AccountView.xaml.cs # GUI/WPF/Views/Controls/ServiceView.xaml # GUI/WPF/Views/Controls/ServiceView.xaml.cs # GUI/WPF/Views/Controls/UserPasswordItem.xaml # GUI/WPF/Views/Controls/UserPasswordItem.xaml.cs # GUI/WPF/Views/Controls/UserPasswordsContainer.xaml # GUI/WPF/Views/Controls/UserPasswordsContainer.xaml.cs # GUI/WPF/Views/Controls/VisiblePasswordBox.xaml # GUI/WPF/Views/Controls/VisiblePasswordBox.xaml.cs # GUI/WPF/Views/DuplicatedPasswordsWarningView.xaml # GUI/WPF/Views/DuplicatedPasswordsWarningView.xaml.cs # GUI/WPF/Views/InsertIdentifierView.xaml # GUI/WPF/Views/InsertIdentifierView.xaml.cs # GUI/WPF/Views/PasswordGeneratorView.xaml # GUI/WPF/Views/PasswordGeneratorView.xaml.cs # GUI/WPF/Views/QrCodeView.xaml # GUI/WPF/Views/QrCodeView.xaml.cs # GUI/WPF/Views/UserActivitiesView.xaml # GUI/WPF/Views/UserActivitiesView.xaml.cs # GUI/WPF/Views/UserServicesView.xaml # GUI/WPF/Views/UserServicesView.xaml.cs # GUI/WPF/Views/UserSettingsView.xaml # GUI/WPF/Views/UserSettingsView.xaml.cs # Interfaces/Enums/AccountOption.cs # Interfaces/Enums/ActivityEventType.cs # Interfaces/Enums/AutoSaveMergeBehavior.cs # Interfaces/Enums/WarningType.cs # Interfaces/Events/AutoSaveDetectedEventArgs.cs # Interfaces/Events/LogoutEventArgs.cs # Interfaces/Events/WarningsUpdatedEventArgs.cs # Interfaces/Models/IAccount.cs # Interfaces/Models/IActivity.cs # Interfaces/Models/IDatabase.cs # Interfaces/Models/IItem.cs # Interfaces/Models/IService.cs # Interfaces/Models/IUser.cs # Interfaces/Models/IWarning.cs # Interfaces/Upsilon.Apps.Passkey.Interfaces.csproj # Interfaces/Utils/Exceptions.cs # Interfaces/Utils/IClipboardManager.cs # Interfaces/Utils/ICryptographyCenter.cs # Interfaces/Utils/IPasswordFactory.cs # Interfaces/Utils/ISerializationCenter.cs # Interfaces/Utils/StaticMethods.cs # Upsilon.Apps.Passkey.Windows.slnx
2 parents 0384b43 + 857f351 commit 3bcacd1

53 files changed

Lines changed: 1666 additions & 532 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
root = true
2+
3+
[*.cs]
4+
indent_style = space
5+
indent_size = 3
6+
end_of_line = crlf
7+
insert_final_newline = true
8+
charset = utf-8-bom
9+
trim_trailing_whitespace = true
10+
11+
# Security/correctness rules are kept as errors.
12+
# The rules below are downgraded because they would require sweeping
13+
# rewrites unrelated to the current focus (architecture, security and
14+
# performance). They can be tightened back to errors progressively.
15+
16+
# Style/design suggestions: keep visible (suggestion) but not breaking.
17+
dotnet_diagnostic.CA1003.severity = suggestion # EventHandler<T> with EventArgs
18+
dotnet_diagnostic.CA1008.severity = suggestion # Enums should have zero value
19+
dotnet_diagnostic.CA1024.severity = suggestion # Use properties where appropriate
20+
dotnet_diagnostic.CA1032.severity = suggestion # Implement standard exception ctors
21+
dotnet_diagnostic.CA1056.severity = suggestion # Uri properties should not be strings
22+
dotnet_diagnostic.CA1030.severity = suggestion # Make method an event
23+
dotnet_diagnostic.CA1031.severity = suggestion # Do not catch general Exception
24+
dotnet_diagnostic.CA1034.severity = suggestion # Nested types should not be visible
25+
dotnet_diagnostic.CA1051.severity = suggestion # Do not declare visible instance fields
26+
dotnet_diagnostic.CA1062.severity = suggestion # Validate arguments of public methods
27+
dotnet_diagnostic.CA1303.severity = none # Localised string literal
28+
dotnet_diagnostic.CA1304.severity = suggestion # ToLower without culture
29+
dotnet_diagnostic.CA1305.severity = suggestion # IFormatProvider missing
30+
dotnet_diagnostic.CA1307.severity = suggestion # StringComparison missing
31+
dotnet_diagnostic.CA1308.severity = suggestion # ToLowerInvariant vs ToUpperInvariant
32+
dotnet_diagnostic.CA1310.severity = suggestion # StartsWith without StringComparison
33+
dotnet_diagnostic.CA1311.severity = suggestion # Specify culture for ToLower/ToUpper
34+
dotnet_diagnostic.CA1707.severity = suggestion # Underscore in identifiers
35+
dotnet_diagnostic.CA1716.severity = suggestion # Reserved language identifier
36+
dotnet_diagnostic.CA1805.severity = suggestion # Default value initialization
37+
dotnet_diagnostic.CA1810.severity = suggestion # Init static fields inline
38+
dotnet_diagnostic.CA1812.severity = suggestion # Avoid uninstantiated internal class
39+
dotnet_diagnostic.CA1815.severity = suggestion # Override Equals on value types
40+
dotnet_diagnostic.CA1819.severity = suggestion # Properties should not return arrays
41+
dotnet_diagnostic.CA1822.severity = suggestion # Mark members static
42+
dotnet_diagnostic.CA1859.severity = suggestion # Use concrete types for performance
43+
dotnet_diagnostic.CA1862.severity = suggestion # StringComparison for Contains
44+
dotnet_diagnostic.CA2227.severity = suggestion # Collection properties should be read-only
45+
46+
# In an executable, internal types are encouraged but the existing
47+
# code base exposes most types publicly. Lower CA1515 to suggestion
48+
# to surface it without breaking the build.
49+
dotnet_diagnostic.CA1515.severity = suggestion
50+
51+
# CA1815 / CA1851 noisy across the codebase; keep as suggestion.
52+
dotnet_diagnostic.CA1851.severity = suggestion
53+
54+
# Security: keep dispose ownership warning visible without breaking.
55+
# Database lifecycle is managed by ISessionService.
56+
dotnet_diagnostic.CA2000.severity = suggestion
57+
58+
# CA5392 (DefaultDllImportSearchPaths): keep as suggestion; the
59+
# P/Invokes target well-known system DLLs (user32, dwmapi).
60+
dotnet_diagnostic.CA5392.severity = suggestion
61+
62+
# The Core/QrCode logic uses multidimensional arrays for matrix algebra.
63+
# Rewriting to jagged arrays would degrade readability without any gain.
64+
dotnet_diagnostic.CA1814.severity = suggestion
65+
66+
# CA1852 (seal internal types): worthwhile but currently noisy in Core.
67+
dotnet_diagnostic.CA1852.severity = suggestion
68+
69+
# CA2201 (reserved exception types): the codebase intentionally throws
70+
# NullReferenceException in some places; flag as suggestion until those
71+
# sites can be reviewed individually.
72+
dotnet_diagnostic.CA2201.severity = suggestion
73+
74+
# CA1001 (type owns disposable field but is not IDisposable): User holds
75+
# a DispatcherTimer; the existing lifetime is tied to Database.Dispose.
76+
# Surfaced as suggestion for a follow-up.
77+
dotnet_diagnostic.CA1001.severity = suggestion
78+
79+
# Cryptography rules: the existing on-disk format relies on MD5/SHA1/
80+
# legacy IV derivation. Changing the algorithms would silently break
81+
# every existing user database, so they are kept as suggestion and the
82+
# follow-up will introduce versioned migrations.
83+
dotnet_diagnostic.CA5350.severity = suggestion
84+
dotnet_diagnostic.CA5351.severity = suggestion
85+
dotnet_diagnostic.CA5394.severity = suggestion
86+
dotnet_diagnostic.CA5401.severity = suggestion
87+
88+
# Visual Studio Editor-only style rules; keep silent when not relevant.
89+
dotnet_diagnostic.IDE0001.severity = suggestion
90+
dotnet_diagnostic.IDE0008.severity = suggestion
91+
dotnet_diagnostic.IDE0028.severity = suggestion
92+
dotnet_diagnostic.IDE0046.severity = suggestion
93+
dotnet_diagnostic.IDE0047.severity = suggestion
94+
dotnet_diagnostic.IDE0048.severity = suggestion
95+
dotnet_diagnostic.IDE0058.severity = suggestion
96+
dotnet_diagnostic.IDE0072.severity = suggestion
97+
dotnet_diagnostic.IDE0290.severity = suggestion

Core/Upsilon.Apps.Passkey.Core.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
<Description>A local stored Password Manager Core.</Description>
1010
<AssemblyVersion>1.0.1</AssemblyVersion>
1111
<FileVersion>1.0.1</FileVersion>
12+
<EnableNETAnalyzers>true</EnableNETAnalyzers>
13+
<AnalysisLevel>latest-all</AnalysisLevel>
1214
</PropertyGroup>
1315

1416
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

Core/Utils/CryptographyCenter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ namespace Upsilon.Apps.Passkey.Core.Utils
77
{
88
public class CryptographyCenter : ICryptographyCenter
99
{
10-
public string GetHash(string source) => Convert.ToBase64String(SHA512.HashData(Encoding.Unicode.GetBytes(source))).Replace("/", "-");
10+
public string GetHash(string source)
11+
{
12+
string md5Hash = Convert.ToBase64String(MD5.HashData(Encoding.Unicode.GetBytes(source)));
13+
string sha1Hash = Convert.ToBase64String(SHA1.HashData(Encoding.Unicode.GetBytes(source)));
14+
15+
return (md5Hash + sha1Hash).Replace("/", "-");
16+
}
1117

1218
public string GetSlowHash(string source)
1319
{

GUI/WPF/App.xaml.cs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Windows;
2+
using System.Windows.Threading;
3+
using Upsilon.Apps.Passkey.GUI.WPF.Helper;
24

35
namespace Upsilon.Apps.Passkey.GUI.WPF
46
{
@@ -7,6 +9,51 @@ namespace Upsilon.Apps.Passkey.GUI.WPF
79
/// </summary>
810
public partial class App : Application
911
{
10-
}
12+
protected override void OnStartup(StartupEventArgs e)
13+
{
14+
DispatcherUnhandledException += _onDispatcherUnhandledException;
15+
AppDomain.CurrentDomain.UnhandledException += _onAppDomainUnhandledException;
16+
TaskScheduler.UnobservedTaskException += _onUnobservedTaskException;
17+
18+
Log.Info($"Application starting (PID {Environment.ProcessId}).");
19+
20+
base.OnStartup(e);
21+
}
22+
23+
protected override void OnExit(ExitEventArgs e)
24+
{
25+
Log.Info($"Application exiting with code {e.ApplicationExitCode}.");
26+
Log.Flush();
27+
28+
base.OnExit(e);
29+
}
1130

31+
private static void _onDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
32+
{
33+
Log.Error(e.Exception, "Unhandled UI exception");
34+
// The application keeps running; the caller has already shown any
35+
// feedback it needed. Marking as handled prevents the default crash.
36+
e.Handled = true;
37+
}
38+
39+
private static void _onAppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
40+
{
41+
if (e.ExceptionObject is Exception exception)
42+
{
43+
Log.Error(exception, $"Unhandled AppDomain exception (terminating: {e.IsTerminating})");
44+
}
45+
else
46+
{
47+
Log.Error($"Unhandled non-CLR AppDomain exception (terminating: {e.IsTerminating}).");
48+
}
49+
50+
Log.Flush();
51+
}
52+
53+
private static void _onUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
54+
{
55+
Log.Error(e.Exception, "Unobserved task exception");
56+
e.SetObserved();
57+
}
58+
}
1259
}

GUI/WPF/Helper/AppInfo.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Reflection;
2+
3+
namespace Upsilon.Apps.Passkey.GUI.WPF.Helper
4+
{
5+
/// <summary>
6+
/// Read-only metadata about the running assembly. Centralises the title shown
7+
/// in window headers so it is no longer scattered across view-models.
8+
/// </summary>
9+
public static class AppInfo
10+
{
11+
private static readonly Lazy<string> _title = new(_buildTitle);
12+
13+
public static string Title => _title.Value;
14+
15+
private static string _buildTitle()
16+
{
17+
AssemblyName name = Assembly.GetExecutingAssembly().GetName();
18+
string version = name.Version?.ToString(3) ?? "0.0.0";
19+
return $"{name.Name} v{version}";
20+
}
21+
}
22+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System.Windows.Input;
2+
3+
namespace Upsilon.Apps.Passkey.GUI.WPF.Helper
4+
{
5+
/// <summary>
6+
/// Asynchronous variant of <see cref="RelayCommand"/>. The command reports
7+
/// <see cref="CanExecute"/> as <c>false</c> while the previous execution is
8+
/// still pending, preventing re-entrancy.
9+
/// </summary>
10+
public sealed class AsyncRelayCommand : ICommand
11+
{
12+
private readonly Func<object?, Task> _execute;
13+
private readonly Predicate<object?>? _canExecute;
14+
private bool _isRunning;
15+
16+
public AsyncRelayCommand(Func<Task> execute, Func<bool>? canExecute = null)
17+
: this(_ => execute(), canExecute is null ? null : _ => canExecute())
18+
{
19+
ArgumentNullException.ThrowIfNull(execute);
20+
}
21+
22+
public AsyncRelayCommand(Func<object?, Task> execute, Predicate<object?>? canExecute = null)
23+
{
24+
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
25+
_canExecute = canExecute;
26+
}
27+
28+
public event EventHandler? CanExecuteChanged
29+
{
30+
add => CommandManager.RequerySuggested += value;
31+
remove => CommandManager.RequerySuggested -= value;
32+
}
33+
34+
public bool IsRunning => _isRunning;
35+
36+
public bool CanExecute(object? parameter) => !_isRunning && (_canExecute?.Invoke(parameter) ?? true);
37+
38+
public async void Execute(object? parameter)
39+
{
40+
if (!CanExecute(parameter))
41+
{
42+
return;
43+
}
44+
45+
_isRunning = true;
46+
CommandManager.InvalidateRequerySuggested();
47+
48+
try
49+
{
50+
await _execute(parameter).ConfigureAwait(true);
51+
}
52+
finally
53+
{
54+
_isRunning = false;
55+
CommandManager.InvalidateRequerySuggested();
56+
}
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)