Add Windows CI and WPF management client - #140
Open
netoyou wants to merge 16 commits into
Open
Conversation
Add Tokens, Logs, AD/LDAP and Maintenance tabs and extend the Users tab: - Users: create (-fastcreate), activate, deactivate, lock, unlock, resync (dialog for two consecutive OTP codes), delete - Tokens: list, assign, remove, delete - Logs: show/clear log, error codes, version info - AD/LDAP: connection check, user list, user sync (min 30s timeout) - Maintenance: encrypted backup/restore, purge lock folder, purge LDAP cache Success now recognizes multiOTP INFO exit codes 11-19, and the exit code table covers the documented 0-59 range. Destructive operations require confirmation; backup/restore passwords are never logged and are cleared after use.
The Users tab gains a QR Code button for the selected user: - -urllink retrieves the otpauth:// provisioning URL (validated before use; unsupported token types are reported instead of showing a QR) - -qrcode renders the QR PNG into a temp file, shown in a dialog with the copyable URL; the temp file embeds the token secret and is deleted as soon as the image is loaded in memory - If image generation fails (e.g. missing GD support), the dialog falls back to the provisioning URL alone
The Create button used -fastcreate, which generates a random 4-digit prefix PIN and enables the prefix-PIN requirement. The PIN is never shown in the GUI, so users who typed only the 6-digit OTP from their authenticator app got exit code 98 (wrong token length) on verify. Switch the default Create to -fastcreatenopin so verification works with just the OTP, matching the Google Authenticator workflow. Also extend the friendly exit-code table to cover codes 60-99 (SMS/email, server, AD/LDAP, push, and authentication errors), so future failures display meaningful text instead of a bare code.
multiOTP Credential Provider only triggers the OTP challenge after a Windows account has logged in, so creating a multiOTP entry for a username that does not exist locally (or in the joined domain) leaves the user with no 2FA coverage. Before -fastcreatenopin runs, the GUI now probes local SAM via System.DirectoryServices.AccountManagement and, if the machine is joined to a domain, also the domain. When the Windows account is missing, a confirmation dialog (default No) explains the situation; if the user overrides, the new entry is created and the status line records the warning.
Users created with -fastcreate carry request_prefix_pin=1, so they still expect the PIN+OTP concatenation even after the default Create button moved to -fastcreatenopin. The Disable PIN button runs '-set <user> request_prefix_pin=0' on the selected user so admins can switch any account to OTP-only verification without touching the CLI.
The user-info dump (Activated, AD/LDAP synchronized, attributes, etc.) was getting cramped on typical Win7 screens with the default 720px window. Three small layout tweaks: - Window default height 720 -> 820, MinHeight 580 -> 640 - UserDetailBox MinHeight=240 so it always shows a useful chunk even when the field grid above is large - Inner Panel padding 14 -> 10 across all tabs to reclaim a few pixels in every panel
- Add per-key Resources.resx (English) and Resources.zh-Hans.resx
(Simplified Chinese), with hand-generated Resources.Designer.cs so
XAML can reference entries via {x:Static p:Resources.Key} and
code-behind via Resources.Key.
- Move every hardcoded user-visible string out of MainWindow.xaml,
MainWindow.xaml.cs, QrCodeDialog and PromptDialog into resources,
including 70+ multiOTP exit-code descriptions and all
confirmation/status messages.
- Add Core/AppSettings.cs to persist user preferences as JSON in
%LocalAppData%\MultiOtpManager\settings.json (hand-rolled parser
to avoid the System.Text.Json reference assembly dependency on
.NET Framework 4.5.2).
- App.OnStartup reads the saved language and sets the UI culture
before MainWindow is constructed; on first run it guesses the
best built-in language from the system UI culture and persists
the choice.
- Add a Language ComboBox to the top bar with three entries
(System default, English, 简体中文). Changing it saves the
preference and offers to restart the application so the new
UI culture takes effect.
Replacing the ItemsSource in LoadUsersAsync was wiping the user's selection whenever the Users tab auto-refreshed on activation, which made Deactivate/Disable PIN/Delete etc. silently fall through the 'SelectUserFirst' branch. - Drop the refreshingUsers flag; it was both racing with the user click and stomping on the SelectedItem reset. - Snapshot the selected user's name before the CLI call and restore it by name on the freshly built list. Missing users fall through to ClearUserDetail as expected. - Detach SelectedItem right before swapping ItemsSource so WPF does not briefly raise a SelectionChanged against a stale item.
Auto-refreshing the Users list on every tab activation was producing a runaway loop in practice: the SelectionChanged raised during the ItemsSource swap fired LoadUserDetailsAsync again, which could trigger additional tab/layout invalidations and queue more refreshes, all forking multiotp CLI child processes. This change: - Adds a per-refresh CancellationTokenSource and a monotonically increasing request id. New refreshes cancel the previous one and the stale tasks bail out before mutating ItemsSource. - Suppresses UsersListBox.SelectionChanged while the refresh is re-binding ItemsSource so the temporary null selection does not trigger another detail load. - Only reacts to MainTabs.SelectionChanged when the Users tab is actually being added to the selection, instead of on every selection-change inside the TabControl. - Reloads the detail panel for the (restored) selection once the refresh completes, guarded by the request id so a newer selection is never overwritten by stale data.
Places the most-used Users tab first; Authentication becomes the second tab. Tab x:Names and the MainTabs_SelectionChanged handler match by reference, so reordering is transparent to the code-behind.
Hover-over explanations for the controls that need operator interpretation rather than restating their label: - Authentication: TIMEOUT (SECONDS) and the -verify checkbox. - Users: Disable PIN, Resync, QR code, and a note that the QR image is deleted from disk right after the dialog loads it. - Tokens: refresher, list, Assign/Remove/Delete semantics. - Credential Provider: LOGON/UNLOCK scope, the multiOTP-only exclusivity choice, and the two-step / single-step OTP field layout. - Maintenance: backup/restore scope, lock-folder and LDAP-cache purges. - AD/LDAP: explicit reminder that Sync is a write operation. - Logs: warning on ClearLog, plus the dictionary/version buttons. Strings live in Resources.resx / Resources.zh-Hans.resx so the existing language switcher picks them up automatically.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Testing