94d5a864e0
Add SecureStore integration to ConfigManager for secure handling of connection strings and sensitive configuration values. Includes store/secret management UI, encrypted .store file support, and comprehensive test coverage.
514 lines
16 KiB
Markdown
514 lines
16 KiB
Markdown
# SecureStoreManager Integration into ConfigManager
|
|
|
|
**Date:** 2026-01-20
|
|
**Status:** Ready for Implementation
|
|
**Estimated Phases:** 9
|
|
|
|
## Overview
|
|
|
|
Merge the complete functionality of SecureStoreManager into ConfigManager, creating a unified configuration and secrets management application. This integration adds encrypted secret store management to the existing configuration editing capabilities.
|
|
|
|
## Design Decisions
|
|
|
|
| Decision | Choice | Rationale |
|
|
|----------|--------|-----------|
|
|
| Integration model | Tree node with explicit auth | Security: secrets require conscious authentication |
|
|
| Store discovery | Hybrid (auto-discover + manual add) | Convenience for co-located stores, flexibility for external |
|
|
| Credential caching | Prompt every time (session cache opt-in) | Default to most secure behavior |
|
|
| Save behavior | Separate saves (config vs stores) | Different security domains shouldn't couple |
|
|
| Service architecture | Use cases layer | Matches existing pattern, separation of concerns |
|
|
|
|
## Architecture
|
|
|
|
### Tree Structure
|
|
```
|
|
📁 Configuration
|
|
├── DataSync
|
|
├── DataAccess
|
|
├── Auth
|
|
├── LDAP
|
|
├── Search
|
|
└── Excel Export
|
|
📁 Pipelines
|
|
└── (pipeline nodes)
|
|
🛡️ Secure Stores
|
|
├── 🔒 production.secrets (locked)
|
|
└── 🔓 development.secrets (unlocked)
|
|
├── 🔑 ConnectionStrings:JDE
|
|
└── 🔑 LdapPassword
|
|
```
|
|
|
|
### Service Layer
|
|
```
|
|
MainWindowViewModel
|
|
├── IConfigFileService (existing - config operations)
|
|
├── IStoreUseCases (new - secure store operations)
|
|
└── ISecretUseCases (new - secret operations)
|
|
└── ISecureStoreManager (new - low-level encryption)
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 1: Project Setup & Dependencies
|
|
|
|
### 1.1 Add SecureStore NuGet Package
|
|
**File:** `NEW/src/Utils/JdeScoping.ConfigManager/JdeScoping.ConfigManager.csproj`
|
|
|
|
Add:
|
|
```xml
|
|
<PackageReference Include="SecureStore" Version="1.2.0" />
|
|
```
|
|
|
|
### 1.2 Add Avalonia.Headless.XUnit to Test Project
|
|
**File:** `NEW/tests/JdeScoping.ConfigManager.Tests/JdeScoping.ConfigManager.Tests.csproj`
|
|
|
|
Add:
|
|
```xml
|
|
<PackageReference Include="Avalonia.Headless.XUnit" Version="11.2.*" />
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 2: Copy Service Layer
|
|
|
|
### 2.1 Copy Core Service Files
|
|
Copy from `SecureStoreManager/Services/` to `ConfigManager/Services/SecureStore/`:
|
|
|
|
| Source | Destination |
|
|
|--------|-------------|
|
|
| `ISecureStoreManager.cs` | `Services/SecureStore/ISecureStoreManager.cs` |
|
|
| `SecureStoreManager.cs` | `Services/SecureStore/SecureStoreManager.cs` |
|
|
|
|
Update namespace: `JdeScoping.SecureStoreManager.Services` → `JdeScoping.ConfigManager.Services.SecureStore`
|
|
|
|
### 2.2 Copy Use Cases
|
|
Copy from `SecureStoreManager/Application/` to `ConfigManager/Application/`:
|
|
|
|
| Source | Destination |
|
|
|--------|-------------|
|
|
| `StoreUseCases.cs` | `Application/StoreUseCases.cs` |
|
|
| `SecretUseCases.cs` | `Application/SecretUseCases.cs` |
|
|
|
|
Update namespaces accordingly.
|
|
|
|
### 2.3 Copy Constants
|
|
Copy from `SecureStoreManager/Constants/` to `ConfigManager/Constants/`:
|
|
|
|
| Source | Destination |
|
|
|--------|-------------|
|
|
| `DialogStrings.cs` | `Constants/SecureStoreStrings.cs` (rename to avoid conflicts) |
|
|
| `FileExtensions.cs` | `Constants/SecureStoreFileExtensions.cs` |
|
|
|
|
### 2.4 Copy Clipboard Service
|
|
Copy from `SecureStoreManager/Services/` to `ConfigManager/Services/`:
|
|
|
|
| Source | Destination |
|
|
|--------|-------------|
|
|
| `IClipboardService.cs` | `Services/IClipboardService.cs` |
|
|
| `AvaloniaClipboardService.cs` | `Services/AvaloniaClipboardService.cs` |
|
|
|
|
---
|
|
|
|
## Phase 3: Extend Tree Node Model
|
|
|
|
### 3.1 Update TreeNodeType Enum
|
|
**File:** `ViewModels/TreeNodeViewModel.cs`
|
|
|
|
```csharp
|
|
public enum TreeNodeType
|
|
{
|
|
Folder,
|
|
SettingsSection,
|
|
Pipeline,
|
|
SecureStoresFolder, // New
|
|
SecureStore, // New
|
|
Secret // New
|
|
}
|
|
```
|
|
|
|
### 3.2 Add SecureStore-Specific Properties
|
|
**File:** `ViewModels/TreeNodeViewModel.cs`
|
|
|
|
Add to `TreeNodeViewModel`:
|
|
```csharp
|
|
/// <summary>
|
|
/// Gets or sets whether this secure store is currently unlocked.
|
|
/// Only applicable for SecureStore node types.
|
|
/// </summary>
|
|
public bool IsUnlocked
|
|
{
|
|
get => _isUnlocked;
|
|
set
|
|
{
|
|
if (SetProperty(ref _isUnlocked, value))
|
|
{
|
|
OnPropertyChanged(nameof(LockIcon));
|
|
OnPropertyChanged(nameof(IsLocked));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets whether this secure store is locked.
|
|
/// </summary>
|
|
public bool IsLocked => !IsUnlocked;
|
|
|
|
/// <summary>
|
|
/// Gets the lock icon for secure store nodes.
|
|
/// </summary>
|
|
public string LockIcon => IsUnlocked ? "🔓" : "🔒";
|
|
|
|
/// <summary>
|
|
/// Gets or sets the full path to the secure store file.
|
|
/// Only applicable for SecureStore node types.
|
|
/// </summary>
|
|
public string? StorePath { get; init; }
|
|
```
|
|
|
|
### 3.3 Update Icon Property Logic
|
|
Update the `Icon` property or add computed property for dynamic icons based on node type and lock state.
|
|
|
|
---
|
|
|
|
## Phase 4: Create New ViewModels
|
|
|
|
### 4.1 SecureStoreFormViewModel (Locked State)
|
|
**File:** `ViewModels/Forms/SecureStoreLockedFormViewModel.cs`
|
|
|
|
Displays when a locked store is selected:
|
|
- Store name and path
|
|
- "This store is locked" message
|
|
- "Unlock Store..." button
|
|
- Last modified date (from file system)
|
|
|
|
### 4.2 SecureStoreFormViewModel (Unlocked State)
|
|
**File:** `ViewModels/Forms/SecureStoreUnlockedFormViewModel.cs`
|
|
|
|
Displays when an unlocked store is selected:
|
|
- Store name and path
|
|
- Secret count
|
|
- "Lock Store" button
|
|
- "Add Secret" button
|
|
- List of secrets (or indicate to select a secret from tree)
|
|
|
|
### 4.3 SecretFormViewModel
|
|
**File:** `ViewModels/Forms/SecretFormViewModel.cs`
|
|
|
|
Displays when a secret is selected:
|
|
- Key (read-only)
|
|
- Value (password-masked by default)
|
|
- Show/Hide toggle button
|
|
- Copy to Clipboard button
|
|
- Delete button
|
|
|
|
Properties:
|
|
```csharp
|
|
public string Key { get; }
|
|
public string Value { get; set; }
|
|
public bool IsValueVisible { get; set; }
|
|
public string DisplayValue => IsValueVisible ? Value : new string('*', 8);
|
|
public ICommand ToggleVisibilityCommand { get; }
|
|
public ICommand CopyToClipboardCommand { get; }
|
|
```
|
|
|
|
### 4.4 Dialog ViewModels
|
|
Copy and adapt from SecureStoreManager:
|
|
|
|
| Source | Destination |
|
|
|--------|-------------|
|
|
| `NewStoreDialogViewModel.cs` | `ViewModels/Dialogs/NewStoreDialogViewModel.cs` |
|
|
| `OpenStoreDialogViewModel.cs` | `ViewModels/Dialogs/UnlockStoreDialogViewModel.cs` (rename) |
|
|
| `SecretEditDialogViewModel.cs` | `ViewModels/Dialogs/SecretEditDialogViewModel.cs` |
|
|
|
|
---
|
|
|
|
## Phase 5: Create New Views
|
|
|
|
### 5.1 Form Views
|
|
**Directory:** `Views/Forms/`
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `SecureStoreLockedFormView.axaml` | Shows unlock button for locked stores |
|
|
| `SecureStoreUnlockedFormView.axaml` | Shows store info when unlocked |
|
|
| `SecretFormView.axaml` | Secret key/value editor with masking |
|
|
|
|
### 5.2 Dialog Views
|
|
**Directory:** `Views/Dialogs/`
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `NewStoreDialog.axaml` | Create new secure store |
|
|
| `UnlockStoreDialog.axaml` | Enter credentials to unlock |
|
|
| `SecretEditDialog.axaml` | Add/edit secret key-value |
|
|
|
|
### 5.3 Update DataTemplates
|
|
**File:** `App.axaml`
|
|
|
|
Add DataTemplates for automatic form selection:
|
|
```xml
|
|
<DataTemplate DataType="{x:Type vm:SecureStoreLockedFormViewModel}">
|
|
<views:SecureStoreLockedFormView />
|
|
</DataTemplate>
|
|
<DataTemplate DataType="{x:Type vm:SecureStoreUnlockedFormViewModel}">
|
|
<views:SecureStoreUnlockedFormView />
|
|
</DataTemplate>
|
|
<DataTemplate DataType="{x:Type vm:SecretFormViewModel}">
|
|
<views:SecretFormView />
|
|
</DataTemplate>
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 6: Update MainWindowViewModel
|
|
|
|
### 6.1 Add Dependencies
|
|
```csharp
|
|
private readonly IStoreUseCases _storeUseCases;
|
|
private readonly ISecretUseCases _secretUseCases;
|
|
private readonly IClipboardService _clipboardService;
|
|
```
|
|
|
|
### 6.2 Add Secure Store Commands
|
|
```csharp
|
|
// Store commands
|
|
public ICommand NewStoreCommand { get; }
|
|
public ICommand AddExistingStoreCommand { get; }
|
|
public ICommand UnlockStoreCommand { get; }
|
|
public ICommand LockStoreCommand { get; }
|
|
public ICommand SaveStoreCommand { get; }
|
|
public ICommand LockAllStoresCommand { get; }
|
|
public ICommand GenerateKeyFileCommand { get; }
|
|
|
|
// Secret commands
|
|
public ICommand AddSecretCommand { get; }
|
|
public ICommand EditSecretCommand { get; }
|
|
public ICommand DeleteSecretCommand { get; }
|
|
public ICommand CopySecretCommand { get; }
|
|
```
|
|
|
|
### 6.3 Add Store State Tracking
|
|
```csharp
|
|
// Track open stores (store path -> credentials for session caching if enabled)
|
|
private readonly Dictionary<string, SecureStoreState> _openStores = new();
|
|
|
|
private class SecureStoreState
|
|
{
|
|
public bool IsUnlocked { get; set; }
|
|
public TreeNodeViewModel TreeNode { get; set; }
|
|
}
|
|
```
|
|
|
|
### 6.4 Update BuildTreeNodes Method
|
|
Add logic to:
|
|
1. Create "Secure Stores" folder node
|
|
2. Discover `*.secrets.json` files in config folder
|
|
3. Create SecureStore nodes for each discovered file
|
|
4. Add manual store tracking
|
|
|
|
### 6.5 Update Form Selection Logic
|
|
Extend `OnSelectedNodeChanged` to handle:
|
|
- `SecureStoresFolder` → Show instructions/empty state
|
|
- `SecureStore` (locked) → Show `SecureStoreLockedFormViewModel`
|
|
- `SecureStore` (unlocked) → Show `SecureStoreUnlockedFormViewModel`
|
|
- `Secret` → Show `SecretFormViewModel`
|
|
|
|
---
|
|
|
|
## Phase 7: Update Menu Bar and Toolbar
|
|
|
|
### 7.1 Add Secure Stores Menu
|
|
**File:** `Views/MainWindow.axaml`
|
|
|
|
Add new menu between "Tools" and "Help":
|
|
```xml
|
|
<MenuItem Header="Secure Stores">
|
|
<MenuItem Header="New Store..." Command="{Binding NewStoreCommand}" InputGesture="Ctrl+Shift+N" />
|
|
<MenuItem Header="Add Existing Store..." Command="{Binding AddExistingStoreCommand}" InputGesture="Ctrl+Shift+O" />
|
|
<Separator />
|
|
<MenuItem Header="Save Store" Command="{Binding SaveStoreCommand}" InputGesture="Ctrl+Shift+S" />
|
|
<MenuItem Header="Lock All Stores" Command="{Binding LockAllStoresCommand}" />
|
|
<Separator />
|
|
<MenuItem Header="Generate Key File..." Command="{Binding GenerateKeyFileCommand}" />
|
|
</MenuItem>
|
|
```
|
|
|
|
### 7.2 Add Toolbar Buttons
|
|
Add after existing buttons with separator:
|
|
```xml
|
|
<Separator />
|
|
<Button Command="{Binding UnlockStoreCommand}" ToolTip.Tip="Unlock/Lock Store">
|
|
<TextBlock Text="🔓" />
|
|
</Button>
|
|
<Button Command="{Binding AddSecretCommand}" ToolTip.Tip="Add Secret">
|
|
<TextBlock Text="🔑+" />
|
|
</Button>
|
|
```
|
|
|
|
### 7.3 Add Context Menus
|
|
Add context menu to TreeView for right-click actions on nodes.
|
|
|
|
---
|
|
|
|
## Phase 8: Register Services
|
|
|
|
### 8.1 Update App.axaml.cs
|
|
**File:** `App.axaml.cs`
|
|
|
|
Add to `ConfigureServices`:
|
|
```csharp
|
|
// SecureStore Services
|
|
services.AddSingleton<ISecureStoreManager, SecureStoreManager>();
|
|
services.AddSingleton<IClipboardService>(sp =>
|
|
new AvaloniaClipboardService(GetMainWindow));
|
|
|
|
// SecureStore Use Cases
|
|
services.AddSingleton<IStoreUseCases, StoreUseCases>();
|
|
services.AddSingleton<ISecretUseCases, SecretUseCases>();
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 9: Migrate Tests
|
|
|
|
### 9.1 Copy Service Tests
|
|
Copy from `SecureStoreManager.Tests/Services/` to `ConfigManager.Tests/Services/SecureStore/`:
|
|
|
|
| Source | Destination |
|
|
|--------|-------------|
|
|
| `SecureStoreManagerTests.cs` | `Services/SecureStore/SecureStoreManagerTests.cs` |
|
|
|
|
Update namespaces.
|
|
|
|
### 9.2 Create New ViewModel Tests
|
|
**Directory:** `ConfigManager.Tests/ViewModels/Forms/`
|
|
|
|
| File | Tests |
|
|
|------|-------|
|
|
| `SecureStoreLockedFormViewModelTests.cs` | Locked state display, unlock command |
|
|
| `SecureStoreUnlockedFormViewModelTests.cs` | Unlocked state, secret list, lock command |
|
|
| `SecretFormViewModelTests.cs` | Value masking, visibility toggle, copy |
|
|
|
|
### 9.3 Create Dialog ViewModel Tests
|
|
**Directory:** `ConfigManager.Tests/ViewModels/Dialogs/`
|
|
|
|
| File | Tests |
|
|
|------|-------|
|
|
| `NewStoreDialogViewModelTests.cs` | Validation, path selection |
|
|
| `UnlockStoreDialogViewModelTests.cs` | Credential validation |
|
|
| `SecretEditDialogViewModelTests.cs` | Key/value validation |
|
|
|
|
### 9.4 Extend MainWindowViewModelTests
|
|
Add tests for:
|
|
- Secure store tree node creation
|
|
- Store unlock/lock workflow
|
|
- Secret CRUD operations
|
|
- Form selection for store/secret nodes
|
|
|
|
### 9.5 Extend TreeNodeViewModelTests
|
|
Add tests for:
|
|
- New node types (SecureStoresFolder, SecureStore, Secret)
|
|
- Lock state properties
|
|
- Icon computation for store nodes
|
|
|
|
### 9.6 Create UI Tests (Optional)
|
|
If UI testing is desired, copy patterns from `SecureStoreManager.Tests/Views/`:
|
|
- Test dialog visual structure
|
|
- Test form view elements
|
|
|
|
---
|
|
|
|
## File Inventory
|
|
|
|
### New Files to Create
|
|
|
|
**Services (5 files):**
|
|
- `Services/SecureStore/ISecureStoreManager.cs`
|
|
- `Services/SecureStore/SecureStoreManager.cs`
|
|
- `Services/IClipboardService.cs`
|
|
- `Services/AvaloniaClipboardService.cs`
|
|
|
|
**Application (2 files):**
|
|
- `Application/StoreUseCases.cs`
|
|
- `Application/SecretUseCases.cs`
|
|
|
|
**Constants (2 files):**
|
|
- `Constants/SecureStoreStrings.cs`
|
|
- `Constants/SecureStoreFileExtensions.cs`
|
|
|
|
**ViewModels (6 files):**
|
|
- `ViewModels/Forms/SecureStoreLockedFormViewModel.cs`
|
|
- `ViewModels/Forms/SecureStoreUnlockedFormViewModel.cs`
|
|
- `ViewModels/Forms/SecretFormViewModel.cs`
|
|
- `ViewModels/Dialogs/NewStoreDialogViewModel.cs`
|
|
- `ViewModels/Dialogs/UnlockStoreDialogViewModel.cs`
|
|
- `ViewModels/Dialogs/SecretEditDialogViewModel.cs`
|
|
|
|
**Views (6 files):**
|
|
- `Views/Forms/SecureStoreLockedFormView.axaml` + `.cs`
|
|
- `Views/Forms/SecureStoreUnlockedFormView.axaml` + `.cs`
|
|
- `Views/Forms/SecretFormView.axaml` + `.cs`
|
|
- `Views/Dialogs/NewStoreDialog.axaml` + `.cs`
|
|
- `Views/Dialogs/UnlockStoreDialog.axaml` + `.cs`
|
|
- `Views/Dialogs/SecretEditDialog.axaml` + `.cs`
|
|
|
|
**Tests (8+ files):**
|
|
- `Services/SecureStore/SecureStoreManagerTests.cs`
|
|
- `ViewModels/Forms/SecureStoreLockedFormViewModelTests.cs`
|
|
- `ViewModels/Forms/SecureStoreUnlockedFormViewModelTests.cs`
|
|
- `ViewModels/Forms/SecretFormViewModelTests.cs`
|
|
- `ViewModels/Dialogs/NewStoreDialogViewModelTests.cs`
|
|
- `ViewModels/Dialogs/UnlockStoreDialogViewModelTests.cs`
|
|
- `ViewModels/Dialogs/SecretEditDialogViewModelTests.cs`
|
|
|
|
### Files to Modify
|
|
|
|
- `JdeScoping.ConfigManager.csproj` (add SecureStore package)
|
|
- `App.axaml.cs` (register services)
|
|
- `App.axaml` (add DataTemplates)
|
|
- `ViewModels/TreeNodeViewModel.cs` (add enum values, properties)
|
|
- `ViewModels/MainWindowViewModel.cs` (add commands, store handling)
|
|
- `Views/MainWindow.axaml` (add menu, toolbar, context menu)
|
|
- `JdeScoping.ConfigManager.Tests.csproj` (add Avalonia.Headless.XUnit)
|
|
|
|
---
|
|
|
|
## Implementation Order
|
|
|
|
1. **Phase 1** - Project setup (dependencies)
|
|
2. **Phase 2** - Copy service layer (foundation)
|
|
3. **Phase 3** - Extend tree model (data structure)
|
|
4. **Phase 4** - Create ViewModels (business logic)
|
|
5. **Phase 5** - Create Views (UI)
|
|
6. **Phase 6** - Update MainWindowViewModel (orchestration)
|
|
7. **Phase 7** - Update menus/toolbar (discoverability)
|
|
8. **Phase 8** - Register services (wiring)
|
|
9. **Phase 9** - Migrate tests (verification)
|
|
|
|
---
|
|
|
|
## Verification Checklist
|
|
|
|
After implementation, verify:
|
|
|
|
- [ ] Can create new secure store with key file
|
|
- [ ] Can create new secure store with password
|
|
- [ ] Can add existing store to tree
|
|
- [ ] Stores appear locked by default
|
|
- [ ] Double-click unlocks store (shows dialog)
|
|
- [ ] Unlock button in form works
|
|
- [ ] Unlocked store shows secrets in tree
|
|
- [ ] Can add new secret
|
|
- [ ] Can edit existing secret
|
|
- [ ] Can delete secret
|
|
- [ ] Secret value is masked by default
|
|
- [ ] Show/Hide toggle works
|
|
- [ ] Copy to clipboard works
|
|
- [ ] Can lock store manually
|
|
- [ ] Lock All Stores works
|
|
- [ ] Save Store saves only that store
|
|
- [ ] Save (Ctrl+S) only saves config
|
|
- [ ] Unsaved changes indicator works for stores
|
|
- [ ] Generate Key File works
|
|
- [ ] All tests pass
|