remove files, update README
This commit is contained in:
parent
d8cf61be47
commit
5ab7004477
@ -1 +0,0 @@
|
|||||||
3.13
|
|
||||||
97
CHANGELOG.md
97
CHANGELOG.md
@ -1,97 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to Browser Vault GUI will be documented in this file.
|
|
||||||
|
|
||||||
## [0.2.0] - 2024-01-XX - Vue 3 Migration + Credential Saving
|
|
||||||
|
|
||||||
### ✨ Major Changes
|
|
||||||
|
|
||||||
#### Vue 3 Migration
|
|
||||||
- **BREAKING**: Complete rewrite from React to Vue 3
|
|
||||||
- Replaced React with Vue 3 Composition API
|
|
||||||
- Replaced custom CSS with Tailwind CSS
|
|
||||||
- Added DaisyUI for beautiful UI components
|
|
||||||
- ~30% smaller bundle size
|
|
||||||
- Better performance and developer experience
|
|
||||||
|
|
||||||
#### New Feature: Optional Credential Saving
|
|
||||||
- Added option to save credentials in localStorage (opt-in)
|
|
||||||
- Prominent security warning modal on first save
|
|
||||||
- Visual indicators (🔓 badge) for servers with saved credentials
|
|
||||||
- Auto-fill credentials on subsequent logins
|
|
||||||
- Easy removal of saved credentials
|
|
||||||
- **Security**: Disabled by default, requires explicit user consent
|
|
||||||
|
|
||||||
### 📦 Added
|
|
||||||
- Vue 3 with `<script setup>` syntax
|
|
||||||
- Tailwind CSS for utility-first styling
|
|
||||||
- DaisyUI component library
|
|
||||||
- Credential saving feature (with warnings)
|
|
||||||
- Security warning modal
|
|
||||||
- `SECURITY_CREDENTIALS.md` documentation
|
|
||||||
- `VUE_MIGRATION.md` migration guide
|
|
||||||
- Server badges showing saved credential status
|
|
||||||
|
|
||||||
### 🔄 Changed
|
|
||||||
- All `.tsx` components converted to `.vue`
|
|
||||||
- All custom CSS replaced with Tailwind utilities
|
|
||||||
- Form inputs now use DaisyUI components
|
|
||||||
- Improved responsive design
|
|
||||||
- Better dark/light mode support
|
|
||||||
- Enhanced warning colors for security features
|
|
||||||
|
|
||||||
### 🗑️ Removed
|
|
||||||
- All React dependencies
|
|
||||||
- All `.tsx` files
|
|
||||||
- All custom `.css` component files
|
|
||||||
- React-specific ESLint config
|
|
||||||
|
|
||||||
### ⚠️ Security Notes
|
|
||||||
- Credential saving is **opt-in only**
|
|
||||||
- Multiple security warnings shown to users
|
|
||||||
- Plain text storage with clear disclosure
|
|
||||||
- Recommended only for development/testing
|
|
||||||
- See `SECURITY_CREDENTIALS.md` for full analysis
|
|
||||||
|
|
||||||
### 🐛 Fixed
|
|
||||||
- Mount point checkbox selectability issue
|
|
||||||
- API response parsing for `/v1/sys/internal/ui/mounts`
|
|
||||||
- TypeScript strict mode compatibility
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## [0.1.0] - 2024-01-XX - Initial React Release
|
|
||||||
|
|
||||||
### ✨ Initial Features
|
|
||||||
- Multiple Vault server management
|
|
||||||
- Token, Userpass, and LDAP authentication
|
|
||||||
- Login verification with mount point detection
|
|
||||||
- Automatic KV v1/v2 detection
|
|
||||||
- Secret reading and browsing
|
|
||||||
- Recursive path search
|
|
||||||
- Multi-mount point search
|
|
||||||
- Smart caching system
|
|
||||||
- Settings panel for cache and search configuration
|
|
||||||
- KV Secret Engine v1 and v2 support
|
|
||||||
- Browser-compatible Vault HTTP client
|
|
||||||
- Retry and timeout handling
|
|
||||||
- Comprehensive error messages
|
|
||||||
- CORS configuration guidance
|
|
||||||
- React 18 + TypeScript
|
|
||||||
- Vite build tooling
|
|
||||||
- Modern CSS3 styling
|
|
||||||
|
|
||||||
### 📚 Documentation
|
|
||||||
- `README.md` - Project overview and setup
|
|
||||||
- `KV_VERSIONS.md` - KV v1 vs v2 guide
|
|
||||||
- `MOUNT_POINTS.md` - Mount point detection
|
|
||||||
- `CORS_AND_CLIENT.md` - CORS configuration
|
|
||||||
- `LATEST_FEATURES.md` - Recent features
|
|
||||||
- `IMPROVEMENTS_SUMMARY.md` - Architecture notes
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Version History
|
|
||||||
|
|
||||||
- **0.2.0** - Vue 3 migration + credential saving (current)
|
|
||||||
- **0.1.0** - Initial React implementation
|
|
||||||
@ -1,240 +0,0 @@
|
|||||||
# Cleanup & Security Improvements Summary
|
|
||||||
|
|
||||||
## ✅ All Requested Changes Implemented
|
|
||||||
|
|
||||||
### 1. 🔒 Secrets Never Cached (Security Fix)
|
|
||||||
|
|
||||||
**Problem**: Secret data was being cached in localStorage, which is a security risk.
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
- ✅ Removed all caching from `readSecret()` method
|
|
||||||
- ✅ Secret data is now **always fetched fresh** from Vault
|
|
||||||
- ✅ Only directory listings are cached (for search performance)
|
|
||||||
- ✅ Updated UI to clearly indicate this security improvement
|
|
||||||
|
|
||||||
**Code Changes**:
|
|
||||||
```typescript
|
|
||||||
// Before: Cached secret data
|
|
||||||
async readSecret() {
|
|
||||||
const cached = vaultCache.get(cacheKey);
|
|
||||||
if (cached) return cached; // ❌ Security risk
|
|
||||||
|
|
||||||
const data = await client.read(path);
|
|
||||||
vaultCache.set(cacheKey, data); // ❌ Caching secrets
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// After: Never cache secrets
|
|
||||||
async readSecret() {
|
|
||||||
console.log(`⚡ API call for read (no cache): ${path}`);
|
|
||||||
const data = await client.read(path);
|
|
||||||
// SECURITY: Never cache secret data - always fetch fresh
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 🎯 Mount Point Selector (UX Improvement)
|
|
||||||
|
|
||||||
**Problem**: Users had to manually type full paths including mount points.
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
- ✅ Added dropdown selector for available mount points
|
|
||||||
- ✅ Separate input field for the secret path (without mount prefix)
|
|
||||||
- ✅ Visual preview of the full path being constructed
|
|
||||||
- ✅ Auto-parsing when selecting paths from search results
|
|
||||||
|
|
||||||
**UI Changes**:
|
|
||||||
```
|
|
||||||
Before: [secret/data/myapp/config ] [Read Secret]
|
|
||||||
|
|
||||||
After: Mount Point: [secret ▼] (kv v2)
|
|
||||||
Secret Path: [secret/] [data/myapp/config] [Read Secret]
|
|
||||||
Full path: secret/data/myapp/config
|
|
||||||
```
|
|
||||||
|
|
||||||
**Features**:
|
|
||||||
- Mount point dropdown shows: `secret/ (kv v2)`, `kv/ (kv v2)`, etc.
|
|
||||||
- Path input is disabled until mount point is selected
|
|
||||||
- Button is disabled until both mount point and path are provided
|
|
||||||
- Search results auto-populate the correct mount point + path
|
|
||||||
|
|
||||||
### 3. 🔍 Search Shown by Default
|
|
||||||
|
|
||||||
**Problem**: Search was hidden by default, but it's the primary function.
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
- ✅ Changed `showSearch = ref(true)` (was `false`)
|
|
||||||
- ✅ Search component is now visible immediately upon login
|
|
||||||
- ✅ Button text updated to "Hide Search" / "Show Search"
|
|
||||||
|
|
||||||
### 4. 🌐 Search All Mount Points by Default
|
|
||||||
|
|
||||||
**Problem**: "Search across all mount points" was disabled by default.
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
- ✅ Changed `searchAllMounts = ref(true)` (was `false`)
|
|
||||||
- ✅ Multi-mount search is now enabled by default
|
|
||||||
- ✅ Users can still disable it if they want to search a specific mount
|
|
||||||
|
|
||||||
## Security Improvements
|
|
||||||
|
|
||||||
### 🔒 Secret Data Protection
|
|
||||||
- **Never cached**: Secret values are always fetched fresh
|
|
||||||
- **Memory only**: Secret data exists only in component state during viewing
|
|
||||||
- **No persistence**: Secrets are not stored in localStorage
|
|
||||||
- **Clear indicators**: UI explicitly states "Secret data is never cached"
|
|
||||||
|
|
||||||
### 📂 Directory Listing Caching (Still Enabled)
|
|
||||||
- **Performance**: Directory listings are still cached for search speed
|
|
||||||
- **No sensitive data**: Only path names, not secret values
|
|
||||||
- **Configurable**: Cache can be cleared manually
|
|
||||||
- **Reasonable**: Directory structure is less sensitive than secret values
|
|
||||||
|
|
||||||
## User Experience Improvements
|
|
||||||
|
|
||||||
### 🎯 Better Path Input
|
|
||||||
- **Guided input**: Mount point dropdown prevents typos
|
|
||||||
- **Visual feedback**: Shows full path being constructed
|
|
||||||
- **Auto-completion**: Search results populate the form correctly
|
|
||||||
- **Validation**: Button disabled until valid input provided
|
|
||||||
|
|
||||||
### 🔍 Search-First Interface
|
|
||||||
- **Primary function**: Search is now the main interface
|
|
||||||
- **Immediate access**: No need to click "Show Search"
|
|
||||||
- **Multi-mount default**: Searches all available secret engines
|
|
||||||
- **Comprehensive**: Finds secrets across the entire Vault instance
|
|
||||||
|
|
||||||
### 📱 Responsive Design
|
|
||||||
- **Mount point selector**: Works well on mobile
|
|
||||||
- **Path preview**: Clear indication of what will be accessed
|
|
||||||
- **Disabled states**: Clear visual feedback for invalid states
|
|
||||||
|
|
||||||
## Technical Implementation
|
|
||||||
|
|
||||||
### Cache Logic Changes
|
|
||||||
```typescript
|
|
||||||
// Only directory listings cached now
|
|
||||||
async listSecrets(path: string) {
|
|
||||||
const cached = vaultCache.get(cacheKey);
|
|
||||||
if (cached) return cached; // ✅ OK - just directory names
|
|
||||||
|
|
||||||
const listing = await client.list(path);
|
|
||||||
vaultCache.set(cacheKey, listing); // ✅ OK - no secret values
|
|
||||||
return listing;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secret data never cached
|
|
||||||
async readSecret(path: string) {
|
|
||||||
// No cache check - always fetch fresh
|
|
||||||
return await client.read(path); // ✅ Always fresh data
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mount Point Integration
|
|
||||||
```typescript
|
|
||||||
// Parse search results to extract mount + path
|
|
||||||
const handleSelectPath = (fullPath: string) => {
|
|
||||||
const mountPoints = connection.mountPoints || []
|
|
||||||
|
|
||||||
// Find matching mount point
|
|
||||||
for (const mount of mountPoints) {
|
|
||||||
if (fullPath.startsWith(mount.path + '/')) {
|
|
||||||
selectedMountPoint.value = mount.path
|
|
||||||
secretPath.value = fullPath.substring(mount.path.length + 1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleReadSecret(fullPath)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Updates
|
|
||||||
|
|
||||||
### Default Settings
|
|
||||||
- **Search visible**: `showSearch = true`
|
|
||||||
- **Multi-mount search**: `searchAllMounts = true`
|
|
||||||
- **No secret caching**: Removed from `readSecret()`
|
|
||||||
- **Directory caching**: Still enabled for performance
|
|
||||||
|
|
||||||
### User Control
|
|
||||||
- Users can still hide search if desired
|
|
||||||
- Users can disable multi-mount search for specific searches
|
|
||||||
- Cache settings still configurable in Settings panel
|
|
||||||
- Mount point selection is per-operation
|
|
||||||
|
|
||||||
## Documentation Updates
|
|
||||||
|
|
||||||
### README.md
|
|
||||||
- Updated cache security section
|
|
||||||
- Clarified what is/isn't cached
|
|
||||||
- Emphasized secret data protection
|
|
||||||
|
|
||||||
### UI Messages
|
|
||||||
- "Secret data is never cached - always fetched fresh"
|
|
||||||
- "Directory listings are cached to improve search performance"
|
|
||||||
- Clear security indicators throughout interface
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
|
|
||||||
### 🔒 Security
|
|
||||||
- **Zero secret persistence**: Secrets never touch localStorage
|
|
||||||
- **Fresh data guarantee**: Always get current secret values
|
|
||||||
- **Reduced attack surface**: No cached secrets to compromise
|
|
||||||
|
|
||||||
### 🚀 Performance
|
|
||||||
- **Smart caching**: Directory listings cached for search speed
|
|
||||||
- **Reduced API calls**: Search still benefits from caching
|
|
||||||
- **Responsive UI**: Mount point selector is fast and intuitive
|
|
||||||
|
|
||||||
### 👥 User Experience
|
|
||||||
- **Search-first**: Primary function is immediately available
|
|
||||||
- **Guided input**: Mount point selector prevents errors
|
|
||||||
- **Multi-mount default**: Comprehensive search out of the box
|
|
||||||
- **Clear feedback**: Visual indicators for all states
|
|
||||||
|
|
||||||
## Migration Notes
|
|
||||||
|
|
||||||
### For Existing Users
|
|
||||||
- **No data loss**: Existing server configurations preserved
|
|
||||||
- **Better security**: Secret data no longer cached (automatic improvement)
|
|
||||||
- **New UI**: Mount point selector may require brief learning
|
|
||||||
- **Search default**: Search is now shown by default (can be hidden)
|
|
||||||
|
|
||||||
### For Developers
|
|
||||||
- **API unchanged**: `vaultApi.readSecret()` still works the same
|
|
||||||
- **Caching removed**: No more secret data in cache
|
|
||||||
- **UI components**: New mount point selector component
|
|
||||||
- **Default states**: Search and multi-mount enabled by default
|
|
||||||
|
|
||||||
## Testing Recommendations
|
|
||||||
|
|
||||||
1. **Verify no secret caching**:
|
|
||||||
- Read a secret
|
|
||||||
- Check localStorage - should contain no secret values
|
|
||||||
- Only directory listings should be cached
|
|
||||||
|
|
||||||
2. **Test mount point selector**:
|
|
||||||
- Select different mount points
|
|
||||||
- Verify path construction
|
|
||||||
- Test with search result selection
|
|
||||||
|
|
||||||
3. **Confirm search defaults**:
|
|
||||||
- Login to Vault
|
|
||||||
- Search should be visible immediately
|
|
||||||
- "Search all mount points" should be checked
|
|
||||||
|
|
||||||
4. **Security validation**:
|
|
||||||
- Read multiple secrets
|
|
||||||
- Confirm fresh API calls each time
|
|
||||||
- Verify no secret data in browser storage
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
✅ **All requested changes implemented successfully**:
|
|
||||||
- 🔒 Secrets never cached (security improvement)
|
|
||||||
- 🎯 Mount point selector (UX improvement)
|
|
||||||
- 🔍 Search shown by default (primary function)
|
|
||||||
- 🌐 Multi-mount search by default (comprehensive)
|
|
||||||
|
|
||||||
The application is now more secure, more user-friendly, and better aligned with its primary purpose as a Vault search and browsing tool.
|
|
||||||
@ -1,356 +0,0 @@
|
|||||||
# CORS and Vault Client Implementation
|
|
||||||
|
|
||||||
## ❌ Why Your Changes Won't Work
|
|
||||||
|
|
||||||
You tried to fix CORS by adding these changes:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// ❌ This won't work
|
|
||||||
headers: {
|
|
||||||
'Access-Control-Allow-Origin': '*'
|
|
||||||
}
|
|
||||||
|
|
||||||
// ❌ This will break response reading
|
|
||||||
mode: 'no-cors'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Why `Access-Control-Allow-Origin` in Client Headers Doesn't Work
|
|
||||||
|
|
||||||
**CORS headers MUST be set by the SERVER, not the client.**
|
|
||||||
|
|
||||||
When you add `Access-Control-Allow-Origin: *` to your request headers:
|
|
||||||
1. The header is sent to the server
|
|
||||||
2. The server ignores it (only the server's response headers matter)
|
|
||||||
3. The browser still blocks the response because the server didn't send the CORS header
|
|
||||||
|
|
||||||
**How CORS Actually Works:**
|
|
||||||
```
|
|
||||||
Browser → Request to vault.example.com
|
|
||||||
↓
|
|
||||||
Vault Server → Response with header: Access-Control-Allow-Origin: https://yourfrontend.com
|
|
||||||
↓
|
|
||||||
Browser → Allows JavaScript to read the response
|
|
||||||
```
|
|
||||||
|
|
||||||
### Why `mode: 'no-cors'` Breaks Everything
|
|
||||||
|
|
||||||
The `no-cors` mode:
|
|
||||||
- ✅ Allows the request to be sent
|
|
||||||
- ❌ **Prevents you from reading the response body**
|
|
||||||
- ❌ You can't access status codes
|
|
||||||
- ❌ You can't read JSON data
|
|
||||||
- ❌ You only get an "opaque" response
|
|
||||||
|
|
||||||
It's called "no-cors" mode because it **doesn't check CORS**, but it also **doesn't let you use the response**.
|
|
||||||
|
|
||||||
**Example:**
|
|
||||||
```typescript
|
|
||||||
// With no-cors
|
|
||||||
const response = await fetch(url, { mode: 'no-cors' });
|
|
||||||
console.log(response.status); // Always 0
|
|
||||||
console.log(await response.json()); // Error: Can't read body
|
|
||||||
```
|
|
||||||
|
|
||||||
## ✅ The Proper Solution
|
|
||||||
|
|
||||||
### 1. Configure Your Vault Server
|
|
||||||
|
|
||||||
Add CORS configuration to your Vault server config file:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
# vault.hcl
|
|
||||||
ui = true
|
|
||||||
|
|
||||||
listener "tcp" {
|
|
||||||
address = "0.0.0.0:8200"
|
|
||||||
tls_disable = 1 # Only for development! Use TLS in production
|
|
||||||
|
|
||||||
# Enable CORS
|
|
||||||
cors_enabled = true
|
|
||||||
cors_allowed_origins = [
|
|
||||||
"http://localhost:5173", # Vite dev server
|
|
||||||
"https://yourdomain.com" # Production domain
|
|
||||||
]
|
|
||||||
cors_allowed_headers = ["*"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Or if using Docker:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# docker-compose.yml
|
|
||||||
version: '3.8'
|
|
||||||
services:
|
|
||||||
vault:
|
|
||||||
image: vault:latest
|
|
||||||
ports:
|
|
||||||
- "8200:8200"
|
|
||||||
environment:
|
|
||||||
VAULT_DEV_ROOT_TOKEN_ID: myroot
|
|
||||||
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
|
|
||||||
VAULT_API_ADDR: http://127.0.0.1:8200
|
|
||||||
cap_add:
|
|
||||||
- IPC_LOCK
|
|
||||||
command:
|
|
||||||
- server
|
|
||||||
- -dev
|
|
||||||
- -dev-root-token-id=myroot
|
|
||||||
```
|
|
||||||
|
|
||||||
Then exec into the container and configure CORS via the API:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker exec -it vault sh
|
|
||||||
vault write sys/config/cors enabled=true allowed_origins="http://localhost:5173"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Use the Proper Vault Client
|
|
||||||
|
|
||||||
We've now implemented a proper browser-compatible Vault client:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// ✅ NEW: Clean, maintainable VaultClient
|
|
||||||
import { VaultClient } from './services/vaultClient';
|
|
||||||
|
|
||||||
const client = new VaultClient({
|
|
||||||
server: { url: 'https://vault.example.com', ... },
|
|
||||||
credentials: { token: 'your-token', ... },
|
|
||||||
timeout: 30000,
|
|
||||||
retries: 2
|
|
||||||
});
|
|
||||||
|
|
||||||
// Read a secret
|
|
||||||
const data = await client.read('secret/data/myapp/config');
|
|
||||||
|
|
||||||
// List secrets
|
|
||||||
const keys = await client.list('secret/');
|
|
||||||
|
|
||||||
// Write a secret
|
|
||||||
await client.write('secret/data/myapp/config', {
|
|
||||||
username: 'admin',
|
|
||||||
password: 'secret'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete a secret
|
|
||||||
await client.delete('secret/data/myapp/config');
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🏗️ Architecture: Why Use a Client Class?
|
|
||||||
|
|
||||||
### Before (Raw API Calls)
|
|
||||||
```typescript
|
|
||||||
// ❌ Hard to maintain, error-prone
|
|
||||||
async function readSecret(url, token, path) {
|
|
||||||
const response = await fetch(`${url}/v1/${path}`, {
|
|
||||||
headers: {
|
|
||||||
'X-Vault-Token': token,
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed'); // Not helpful
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return data.data.data; // Confusing nested structure
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### After (VaultClient)
|
|
||||||
```typescript
|
|
||||||
// ✅ Clean, maintainable, type-safe
|
|
||||||
const client = new VaultClient(options);
|
|
||||||
const data = await client.read(path); // Simple!
|
|
||||||
```
|
|
||||||
|
|
||||||
### Benefits of VaultClient
|
|
||||||
|
|
||||||
1. **Error Handling**
|
|
||||||
```typescript
|
|
||||||
try {
|
|
||||||
await client.read('secret/data/test');
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof VaultError) {
|
|
||||||
console.log(error.statusCode); // 403
|
|
||||||
console.log(error.errors); // ["permission denied"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Automatic Retries**
|
|
||||||
- Retries on network errors
|
|
||||||
- Exponential backoff
|
|
||||||
- Configurable retry count
|
|
||||||
|
|
||||||
3. **Timeout Protection**
|
|
||||||
```typescript
|
|
||||||
const client = new VaultClient({
|
|
||||||
...options,
|
|
||||||
timeout: 5000 // 5 seconds
|
|
||||||
});
|
|
||||||
// Request will abort after 5 seconds
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Path Normalization**
|
|
||||||
```typescript
|
|
||||||
// All of these work the same:
|
|
||||||
await client.read('/secret/data/test');
|
|
||||||
await client.read('secret/data/test');
|
|
||||||
await client.read('//secret/data/test//');
|
|
||||||
// All normalized to: secret/data/test
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Authentication Methods**
|
|
||||||
```typescript
|
|
||||||
// Token (already have one)
|
|
||||||
const client = new VaultClient({
|
|
||||||
server,
|
|
||||||
credentials: { token: 'your-token', ... }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Username/Password (get token)
|
|
||||||
const token = await client.loginUserpass('username', 'password');
|
|
||||||
|
|
||||||
// LDAP (get token)
|
|
||||||
const token = await client.loginLdap('username', 'password');
|
|
||||||
```
|
|
||||||
|
|
||||||
6. **Type Safety**
|
|
||||||
```typescript
|
|
||||||
// TypeScript knows the structure
|
|
||||||
interface MySecret {
|
|
||||||
username: string;
|
|
||||||
password: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await client.read<MySecret>('secret/data/myapp');
|
|
||||||
console.log(data.username); // TypeScript autocomplete works!
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Testing Your CORS Configuration
|
|
||||||
|
|
||||||
### 1. Test with curl (should work)
|
|
||||||
```bash
|
|
||||||
curl -X GET \
|
|
||||||
-H "X-Vault-Token: your-token" \
|
|
||||||
http://localhost:8200/v1/secret/data/test
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Test from browser console
|
|
||||||
```javascript
|
|
||||||
fetch('http://localhost:8200/v1/secret/data/test', {
|
|
||||||
headers: {
|
|
||||||
'X-Vault-Token': 'your-token'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(r => r.json())
|
|
||||||
.then(console.log)
|
|
||||||
.catch(console.error);
|
|
||||||
```
|
|
||||||
|
|
||||||
If you see a CORS error here, your Vault server CORS is not configured correctly.
|
|
||||||
|
|
||||||
### 3. Check Response Headers
|
|
||||||
|
|
||||||
In browser DevTools → Network tab, check the response headers:
|
|
||||||
|
|
||||||
```
|
|
||||||
✅ Should see:
|
|
||||||
Access-Control-Allow-Origin: http://localhost:5173
|
|
||||||
Access-Control-Allow-Headers: *
|
|
||||||
|
|
||||||
❌ If missing:
|
|
||||||
Your Vault server CORS is not configured
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🆚 Comparison: Raw API vs VaultClient
|
|
||||||
|
|
||||||
| Feature | Raw fetch() | VaultClient |
|
|
||||||
|---------|-------------|-------------|
|
|
||||||
| Code lines (typical read) | ~15 lines | 1 line |
|
|
||||||
| Error handling | Manual | Built-in |
|
|
||||||
| Retries | Manual | Automatic |
|
|
||||||
| Timeouts | Manual | Built-in |
|
|
||||||
| Type safety | None | Full |
|
|
||||||
| Path normalization | Manual | Automatic |
|
|
||||||
| Authentication | Manual | Built-in |
|
|
||||||
| Token management | Manual | Built-in |
|
|
||||||
| Health checks | Manual | Built-in |
|
|
||||||
| Maintainability | Low | High |
|
|
||||||
|
|
||||||
## 📚 Advanced Usage
|
|
||||||
|
|
||||||
### Custom Error Handling
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { VaultError } from './services/vaultClient';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = await client.read('secret/data/test');
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof VaultError) {
|
|
||||||
switch (error.statusCode) {
|
|
||||||
case 403:
|
|
||||||
alert('Permission denied');
|
|
||||||
break;
|
|
||||||
case 404:
|
|
||||||
alert('Secret not found');
|
|
||||||
break;
|
|
||||||
case 500:
|
|
||||||
alert('Vault server error');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
alert(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Health Check Before Operations
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const client = new VaultClient(options);
|
|
||||||
|
|
||||||
// Check if Vault is healthy
|
|
||||||
const health = await client.health();
|
|
||||||
if (health.sealed) {
|
|
||||||
alert('Vault is sealed! Please unseal it first.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now safe to perform operations
|
|
||||||
const data = await client.read('secret/data/test');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Token Lifecycle Management
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Login
|
|
||||||
const client = new VaultClient(options);
|
|
||||||
const token = await client.loginUserpass('user', 'pass');
|
|
||||||
|
|
||||||
// Check token info
|
|
||||||
const tokenInfo = await client.tokenLookupSelf();
|
|
||||||
console.log('Token expires:', tokenInfo.data.expire_time);
|
|
||||||
console.log('Token TTL:', tokenInfo.data.ttl);
|
|
||||||
|
|
||||||
// Revoke token on logout
|
|
||||||
await client.tokenRevokeSelf();
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 Summary
|
|
||||||
|
|
||||||
1. **CORS must be configured on the Vault SERVER, not the client**
|
|
||||||
2. **`mode: 'no-cors'` prevents you from reading responses**
|
|
||||||
3. **Use the VaultClient class for clean, maintainable code**
|
|
||||||
4. **VaultClient provides:**
|
|
||||||
- Automatic retries
|
|
||||||
- Timeout protection
|
|
||||||
- Better error messages
|
|
||||||
- Type safety
|
|
||||||
- Built-in authentication
|
|
||||||
- Path normalization
|
|
||||||
|
|
||||||
The new implementation is production-ready, maintainable, and properly handles all edge cases!
|
|
||||||
|
|
||||||
237
FEATURES.md
237
FEATURES.md
@ -1,237 +0,0 @@
|
|||||||
# Feature Summary
|
|
||||||
|
|
||||||
## ✅ Completed Features
|
|
||||||
|
|
||||||
### 1. Recursive Path Search 🔍
|
|
||||||
- **Location**: Dashboard → "🔍 Search" button
|
|
||||||
- **Functionality**:
|
|
||||||
- Recursively searches through vault paths
|
|
||||||
- Configurable search depth to prevent infinite loops
|
|
||||||
- Configurable maximum results
|
|
||||||
- Case-insensitive partial matching
|
|
||||||
- Distinguishes between directories (📁) and secrets (📄)
|
|
||||||
- **Performance**:
|
|
||||||
- Search time displayed
|
|
||||||
- Results cached automatically
|
|
||||||
- Non-blocking UI during search
|
|
||||||
|
|
||||||
### 2. Smart Caching System 💾
|
|
||||||
- **Location**: Implemented globally, managed in Settings
|
|
||||||
- **Features**:
|
|
||||||
- Caches all API responses (list and read operations)
|
|
||||||
- Configurable cache size limit (MB)
|
|
||||||
- Configurable expiration time (minutes)
|
|
||||||
- Automatic size enforcement with LRU eviction
|
|
||||||
- Cache key format: `{serverId}:{operation}:{path}`
|
|
||||||
- **Statistics**:
|
|
||||||
- Real-time cache size monitoring
|
|
||||||
- Entry count tracking
|
|
||||||
- Oldest/newest entry timestamps
|
|
||||||
- Manual cache clearing
|
|
||||||
|
|
||||||
### 3. Configuration System ⚙️
|
|
||||||
- **Location**: Dashboard → "⚙️ Settings" button
|
|
||||||
- **Cache Configuration**:
|
|
||||||
- Enable/disable caching
|
|
||||||
- Max cache size (1-100 MB, default: 10 MB)
|
|
||||||
- Cache expiration (1-1440 minutes, default: 30 min)
|
|
||||||
- **Search Configuration**:
|
|
||||||
- Max search depth (1-50, default: 10)
|
|
||||||
- Max search results (10-10000, default: 1000)
|
|
||||||
- **Persistence**: All settings saved to localStorage
|
|
||||||
|
|
||||||
### 4. Vault API Client 🔌
|
|
||||||
- **Location**: `src/services/vaultApi.ts`
|
|
||||||
- **Implemented Endpoints**:
|
|
||||||
- ✅ `listSecrets()` - LIST endpoint with caching
|
|
||||||
- ✅ `readSecret()` - GET endpoint with caching
|
|
||||||
- ✅ `searchPaths()` - Recursive search with depth control
|
|
||||||
- **Features**:
|
|
||||||
- Automatic cache integration
|
|
||||||
- Error handling
|
|
||||||
- Path normalization
|
|
||||||
- Support for multiple auth methods
|
|
||||||
|
|
||||||
### 5. Cache Manager 🗄️
|
|
||||||
- **Location**: `src/utils/cache.ts`
|
|
||||||
- **Capabilities**:
|
|
||||||
- localStorage-based persistence
|
|
||||||
- Size calculation and enforcement
|
|
||||||
- Age-based expiration
|
|
||||||
- LRU eviction when quota exceeded
|
|
||||||
- Cleanup of expired entries
|
|
||||||
- Statistics collection
|
|
||||||
- **Methods**:
|
|
||||||
- `get<T>(key)` - Retrieve with expiration check
|
|
||||||
- `set<T>(key, data)` - Store with size calculation
|
|
||||||
- `has(key)` - Check existence
|
|
||||||
- `delete(key)` - Remove entry
|
|
||||||
- `clear()` - Remove all entries
|
|
||||||
- `getStats()` - Get cache statistics
|
|
||||||
- `cleanup()` - Remove expired entries
|
|
||||||
|
|
||||||
### 6. Settings UI 🎛️
|
|
||||||
- **Location**: `src/components/Settings.tsx`
|
|
||||||
- **Features**:
|
|
||||||
- Modal overlay interface
|
|
||||||
- Real-time cache statistics
|
|
||||||
- Form validation
|
|
||||||
- Immediate save and apply
|
|
||||||
- Responsive design
|
|
||||||
|
|
||||||
### 7. Search UI 🔎
|
|
||||||
- **Location**: `src/components/PathSearch.tsx`
|
|
||||||
- **Features**:
|
|
||||||
- Base path configuration
|
|
||||||
- Search term input with Enter key support
|
|
||||||
- Loading spinner during search
|
|
||||||
- Search statistics (results count, time taken)
|
|
||||||
- Clickable results for secrets
|
|
||||||
- Visual distinction of directories vs secrets
|
|
||||||
- Depth indicator for each result
|
|
||||||
- Helpful search tips
|
|
||||||
|
|
||||||
## 🎨 UI/UX Enhancements
|
|
||||||
|
|
||||||
### Dashboard Updates
|
|
||||||
- Added action button group (Search, Settings, Logout)
|
|
||||||
- Toggle search panel visibility
|
|
||||||
- Integrated settings modal
|
|
||||||
- Improved responsive layout
|
|
||||||
|
|
||||||
### Visual Feedback
|
|
||||||
- Loading states for all async operations
|
|
||||||
- Progress indicators during search
|
|
||||||
- Success/error messages
|
|
||||||
- Cache statistics display
|
|
||||||
- Search result highlighting
|
|
||||||
|
|
||||||
## 🔒 Security Features
|
|
||||||
|
|
||||||
### Data Protection
|
|
||||||
- ✅ Credentials never cached or persisted
|
|
||||||
- ✅ Only in-memory storage during session
|
|
||||||
- ✅ Server configurations saved securely
|
|
||||||
- ✅ Cache can be manually cleared
|
|
||||||
- ⚠️ Cached data includes secret values (cleared on logout recommended)
|
|
||||||
|
|
||||||
### DDoS Prevention
|
|
||||||
- ✅ Configurable cache prevents repeat API calls
|
|
||||||
- ✅ Search depth limits prevent runaway recursion
|
|
||||||
- ✅ Result limits prevent memory exhaustion
|
|
||||||
- ✅ Automatic size enforcement prevents quota issues
|
|
||||||
|
|
||||||
## 📊 Performance Optimizations
|
|
||||||
|
|
||||||
### Caching Strategy
|
|
||||||
1. **Cache Hit**: Instant response from localStorage
|
|
||||||
2. **Cache Miss**: API call + cache storage
|
|
||||||
3. **Cache Expiration**: Automatic refresh after configured time
|
|
||||||
4. **Cache Eviction**: LRU algorithm when size limit reached
|
|
||||||
|
|
||||||
### Search Optimization
|
|
||||||
1. **Early Exit**: Stops at max results or depth
|
|
||||||
2. **Parallel Operations**: Could be enhanced with Promise.all
|
|
||||||
3. **Progress Feedback**: Non-blocking UI
|
|
||||||
4. **Cached Paths**: Subsequent searches of same paths are instant
|
|
||||||
|
|
||||||
## 📁 File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── components/
|
|
||||||
│ ├── ServerSelector.tsx/css # Multi-server management
|
|
||||||
│ ├── LoginForm.tsx/css # Authentication UI
|
|
||||||
│ ├── Dashboard.tsx/css # Main dashboard (enhanced)
|
|
||||||
│ ├── PathSearch.tsx/css # NEW: Search interface
|
|
||||||
│ └── Settings.tsx/css # NEW: Settings modal
|
|
||||||
├── services/
|
|
||||||
│ └── vaultApi.ts # NEW: API client with caching
|
|
||||||
├── utils/
|
|
||||||
│ └── cache.ts # NEW: Cache management
|
|
||||||
├── config.ts # NEW: Configuration system
|
|
||||||
├── types.ts # Type definitions
|
|
||||||
├── App.tsx/css # Main app
|
|
||||||
├── main.tsx # Entry point
|
|
||||||
└── index.css # Global styles
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🧪 Testing Recommendations
|
|
||||||
|
|
||||||
### Manual Testing Checklist
|
|
||||||
- [ ] Add/remove vault servers
|
|
||||||
- [ ] Connect with token auth
|
|
||||||
- [ ] Read a secret directly
|
|
||||||
- [ ] Perform recursive search
|
|
||||||
- [ ] Verify cache hit (check console logs)
|
|
||||||
- [ ] Adjust cache settings
|
|
||||||
- [ ] Clear cache
|
|
||||||
- [ ] View cache statistics
|
|
||||||
- [ ] Test search depth limits
|
|
||||||
- [ ] Test result limits
|
|
||||||
- [ ] Test with expired cache
|
|
||||||
- [ ] Test with full localStorage
|
|
||||||
- [ ] Test responsive design
|
|
||||||
- [ ] Test logout (clears session but not cache)
|
|
||||||
|
|
||||||
### Edge Cases to Test
|
|
||||||
- [ ] Search with no results
|
|
||||||
- [ ] Search at max depth
|
|
||||||
- [ ] Search at max results
|
|
||||||
- [ ] Very large cache size
|
|
||||||
- [ ] Very small cache size
|
|
||||||
- [ ] Cache expiration edge cases
|
|
||||||
- [ ] localStorage quota exceeded
|
|
||||||
- [ ] CORS errors
|
|
||||||
- [ ] Network errors
|
|
||||||
- [ ] Invalid paths
|
|
||||||
- [ ] Invalid credentials
|
|
||||||
|
|
||||||
## 🔮 Future Enhancements
|
|
||||||
|
|
||||||
### Potential Additions
|
|
||||||
1. **Auto-clear cache on logout** (currently requires manual clear)
|
|
||||||
2. **Cache encryption** for sensitive data
|
|
||||||
3. **Parallel search** with Promise.all for better performance
|
|
||||||
4. **Search filters** (directories only, secrets only, etc.)
|
|
||||||
5. **Search history** saved in localStorage
|
|
||||||
6. **Export/import settings**
|
|
||||||
7. **Secret writing/updating**
|
|
||||||
8. **Secret deletion**
|
|
||||||
9. **Batch operations**
|
|
||||||
10. **Tree view** for path browsing
|
|
||||||
|
|
||||||
### Code Improvements
|
|
||||||
1. Add unit tests for cache manager
|
|
||||||
2. Add integration tests for API client
|
|
||||||
3. Add E2E tests with Playwright
|
|
||||||
4. Implement proper error boundaries
|
|
||||||
5. Add telemetry/analytics (opt-in)
|
|
||||||
6. Improve TypeScript strictness
|
|
||||||
7. Add API request cancellation
|
|
||||||
8. Implement retry logic
|
|
||||||
9. Add request queuing/throttling
|
|
||||||
10. Add offline support
|
|
||||||
|
|
||||||
## 📖 Documentation
|
|
||||||
|
|
||||||
- ✅ `README.md` - Updated with new features
|
|
||||||
- ✅ `USAGE.md` - Comprehensive usage guide
|
|
||||||
- ✅ `FEATURES.md` - This file
|
|
||||||
- ✅ Inline code comments
|
|
||||||
- ✅ JSDoc comments on key functions
|
|
||||||
- ✅ Configuration examples
|
|
||||||
|
|
||||||
## 🎯 Key Accomplishments
|
|
||||||
|
|
||||||
1. ✅ **Recursive search** with configurable limits
|
|
||||||
2. ✅ **Smart caching** to prevent DDoS
|
|
||||||
3. ✅ **Configurable settings** for both cache and search
|
|
||||||
4. ✅ **Real-time statistics** for monitoring
|
|
||||||
5. ✅ **Clean architecture** with separation of concerns
|
|
||||||
6. ✅ **Type safety** throughout
|
|
||||||
7. ✅ **Responsive UI** that works on mobile
|
|
||||||
8. ✅ **Production-ready** with proper error handling
|
|
||||||
9. ✅ **Well-documented** with multiple documentation files
|
|
||||||
10. ✅ **Extensible** design for future enhancements
|
|
||||||
|
|
||||||
@ -1,272 +0,0 @@
|
|||||||
# Final UI/UX Improvements Summary
|
|
||||||
|
|
||||||
## ✅ All Requested Changes Implemented
|
|
||||||
|
|
||||||
### 1. 🎯 First Mount Point Selected by Default
|
|
||||||
|
|
||||||
**Problem**: Users had to manually select a mount point every time.
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
- ✅ Added `onMounted()` hook in Dashboard
|
|
||||||
- ✅ Automatically selects first available mount point
|
|
||||||
- ✅ Immediate usability - no extra clicks needed
|
|
||||||
|
|
||||||
**Code**:
|
|
||||||
```typescript
|
|
||||||
onMounted(() => {
|
|
||||||
if (props.connection.mountPoints && props.connection.mountPoints.length > 0) {
|
|
||||||
selectedMountPoint.value = props.connection.mountPoints[0].path
|
|
||||||
}
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 🌐 Always Search All Mount Points (Removed Toggle)
|
|
||||||
|
|
||||||
**Problem**: Toggle was confusing and the default should be comprehensive search.
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
- ✅ Removed checkbox toggle from PathSearch
|
|
||||||
- ✅ Always searches across all mount points
|
|
||||||
- ✅ Simplified UI with clear info message
|
|
||||||
- ✅ Updated search tips and messaging
|
|
||||||
|
|
||||||
**Changes**:
|
|
||||||
- Removed `searchAllMounts` reactive variable
|
|
||||||
- Always calls `vaultApi.searchAllMounts()`
|
|
||||||
- Replaced checkbox with informational alert
|
|
||||||
- Updated all UI text to reflect always-on behavior
|
|
||||||
|
|
||||||
### 3. 📋 Secret Viewer Modal with Metadata & History
|
|
||||||
|
|
||||||
**Problem**: Secrets were displayed inline, no metadata or version history.
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
- ✅ Created comprehensive `SecretModal.vue` component
|
|
||||||
- ✅ Tabbed interface: Current Data | Metadata | Versions
|
|
||||||
- ✅ Full metadata display for KV v2 secrets
|
|
||||||
- ✅ Version history with ability to view specific versions
|
|
||||||
- ✅ Copy to clipboard functionality
|
|
||||||
- ✅ Responsive design with proper overflow handling
|
|
||||||
|
|
||||||
**Features**:
|
|
||||||
|
|
||||||
#### 📄 Current Data Tab
|
|
||||||
- JSON formatted secret data
|
|
||||||
- Copy to clipboard button
|
|
||||||
- Syntax highlighting
|
|
||||||
- Scrollable for large secrets
|
|
||||||
|
|
||||||
#### 📋 Metadata Tab (KV v2 only)
|
|
||||||
- General info: current version, max versions, created/updated times
|
|
||||||
- Status: destroyed flag, deletion policies
|
|
||||||
- Custom metadata if present
|
|
||||||
- Raw metadata JSON view
|
|
||||||
|
|
||||||
#### 🕒 Versions Tab (KV v2 only)
|
|
||||||
- Complete version history
|
|
||||||
- Version status (current, destroyed)
|
|
||||||
- Creation and deletion timestamps
|
|
||||||
- "View Version" buttons to load specific versions
|
|
||||||
- Sorted by version (latest first)
|
|
||||||
|
|
||||||
#### 🔒 Security Features
|
|
||||||
- Always fetches fresh data (no caching)
|
|
||||||
- Clear indicators about security practices
|
|
||||||
- KV version awareness (v1 vs v2 features)
|
|
||||||
|
|
||||||
## Technical Implementation Details
|
|
||||||
|
|
||||||
### SecretModal Component Structure
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<div class="modal modal-open">
|
|
||||||
<div class="modal-box max-w-6xl max-h-[90vh]">
|
|
||||||
<!-- Header with path and close button -->
|
|
||||||
|
|
||||||
<!-- Loading/Error states -->
|
|
||||||
|
|
||||||
<!-- Tabbed content -->
|
|
||||||
<div class="tabs tabs-bordered">
|
|
||||||
<button class="tab">📄 Current Data</button>
|
|
||||||
<button class="tab">📋 Metadata</button>
|
|
||||||
<button class="tab">🕒 Versions</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Tab content with proper overflow handling -->
|
|
||||||
|
|
||||||
<!-- Footer with security info -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Integration
|
|
||||||
|
|
||||||
**Secret Data Loading**:
|
|
||||||
```typescript
|
|
||||||
const loadSecret = async () => {
|
|
||||||
// Load current secret data (always fresh)
|
|
||||||
const data = await vaultApi.readSecret(server, credentials, secretPath)
|
|
||||||
|
|
||||||
// Load metadata and versions for KV v2
|
|
||||||
if (server.kvVersion === 2) {
|
|
||||||
await loadMetadataAndVersions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Version Loading**:
|
|
||||||
```typescript
|
|
||||||
const loadVersion = async (version: number) => {
|
|
||||||
// Load specific version with ?version=X parameter
|
|
||||||
const versionPath = `${secretPath}?version=${version}`
|
|
||||||
const data = await vaultApi.readSecret(server, credentials, versionPath)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dashboard Integration
|
|
||||||
|
|
||||||
**Modal Trigger**:
|
|
||||||
```typescript
|
|
||||||
const handleSelectPath = (path: string) => {
|
|
||||||
// Parse path to update form fields
|
|
||||||
// Open modal instead of inline display
|
|
||||||
selectedSecretPath.value = path
|
|
||||||
showSecretModal.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleViewSecret = () => {
|
|
||||||
// Build path from mount point + secret path
|
|
||||||
const fullPath = `${selectedMountPoint.value}/${secretPath.value}`
|
|
||||||
selectedSecretPath.value = fullPath
|
|
||||||
showSecretModal.value = true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## User Experience Improvements
|
|
||||||
|
|
||||||
### 🎯 Streamlined Workflow
|
|
||||||
1. **Login** → First mount point auto-selected
|
|
||||||
2. **Search** → Always comprehensive across all mounts
|
|
||||||
3. **View Secret** → Rich modal with all metadata
|
|
||||||
4. **Browse Versions** → Full history for KV v2
|
|
||||||
|
|
||||||
### 🔍 Enhanced Search Experience
|
|
||||||
- **No configuration needed** - always searches everything
|
|
||||||
- **Clear feedback** - shows which mount points are being searched
|
|
||||||
- **Simplified UI** - removed confusing toggle
|
|
||||||
- **Better results** - mount point shown for each result
|
|
||||||
|
|
||||||
### 📱 Better Mobile Experience
|
|
||||||
- **Large modal** - max-w-6xl for desktop, responsive on mobile
|
|
||||||
- **Scrollable content** - proper overflow handling
|
|
||||||
- **Touch-friendly** - large buttons and touch targets
|
|
||||||
- **Readable text** - appropriate font sizes
|
|
||||||
|
|
||||||
### 🔒 Security Transparency
|
|
||||||
- **Clear indicators** - "Secret data is never cached"
|
|
||||||
- **Fresh data guarantee** - always fetched from Vault
|
|
||||||
- **Version awareness** - shows KV v1 vs v2 capabilities
|
|
||||||
- **Metadata visibility** - full transparency into secret lifecycle
|
|
||||||
|
|
||||||
## Performance Optimizations
|
|
||||||
|
|
||||||
### 🚀 Smart Loading
|
|
||||||
- **Lazy metadata loading** - only for KV v2 secrets
|
|
||||||
- **On-demand versions** - loaded when tab is accessed
|
|
||||||
- **Efficient API calls** - minimal requests for maximum data
|
|
||||||
- **Error handling** - graceful degradation for missing features
|
|
||||||
|
|
||||||
### 💾 Intelligent Caching
|
|
||||||
- **Directory listings** - cached for search performance
|
|
||||||
- **Secret data** - never cached (security)
|
|
||||||
- **Metadata** - not cached (always fresh)
|
|
||||||
- **Mount points** - cached during session
|
|
||||||
|
|
||||||
## Accessibility Improvements
|
|
||||||
|
|
||||||
### ♿ Better Navigation
|
|
||||||
- **Keyboard support** - Enter key works in forms
|
|
||||||
- **Focus management** - proper tab order
|
|
||||||
- **Screen reader friendly** - semantic HTML
|
|
||||||
- **Clear labels** - descriptive text throughout
|
|
||||||
|
|
||||||
### 🎨 Visual Design
|
|
||||||
- **Consistent icons** - 📄 for secrets, 📁 for directories
|
|
||||||
- **Status indicators** - badges for versions, mount points
|
|
||||||
- **Color coding** - success/error/warning states
|
|
||||||
- **Responsive layout** - works on all screen sizes
|
|
||||||
|
|
||||||
## Migration Notes
|
|
||||||
|
|
||||||
### For Existing Users
|
|
||||||
- **No breaking changes** - all existing functionality preserved
|
|
||||||
- **Better defaults** - first mount point selected automatically
|
|
||||||
- **Enhanced features** - modal provides much more information
|
|
||||||
- **Simplified interface** - removed confusing search toggle
|
|
||||||
|
|
||||||
### For Developers
|
|
||||||
- **New component** - `SecretModal.vue` added
|
|
||||||
- **Updated Dashboard** - modal integration
|
|
||||||
- **Simplified PathSearch** - removed toggle complexity
|
|
||||||
- **Enhanced API usage** - better metadata handling
|
|
||||||
|
|
||||||
## Testing Recommendations
|
|
||||||
|
|
||||||
### 🧪 Functional Testing
|
|
||||||
1. **Mount point selection** - verify first mount auto-selected
|
|
||||||
2. **Search behavior** - confirm always searches all mounts
|
|
||||||
3. **Modal functionality** - test all tabs and features
|
|
||||||
4. **Version loading** - test KV v2 version history
|
|
||||||
5. **Error handling** - test with invalid paths/permissions
|
|
||||||
|
|
||||||
### 🔒 Security Testing
|
|
||||||
1. **No secret caching** - verify localStorage contains no secrets
|
|
||||||
2. **Fresh data** - confirm API calls for each secret view
|
|
||||||
3. **Metadata security** - ensure metadata doesn't leak sensitive info
|
|
||||||
4. **Version access** - test permission handling for versions
|
|
||||||
|
|
||||||
### 📱 UI/UX Testing
|
|
||||||
1. **Responsive design** - test on mobile/tablet/desktop
|
|
||||||
2. **Modal behavior** - test scrolling, overflow, closing
|
|
||||||
3. **Keyboard navigation** - test all interactions
|
|
||||||
4. **Loading states** - verify proper feedback during API calls
|
|
||||||
|
|
||||||
## Benefits Summary
|
|
||||||
|
|
||||||
### 🎯 User Experience
|
|
||||||
- **Faster workflow** - auto-selected mount point
|
|
||||||
- **Comprehensive search** - always finds everything
|
|
||||||
- **Rich secret viewing** - metadata and version history
|
|
||||||
- **Better mobile support** - responsive modal design
|
|
||||||
|
|
||||||
### 🔒 Security
|
|
||||||
- **No secret caching** - always fresh data
|
|
||||||
- **Clear transparency** - users know what's cached
|
|
||||||
- **Version control** - full audit trail for KV v2
|
|
||||||
- **Metadata visibility** - complete secret lifecycle
|
|
||||||
|
|
||||||
### 🚀 Performance
|
|
||||||
- **Smart caching** - directories cached, secrets fresh
|
|
||||||
- **Efficient loading** - lazy load metadata/versions
|
|
||||||
- **Responsive UI** - no blocking operations
|
|
||||||
- **Optimized API calls** - minimal requests
|
|
||||||
|
|
||||||
### 👥 Developer Experience
|
|
||||||
- **Clean architecture** - well-separated concerns
|
|
||||||
- **Reusable components** - modal can be extended
|
|
||||||
- **Type safety** - full TypeScript coverage
|
|
||||||
- **Maintainable code** - clear separation of logic
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
✅ **All requested improvements implemented successfully**:
|
|
||||||
|
|
||||||
1. **🎯 First mount point selected by default** - Immediate usability
|
|
||||||
2. **🌐 Always search all mount points** - Comprehensive by default
|
|
||||||
3. **📋 Rich secret modal** - Metadata, versions, and better UX
|
|
||||||
|
|
||||||
The application now provides a **streamlined, comprehensive, and secure** experience for browsing Vault secrets with **professional-grade** metadata visibility and **mobile-friendly** design.
|
|
||||||
|
|
||||||
**Key Achievement**: Transformed from a basic secret reader into a **full-featured Vault browser** with enterprise-level capabilities while maintaining simplicity and security.
|
|
||||||
@ -1,203 +0,0 @@
|
|||||||
# Improvements Summary
|
|
||||||
|
|
||||||
## ✅ Your Feedback Implemented
|
|
||||||
|
|
||||||
### 1. "You should probably use a vault-client instead of the raw api, no?"
|
|
||||||
|
|
||||||
**✅ DONE**: Created proper `VaultClient` class
|
|
||||||
- Browser-compatible Vault HTTP API client
|
|
||||||
- Automatic retries with exponential backoff
|
|
||||||
- Timeout protection
|
|
||||||
- Type-safe operations
|
|
||||||
- Better error handling with `VaultError` class
|
|
||||||
- See: `src/services/vaultClient.ts`
|
|
||||||
|
|
||||||
### 2. "You should make the call on /secret/metadata instead no?"
|
|
||||||
|
|
||||||
**✅ DONE**: Proper KV v1/v2 support with correct paths
|
|
||||||
- **KV v2 LIST operations** now use `/metadata/` endpoint (correct!)
|
|
||||||
- **KV v2 READ/WRITE** operations use `/data/` endpoint
|
|
||||||
- **KV v1** uses direct paths (no prefixes)
|
|
||||||
- Automatic path transformation based on configured KV version
|
|
||||||
- Users can select KV version when adding servers
|
|
||||||
|
|
||||||
## What Changed
|
|
||||||
|
|
||||||
### VaultClient (New File)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Automatically handles KV v1 vs v2 paths
|
|
||||||
const client = new VaultClient({
|
|
||||||
server,
|
|
||||||
credentials,
|
|
||||||
kvVersion: 2 // or 1 for legacy
|
|
||||||
});
|
|
||||||
|
|
||||||
// LIST - uses /metadata/ for KV v2
|
|
||||||
await client.list('secret/myapp/');
|
|
||||||
// KV v2 → GET /v1/secret/metadata/myapp/?list=true ✅
|
|
||||||
// KV v1 → GET /v1/secret/myapp/?list=true
|
|
||||||
|
|
||||||
// READ - uses /data/ for KV v2
|
|
||||||
await client.read('secret/myapp/config');
|
|
||||||
// KV v2 → GET /v1/secret/data/myapp/config ✅
|
|
||||||
// KV v1 → GET /v1/secret/myapp/config
|
|
||||||
```
|
|
||||||
|
|
||||||
### Path Transformation Logic
|
|
||||||
|
|
||||||
**KV v2:**
|
|
||||||
- `list('secret/myapp')` → `secret/metadata/myapp` ✅
|
|
||||||
- `read('secret/myapp')` → `secret/data/myapp` ✅
|
|
||||||
- `write('secret/myapp')` → `secret/data/myapp` ✅
|
|
||||||
|
|
||||||
**KV v1:**
|
|
||||||
- All operations use paths as-is (no transformation)
|
|
||||||
|
|
||||||
### UI Updates
|
|
||||||
|
|
||||||
**Server Configuration:**
|
|
||||||
- Added KV version selector when adding servers
|
|
||||||
- Default: KV v2 (most common)
|
|
||||||
- Option: KV v1 (for legacy systems)
|
|
||||||
- Badge showing KV version on each server card
|
|
||||||
|
|
||||||
### New Features
|
|
||||||
|
|
||||||
1. **Automatic Path Handling**
|
|
||||||
- No need to manually add `/data/` or `/metadata/`
|
|
||||||
- Client handles it based on operation and KV version
|
|
||||||
|
|
||||||
2. **KV Version Detection**
|
|
||||||
- `client.detectKvVersion('secret')` - auto-detect if needed
|
|
||||||
|
|
||||||
3. **Metadata Operations** (KV v2 only)
|
|
||||||
- `client.readMetadata(path)` - get version history
|
|
||||||
- Returns versions, creation times, etc.
|
|
||||||
|
|
||||||
4. **Better Error Messages**
|
|
||||||
- "no handler for route" → suggests checking KV version
|
|
||||||
- Includes status codes and Vault error details
|
|
||||||
|
|
||||||
## File Changes
|
|
||||||
|
|
||||||
### New Files
|
|
||||||
- ✅ `src/services/vaultClient.ts` - Core Vault client
|
|
||||||
- ✅ `KV_VERSIONS.md` - Comprehensive KV v1/v2 guide
|
|
||||||
- ✅ `CORS_AND_CLIENT.md` - CORS and architecture docs
|
|
||||||
- ✅ `CHANGELOG.md` - Detailed changelog
|
|
||||||
|
|
||||||
### Modified Files
|
|
||||||
- ✅ `src/services/vaultApi.ts` - Now uses VaultClient
|
|
||||||
- ✅ `src/types.ts` - Added `kvVersion` to VaultServer
|
|
||||||
- ✅ `src/components/ServerSelector.tsx` - KV version selector
|
|
||||||
- ✅ `src/components/ServerSelector.css` - Badge styling
|
|
||||||
- ✅ `src/components/Dashboard.tsx` - Better error handling
|
|
||||||
|
|
||||||
## Why These Changes Matter
|
|
||||||
|
|
||||||
### ❌ Before (Problems)
|
|
||||||
```typescript
|
|
||||||
// Manual CORS headers (doesn't work)
|
|
||||||
headers: {
|
|
||||||
'Access-Control-Allow-Origin': '*' // ❌ Ignored by browser
|
|
||||||
}
|
|
||||||
|
|
||||||
// no-cors mode (breaks response reading)
|
|
||||||
mode: 'no-cors' // ❌ Can't read response body
|
|
||||||
|
|
||||||
// Raw API calls
|
|
||||||
fetch(`${url}/v1/${path}`) // ❌ No retries, timeouts, error handling
|
|
||||||
|
|
||||||
// Wrong paths for KV v2
|
|
||||||
fetch(`${url}/v1/secret/myapp?list=true`) // ❌ Should use /metadata/
|
|
||||||
```
|
|
||||||
|
|
||||||
### ✅ After (Solutions)
|
|
||||||
```typescript
|
|
||||||
// Proper Vault client
|
|
||||||
const client = new VaultClient({
|
|
||||||
server,
|
|
||||||
credentials,
|
|
||||||
kvVersion: 2,
|
|
||||||
timeout: 30000,
|
|
||||||
retries: 2
|
|
||||||
});
|
|
||||||
|
|
||||||
// Automatic path transformation
|
|
||||||
await client.list('secret/myapp');
|
|
||||||
// → Uses /metadata/ for KV v2 ✅
|
|
||||||
// → Uses direct path for KV v1 ✅
|
|
||||||
|
|
||||||
// Better errors
|
|
||||||
try {
|
|
||||||
await client.read(path);
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof VaultError) {
|
|
||||||
console.log(error.statusCode); // 403, 404, etc.
|
|
||||||
console.log(error.errors); // Detailed Vault errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [x] VaultClient compiles without errors
|
|
||||||
- [x] Path transformation for KV v1
|
|
||||||
- [x] Path transformation for KV v2
|
|
||||||
- [x] LIST uses /metadata/ for KV v2 ✅
|
|
||||||
- [x] READ uses /data/ for KV v2 ✅
|
|
||||||
- [x] WRITE uses /data/ for KV v2 ✅
|
|
||||||
- [x] UI shows KV version selector
|
|
||||||
- [x] UI shows KV version badge on servers
|
|
||||||
- [x] Better error messages
|
|
||||||
- [ ] Manual testing with real Vault KV v1
|
|
||||||
- [ ] Manual testing with real Vault KV v2
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
Comprehensive documentation added:
|
|
||||||
|
|
||||||
1. **`KV_VERSIONS.md`**
|
|
||||||
- KV v1 vs v2 comparison
|
|
||||||
- Path structure explained
|
|
||||||
- When to use each version
|
|
||||||
- Troubleshooting guide
|
|
||||||
|
|
||||||
2. **`CORS_AND_CLIENT.md`**
|
|
||||||
- Why client-side CORS headers don't work
|
|
||||||
- Why `mode: 'no-cors'` breaks things
|
|
||||||
- Proper Vault CORS configuration
|
|
||||||
- VaultClient architecture benefits
|
|
||||||
|
|
||||||
3. **`CHANGELOG.md`**
|
|
||||||
- Detailed list of changes
|
|
||||||
- Before/after comparisons
|
|
||||||
- Migration guide
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
### Your Feedback ✅
|
|
||||||
|
|
||||||
1. ✅ **"Use a vault-client instead of raw API"**
|
|
||||||
- Created proper VaultClient class
|
|
||||||
- Production-ready with retries, timeouts, error handling
|
|
||||||
|
|
||||||
2. ✅ **"Make call on /secret/metadata"**
|
|
||||||
- LIST operations use `/metadata/` for KV v2
|
|
||||||
- READ/WRITE use `/data/` for KV v2
|
|
||||||
- Automatic path transformation
|
|
||||||
- Support for both KV v1 and v2
|
|
||||||
|
|
||||||
### Benefits
|
|
||||||
|
|
||||||
- 🎯 **Correct API endpoints** for KV v2
|
|
||||||
- 🔄 **Automatic retries** on failures
|
|
||||||
- ⏱️ **Timeout protection** prevents hanging
|
|
||||||
- 🛡️ **Better error handling** with detailed messages
|
|
||||||
- 🎨 **Clean API** - same code for v1 and v2
|
|
||||||
- 📚 **Comprehensive docs** explaining everything
|
|
||||||
- ✅ **Type-safe** with full TypeScript support
|
|
||||||
|
|
||||||
The application now properly handles both KV versions with the correct endpoints! 🎉
|
|
||||||
|
|
||||||
@ -1,259 +0,0 @@
|
|||||||
# KV v2 Enforcement - Simplification Complete
|
|
||||||
|
|
||||||
## ✅ All Changes Implemented
|
|
||||||
|
|
||||||
The application now **enforces KV v2** throughout, removing the complexity of supporting both KV v1 and v2. This simplifies the codebase and UI while focusing on the modern Vault standard.
|
|
||||||
|
|
||||||
## Changes Made
|
|
||||||
|
|
||||||
### 1. 🗑️ Removed KV Version Selection from UI
|
|
||||||
|
|
||||||
**ServerSelector.vue**:
|
|
||||||
- ✅ Removed KV version dropdown from "Add Server" form
|
|
||||||
- ✅ Removed `kvVersion` from `newServer` state
|
|
||||||
- ✅ Updated server cards to show static "KV v2" badge
|
|
||||||
- ✅ Simplified form validation and submission
|
|
||||||
|
|
||||||
**Before**:
|
|
||||||
```vue
|
|
||||||
<select v-model="newServer.kvVersion">
|
|
||||||
<option :value="2">KV v2 (recommended)</option>
|
|
||||||
<option :value="1">KV v1 (legacy)</option>
|
|
||||||
</select>
|
|
||||||
```
|
|
||||||
|
|
||||||
**After**:
|
|
||||||
```vue
|
|
||||||
<!-- KV v2 is enforced - no version selection needed -->
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 🔧 Updated Type Definitions
|
|
||||||
|
|
||||||
**types.ts**:
|
|
||||||
- ✅ Removed `kvVersion?: 1 | 2` from `VaultServer` interface
|
|
||||||
- ✅ Added comment explaining KV v2 enforcement
|
|
||||||
- ✅ Simplified server object structure
|
|
||||||
|
|
||||||
**Before**:
|
|
||||||
```typescript
|
|
||||||
export interface VaultServer {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
description?: string;
|
|
||||||
kvVersion?: 1 | 2; // KV secret engine version (default: 2)
|
|
||||||
savedCredentials?: VaultCredentials;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**After**:
|
|
||||||
```typescript
|
|
||||||
export interface VaultServer {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
description?: string;
|
|
||||||
// KV v2 is enforced - no version selection needed
|
|
||||||
savedCredentials?: VaultCredentials;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. ⚙️ Simplified VaultApi Service
|
|
||||||
|
|
||||||
**vaultApi.ts**:
|
|
||||||
- ✅ Removed `kvVersion` parameter from `createClient()`
|
|
||||||
- ✅ Hard-coded `kvVersion: 2` in VaultClient constructor
|
|
||||||
- ✅ Updated all method calls to remove version parameter
|
|
||||||
- ✅ Simplified `searchAllMounts()` logic
|
|
||||||
|
|
||||||
**Before**:
|
|
||||||
```typescript
|
|
||||||
private createClient(
|
|
||||||
server: VaultServer,
|
|
||||||
credentials: VaultCredentials,
|
|
||||||
kvVersion: 1 | 2 = 2
|
|
||||||
): VaultClient {
|
|
||||||
return new VaultClient({
|
|
||||||
server,
|
|
||||||
credentials,
|
|
||||||
timeout: 30000,
|
|
||||||
retries: 2,
|
|
||||||
kvVersion, // KV v2 by default (most common)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**After**:
|
|
||||||
```typescript
|
|
||||||
private createClient(
|
|
||||||
server: VaultServer,
|
|
||||||
credentials: VaultCredentials
|
|
||||||
): VaultClient {
|
|
||||||
return new VaultClient({
|
|
||||||
server,
|
|
||||||
credentials,
|
|
||||||
timeout: 30000,
|
|
||||||
retries: 2,
|
|
||||||
kvVersion: 2, // KV v2 is enforced
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 📋 Updated Dashboard Display
|
|
||||||
|
|
||||||
**Dashboard.vue**:
|
|
||||||
- ✅ Mount point selector shows static "v2" for all mounts
|
|
||||||
- ✅ Removed conditional version display logic
|
|
||||||
- ✅ Simplified mount point rendering
|
|
||||||
|
|
||||||
**Before**:
|
|
||||||
```vue
|
|
||||||
{{ mount.path }}/ ({{ mount.type }} v{{ mount.options?.version || '1' }})
|
|
||||||
```
|
|
||||||
|
|
||||||
**After**:
|
|
||||||
```vue
|
|
||||||
{{ mount.path }}/ ({{ mount.type }} v2)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 🔐 Simplified SecretModal
|
|
||||||
|
|
||||||
**SecretModal.vue**:
|
|
||||||
- ✅ Always loads metadata and versions (no KV version check)
|
|
||||||
- ✅ Removed conditional metadata loading logic
|
|
||||||
- ✅ Updated footer text to reflect KV v2 enforcement
|
|
||||||
- ✅ Simplified error messages
|
|
||||||
|
|
||||||
**Before**:
|
|
||||||
```typescript
|
|
||||||
// Try to load metadata and versions (KV v2 only)
|
|
||||||
if (props.server.kvVersion === 2) {
|
|
||||||
await loadMetadataAndVersions();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**After**:
|
|
||||||
```typescript
|
|
||||||
// Load metadata and versions (KV v2 enforced)
|
|
||||||
await loadMetadataAndVersions();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Benefits of KV v2 Enforcement
|
|
||||||
|
|
||||||
### 🎯 Simplified User Experience
|
|
||||||
- **No confusing choices** - users don't need to know about KV versions
|
|
||||||
- **Consistent behavior** - all features work the same way
|
|
||||||
- **Modern defaults** - KV v2 is the current Vault standard
|
|
||||||
- **Reduced cognitive load** - fewer options to understand
|
|
||||||
|
|
||||||
### 🔧 Cleaner Codebase
|
|
||||||
- **Less complexity** - no conditional logic for versions
|
|
||||||
- **Fewer parameters** - simplified method signatures
|
|
||||||
- **Better maintainability** - single code path to maintain
|
|
||||||
- **Reduced testing surface** - fewer edge cases
|
|
||||||
|
|
||||||
### 📊 Enhanced Features
|
|
||||||
- **Always available metadata** - version history, timestamps, etc.
|
|
||||||
- **Consistent API paths** - `/data/` and `/metadata/` endpoints
|
|
||||||
- **Better secret management** - soft deletes, version control
|
|
||||||
- **Audit capabilities** - full version history tracking
|
|
||||||
|
|
||||||
### 🚀 Performance Benefits
|
|
||||||
- **No version detection** - faster mount point processing
|
|
||||||
- **Optimized API calls** - direct KV v2 endpoint usage
|
|
||||||
- **Simplified caching** - consistent cache key generation
|
|
||||||
- **Reduced overhead** - no version-specific logic
|
|
||||||
|
|
||||||
## Technical Implementation
|
|
||||||
|
|
||||||
### API Endpoint Usage
|
|
||||||
All secret operations now use KV v2 endpoints:
|
|
||||||
- **List**: `/v1/{mount}/metadata/{path}?list=true`
|
|
||||||
- **Read**: `/v1/{mount}/data/{path}`
|
|
||||||
- **Write**: `/v1/{mount}/data/{path}`
|
|
||||||
- **Delete**: `/v1/{mount}/data/{path}`
|
|
||||||
- **Metadata**: `/v1/{mount}/metadata/{path}`
|
|
||||||
|
|
||||||
### Mount Point Detection
|
|
||||||
Mount points are detected via `/v1/sys/internal/ui/mounts` and filtered for:
|
|
||||||
- `type === 'kv'` (KV secret engines)
|
|
||||||
- `type === 'generic'` (legacy KV engines)
|
|
||||||
|
|
||||||
All detected KV mounts are treated as v2.
|
|
||||||
|
|
||||||
### Secret Modal Features
|
|
||||||
With KV v2 enforcement, the SecretModal always provides:
|
|
||||||
- **Current secret data** with JSON formatting
|
|
||||||
- **Complete metadata** including versions, timestamps
|
|
||||||
- **Version history** with ability to view any version
|
|
||||||
- **Audit trail** showing creation/modification times
|
|
||||||
|
|
||||||
## Migration Notes
|
|
||||||
|
|
||||||
### For Existing Users
|
|
||||||
- **No data loss** - existing server configurations preserved
|
|
||||||
- **Automatic upgrade** - all servers now treated as KV v2
|
|
||||||
- **Enhanced features** - metadata and versions now always available
|
|
||||||
- **Simplified interface** - no version selection needed
|
|
||||||
|
|
||||||
### For Vault Administrators
|
|
||||||
- **KV v2 required** - ensure all secret engines are KV v2
|
|
||||||
- **Migration path** - upgrade KV v1 engines to v2 if needed
|
|
||||||
- **Feature compatibility** - all advanced features require KV v2
|
|
||||||
|
|
||||||
### Vault KV v1 to v2 Migration
|
|
||||||
If you have KV v1 engines, upgrade them:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Enable KV v2 engine
|
|
||||||
vault secrets enable -path=secret -version=2 kv
|
|
||||||
|
|
||||||
# Migrate data from v1 to v2 (manual process)
|
|
||||||
# Note: This requires custom scripting as there's no direct migration
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
### KV v1 Compatibility
|
|
||||||
If the application encounters a KV v1 engine:
|
|
||||||
- **Metadata loading** will fail gracefully
|
|
||||||
- **Basic secret reading** will still work
|
|
||||||
- **Version history** will be unavailable
|
|
||||||
- **User feedback** indicates KV v2 features missing
|
|
||||||
|
|
||||||
### Graceful Degradation
|
|
||||||
The application handles KV v1 engines by:
|
|
||||||
- Showing error messages for metadata operations
|
|
||||||
- Continuing to function for basic secret operations
|
|
||||||
- Providing clear feedback about missing features
|
|
||||||
- Suggesting KV v2 upgrade in error messages
|
|
||||||
|
|
||||||
## Future Considerations
|
|
||||||
|
|
||||||
### Potential Enhancements
|
|
||||||
With KV v2 enforcement, future features could include:
|
|
||||||
- **Secret versioning UI** - visual diff between versions
|
|
||||||
- **Rollback functionality** - restore previous versions
|
|
||||||
- **Metadata editing** - custom metadata management
|
|
||||||
- **Deletion policies** - configure auto-deletion rules
|
|
||||||
- **Secret templates** - predefined secret structures
|
|
||||||
|
|
||||||
### Advanced KV v2 Features
|
|
||||||
Could be implemented:
|
|
||||||
- **Check-and-set operations** - prevent concurrent modifications
|
|
||||||
- **Secret patching** - partial updates to secrets
|
|
||||||
- **Metadata-only operations** - manage metadata without reading secrets
|
|
||||||
- **Bulk operations** - batch secret management
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
✅ **KV v2 enforcement successfully implemented**:
|
|
||||||
|
|
||||||
1. **🗑️ Removed version selection** - simplified UI
|
|
||||||
2. **🔧 Updated all services** - consistent KV v2 usage
|
|
||||||
3. **📋 Enhanced features** - metadata always available
|
|
||||||
4. **🚀 Improved performance** - optimized for single version
|
|
||||||
|
|
||||||
The application now provides a **streamlined, modern experience** focused on KV v2's advanced capabilities while maintaining **backward compatibility** through graceful degradation.
|
|
||||||
|
|
||||||
**Key Achievement**: Transformed from a dual-version system to a **focused, feature-rich KV v2 application** with enhanced metadata, versioning, and audit capabilities.
|
|
||||||
318
KV_VERSIONS.md
318
KV_VERSIONS.md
@ -1,318 +0,0 @@
|
|||||||
# HashiCorp Vault KV Secret Engine Versions
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
HashiCorp Vault has two versions of the Key-Value (KV) secret engine:
|
|
||||||
- **KV v1**: Simple key-value storage (legacy)
|
|
||||||
- **KV v2**: Versioned secrets with metadata (recommended)
|
|
||||||
|
|
||||||
This application supports both versions and automatically handles the path differences.
|
|
||||||
|
|
||||||
## Key Differences
|
|
||||||
|
|
||||||
| Feature | KV v1 | KV v2 |
|
|
||||||
|---------|-------|-------|
|
|
||||||
| **Versioning** | ❌ No | ✅ Yes |
|
|
||||||
| **Metadata** | ❌ No | ✅ Yes |
|
|
||||||
| **Soft Delete** | ❌ No | ✅ Yes |
|
|
||||||
| **Path Structure** | Simple | Nested (data/metadata) |
|
|
||||||
| **API Calls** | Direct | Through /data or /metadata |
|
|
||||||
| **Rollback** | ❌ No | ✅ Yes |
|
|
||||||
| **Check-and-Set** | ❌ No | ✅ Yes |
|
|
||||||
|
|
||||||
## Path Structures
|
|
||||||
|
|
||||||
### KV v1
|
|
||||||
```
|
|
||||||
secret/myapp/database
|
|
||||||
secret/myapp/api-keys
|
|
||||||
secret/team/config
|
|
||||||
```
|
|
||||||
|
|
||||||
Simple, direct paths. What you see is what you get.
|
|
||||||
|
|
||||||
### KV v2
|
|
||||||
```
|
|
||||||
secret/data/myapp/database # For reading/writing secrets
|
|
||||||
secret/metadata/myapp/database # For metadata operations
|
|
||||||
```
|
|
||||||
|
|
||||||
KV v2 uses special path prefixes:
|
|
||||||
- `/data/` for reading and writing secret data
|
|
||||||
- `/metadata/` for listing and metadata operations
|
|
||||||
|
|
||||||
## How Our Client Handles This
|
|
||||||
|
|
||||||
### Automatic Path Transformation
|
|
||||||
|
|
||||||
Our `VaultClient` automatically transforms paths based on the KV version:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// You write this:
|
|
||||||
await client.read('secret/myapp/database');
|
|
||||||
|
|
||||||
// KV v1: Uses path as-is
|
|
||||||
// → GET /v1/secret/myapp/database
|
|
||||||
|
|
||||||
// KV v2: Automatically adds /data/
|
|
||||||
// → GET /v1/secret/data/myapp/database
|
|
||||||
```
|
|
||||||
|
|
||||||
### List Operations
|
|
||||||
|
|
||||||
**KV v1:**
|
|
||||||
```typescript
|
|
||||||
await client.list('secret/myapp/');
|
|
||||||
// → LIST /v1/secret/myapp/?list=true
|
|
||||||
```
|
|
||||||
|
|
||||||
**KV v2:**
|
|
||||||
```typescript
|
|
||||||
await client.list('secret/myapp/');
|
|
||||||
// → LIST /v1/secret/metadata/myapp/?list=true
|
|
||||||
// (uses /metadata/ for listing)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Read Operations
|
|
||||||
|
|
||||||
**KV v1:**
|
|
||||||
```typescript
|
|
||||||
const data = await client.read('secret/myapp/config');
|
|
||||||
// Returns: { username: '...', password: '...' }
|
|
||||||
```
|
|
||||||
|
|
||||||
**KV v2:**
|
|
||||||
```typescript
|
|
||||||
const data = await client.read('secret/myapp/config');
|
|
||||||
// Automatically unwraps: data.data.data → { username: '...', password: '...' }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Write Operations
|
|
||||||
|
|
||||||
**KV v1:**
|
|
||||||
```typescript
|
|
||||||
await client.write('secret/myapp/config', {
|
|
||||||
username: 'admin',
|
|
||||||
password: 'secret'
|
|
||||||
});
|
|
||||||
// POST /v1/secret/myapp/config
|
|
||||||
// Body: { username: '...', password: '...' }
|
|
||||||
```
|
|
||||||
|
|
||||||
**KV v2:**
|
|
||||||
```typescript
|
|
||||||
await client.write('secret/myapp/config', {
|
|
||||||
username: 'admin',
|
|
||||||
password: 'secret'
|
|
||||||
});
|
|
||||||
// POST /v1/secret/data/myapp/config
|
|
||||||
// Body: { data: { username: '...', password: '...' } }
|
|
||||||
// (wrapped in data object)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuring KV Version
|
|
||||||
|
|
||||||
### In the UI
|
|
||||||
|
|
||||||
When adding a Vault server:
|
|
||||||
|
|
||||||
1. Click "Add Server"
|
|
||||||
2. Fill in the server details
|
|
||||||
3. Select **KV Secret Engine Version**:
|
|
||||||
- **KV v2 (recommended)** - Default, most common
|
|
||||||
- **KV v1 (legacy)** - For older Vault installations
|
|
||||||
|
|
||||||
### Detecting KV Version
|
|
||||||
|
|
||||||
If you're unsure which version your Vault uses:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check your Vault server
|
|
||||||
vault secrets list -detailed
|
|
||||||
|
|
||||||
# Look for the "Options" column
|
|
||||||
# version=2 means KV v2
|
|
||||||
# No version or version=1 means KV v1
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use the API:
|
|
||||||
```bash
|
|
||||||
curl -H "X-Vault-Token: $VAULT_TOKEN" \
|
|
||||||
$VAULT_ADDR/v1/sys/internal/ui/mounts/secret
|
|
||||||
```
|
|
||||||
|
|
||||||
Look for `"options": {"version": "2"}` in the response.
|
|
||||||
|
|
||||||
## When to Use KV v1 vs KV v2
|
|
||||||
|
|
||||||
### Use KV v2 (Recommended) When:
|
|
||||||
- ✅ Starting a new Vault installation
|
|
||||||
- ✅ You need versioning and rollback
|
|
||||||
- ✅ You want soft delete (undelete capability)
|
|
||||||
- ✅ You need to track secret history
|
|
||||||
- ✅ You want check-and-set operations
|
|
||||||
|
|
||||||
### Use KV v1 Only When:
|
|
||||||
- Legacy Vault installation that can't be upgraded
|
|
||||||
- Specific requirement to not version secrets
|
|
||||||
- Very simple use case without versioning needs
|
|
||||||
|
|
||||||
## Example Workflows
|
|
||||||
|
|
||||||
### Reading a Secret
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Same code works for both versions!
|
|
||||||
const data = await client.read('secret/myapp/database');
|
|
||||||
console.log(data.username);
|
|
||||||
console.log(data.password);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Listing Secrets
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Same code works for both versions!
|
|
||||||
const keys = await client.list('secret/myapp/');
|
|
||||||
console.log(keys);
|
|
||||||
// ['config', 'database/', 'api-keys/']
|
|
||||||
```
|
|
||||||
|
|
||||||
### Searching Recursively
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Uses list() internally, works with both versions
|
|
||||||
const results = await vaultApi.searchPaths(
|
|
||||||
server,
|
|
||||||
credentials,
|
|
||||||
'secret/',
|
|
||||||
'database'
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
## KV v2 Specific Features
|
|
||||||
|
|
||||||
### Metadata Operations
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Only available in KV v2
|
|
||||||
const metadata = await client.readMetadata('secret/myapp/database');
|
|
||||||
|
|
||||||
console.log(metadata.current_version); // 5
|
|
||||||
console.log(metadata.versions);
|
|
||||||
// {
|
|
||||||
// "1": { created_time: "...", destroyed: false },
|
|
||||||
// "2": { created_time: "...", destroyed: false },
|
|
||||||
// "3": { created_time: "...", destroyed: true },
|
|
||||||
// "4": { created_time: "...", destroyed: false },
|
|
||||||
// "5": { created_time: "...", destroyed: false }
|
|
||||||
// }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Version History
|
|
||||||
|
|
||||||
KV v2 keeps track of all versions:
|
|
||||||
- Can read previous versions
|
|
||||||
- Can undelete soft-deleted secrets
|
|
||||||
- Can permanently destroy specific versions
|
|
||||||
- Can destroy all versions
|
|
||||||
|
|
||||||
### Soft Delete vs Hard Delete
|
|
||||||
|
|
||||||
**Soft Delete (KV v2):**
|
|
||||||
```typescript
|
|
||||||
await client.delete('secret/myapp/database');
|
|
||||||
// Secret is "deleted" but can be undeleted
|
|
||||||
// Metadata still exists
|
|
||||||
```
|
|
||||||
|
|
||||||
**Hard Delete (KV v1):**
|
|
||||||
```typescript
|
|
||||||
await client.delete('secret/myapp/database');
|
|
||||||
// Secret is permanently gone
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Error: "no handler for route"
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: Vault API error: no handler for route 'secret/data/...'
|
|
||||||
```
|
|
||||||
|
|
||||||
**Cause**: Your Vault is using KV v1, but the client is configured for KV v2.
|
|
||||||
|
|
||||||
**Solution**: Edit the server configuration and change KV version to v1.
|
|
||||||
|
|
||||||
### Error: "1 error occurred: * permission denied"
|
|
||||||
|
|
||||||
This can happen if:
|
|
||||||
1. You don't have permission to the path
|
|
||||||
2. You're using the wrong KV version (v1 paths on v2 or vice versa)
|
|
||||||
|
|
||||||
**Solution**:
|
|
||||||
- Check your Vault policies
|
|
||||||
- Verify the KV version in server settings
|
|
||||||
|
|
||||||
### Paths Look Wrong
|
|
||||||
|
|
||||||
If you see paths like `secret/data/data/myapp`:
|
|
||||||
|
|
||||||
**Cause**: You're manually adding `/data/` when the client already does it for KV v2.
|
|
||||||
|
|
||||||
**Solution**: Use simple paths like `secret/myapp`. The client adds `/data/` or `/metadata/` automatically.
|
|
||||||
|
|
||||||
## Migration: KV v1 → KV v2
|
|
||||||
|
|
||||||
If you're migrating from KV v1 to KV v2:
|
|
||||||
|
|
||||||
1. **Backup all secrets** from KV v1
|
|
||||||
2. **Enable KV v2** on a new mount point
|
|
||||||
3. **Migrate secrets** to new paths
|
|
||||||
4. **Update application** to use new KV version
|
|
||||||
5. **Test thoroughly**
|
|
||||||
6. **Switch traffic** to new mount
|
|
||||||
|
|
||||||
In this GUI:
|
|
||||||
1. Add a new server entry with the same URL
|
|
||||||
2. Set KV version to v2
|
|
||||||
3. Use new mount path (e.g., `secretv2/` instead of `secret/`)
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Use KV v2 for new installations**
|
|
||||||
2. **Configure correct version when adding servers**
|
|
||||||
3. **Don't mix KV v1 and v2 mount points** on same server without proper labeling
|
|
||||||
4. **Use simple paths** - let the client handle /data/ and /metadata/ prefixes
|
|
||||||
5. **Document which mounts use which version** for your team
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
All examples work transparently with both versions:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// List
|
|
||||||
const keys = await client.list('secret/myapp/');
|
|
||||||
|
|
||||||
// Read
|
|
||||||
const data = await client.read('secret/myapp/config');
|
|
||||||
|
|
||||||
// Write
|
|
||||||
await client.write('secret/myapp/config', { key: 'value' });
|
|
||||||
|
|
||||||
// Delete
|
|
||||||
await client.delete('secret/myapp/config');
|
|
||||||
|
|
||||||
// Metadata (KV v2 only)
|
|
||||||
const meta = await client.readMetadata('secret/myapp/config');
|
|
||||||
```
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
✅ **Both versions are fully supported**
|
|
||||||
✅ **Paths are automatically transformed**
|
|
||||||
✅ **Select correct version when adding server**
|
|
||||||
✅ **Use KV v2 for new installations**
|
|
||||||
✅ **Code is the same for both versions**
|
|
||||||
|
|
||||||
The client handles all the complexity, so you can focus on managing your secrets!
|
|
||||||
|
|
||||||
@ -1,364 +0,0 @@
|
|||||||
# Latest Features - Mount Point Detection & Multi-Mount Search
|
|
||||||
|
|
||||||
## 🎉 What's New
|
|
||||||
|
|
||||||
### 1. Login Verification ✅
|
|
||||||
|
|
||||||
**Before:** Login was assumed to work, no verification
|
|
||||||
|
|
||||||
**Now:** Login is verified by calling `/v1/sys/internal/ui/mounts`
|
|
||||||
- ✅ Confirms credentials are valid
|
|
||||||
- ✅ Provides immediate feedback
|
|
||||||
- ✅ Shows detailed error messages on failure
|
|
||||||
- ✅ Fails fast if credentials are invalid
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// On login:
|
|
||||||
const mountPoints = await vaultApi.verifyLoginAndGetMounts(server, credentials);
|
|
||||||
// ✓ Login verified
|
|
||||||
// ✓ Mount points detected
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Automatic Mount Point Discovery 🔍
|
|
||||||
|
|
||||||
**Detects all KV secret engine mount points:**
|
|
||||||
- Queries `/v1/sys/internal/ui/mounts` on login
|
|
||||||
- Filters for KV secret engines only (`kv` or `generic` type)
|
|
||||||
- Auto-detects KV version (v1 or v2) from mount options
|
|
||||||
- Stores mount points in connection state
|
|
||||||
|
|
||||||
**Example Console Output:**
|
|
||||||
```
|
|
||||||
⚡ Verifying login and fetching mount points...
|
|
||||||
✓ Found 3 KV mount point(s): ["secret", "secret-v1", "team-secrets"]
|
|
||||||
✓ Logged in successfully.
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Search Across All Mounts 🚀
|
|
||||||
|
|
||||||
**New Feature:** Optional multi-mount search
|
|
||||||
|
|
||||||
**UI Changes:**
|
|
||||||
- Checkbox: "Search across all mount points (N available)"
|
|
||||||
- Shows number of detected mount points
|
|
||||||
- Disabled when no mount points available
|
|
||||||
- **Off by default** (single path search)
|
|
||||||
|
|
||||||
**When Enabled:**
|
|
||||||
- Searches all detected KV mount points
|
|
||||||
- Each mount searched with correct KV version
|
|
||||||
- Results show mount point indicator: 📌
|
|
||||||
- Continues even if some mounts fail (permission denied)
|
|
||||||
|
|
||||||
**Search Results:**
|
|
||||||
```
|
|
||||||
📄 secret/prod/database/credentials
|
|
||||||
📌 secret
|
|
||||||
Depth: 2
|
|
||||||
|
|
||||||
📄 team-secrets/shared/database
|
|
||||||
📌 team-secrets
|
|
||||||
Depth: 1
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Intelligent KV Version Handling
|
|
||||||
|
|
||||||
**Per-mount KV version detection:**
|
|
||||||
```typescript
|
|
||||||
// Each mount can have different KV version
|
|
||||||
secret/ → KV v2 (uses /metadata/ for LIST)
|
|
||||||
secret-v1/ → KV v1 (direct LIST)
|
|
||||||
team-secrets/ → KV v2 (uses /metadata/ for LIST)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Automatic path transformation:**
|
|
||||||
- KV v2: `secret/` → `secret/metadata/` for LIST
|
|
||||||
- KV v2: `secret/` → `secret/data/` for READ/WRITE
|
|
||||||
- KV v1: `secret/` → `secret/` (no transformation)
|
|
||||||
|
|
||||||
## 📊 Technical Changes
|
|
||||||
|
|
||||||
### New API Methods
|
|
||||||
|
|
||||||
**`VaultClient.listMounts()`**
|
|
||||||
```typescript
|
|
||||||
const mounts = await client.listMounts();
|
|
||||||
// Returns all mount points with metadata
|
|
||||||
```
|
|
||||||
|
|
||||||
**`vaultApi.verifyLoginAndGetMounts()`**
|
|
||||||
```typescript
|
|
||||||
const mountPoints = await vaultApi.verifyLoginAndGetMounts(server, credentials);
|
|
||||||
// Verifies login + returns filtered KV mount points
|
|
||||||
```
|
|
||||||
|
|
||||||
**`vaultApi.searchAllMounts()`**
|
|
||||||
```typescript
|
|
||||||
const results = await vaultApi.searchAllMounts(
|
|
||||||
server,
|
|
||||||
credentials,
|
|
||||||
mountPoints,
|
|
||||||
searchTerm
|
|
||||||
);
|
|
||||||
// Searches across all provided mount points
|
|
||||||
```
|
|
||||||
|
|
||||||
### Updated Types
|
|
||||||
|
|
||||||
**`MountPoint` interface:**
|
|
||||||
```typescript
|
|
||||||
interface MountPoint {
|
|
||||||
path: string; // "secret"
|
|
||||||
type: string; // "kv"
|
|
||||||
description: string; // "key/value secret storage"
|
|
||||||
accessor: string; // "kv_abc123"
|
|
||||||
config: { ... };
|
|
||||||
options: {
|
|
||||||
version?: string; // "2" for KV v2
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**`VaultConnection` interface:**
|
|
||||||
```typescript
|
|
||||||
interface VaultConnection {
|
|
||||||
server: VaultServer;
|
|
||||||
credentials: VaultCredentials;
|
|
||||||
isConnected: boolean;
|
|
||||||
lastConnected?: Date;
|
|
||||||
mountPoints?: MountPoint[]; // ← NEW
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**`SearchResult` interface:**
|
|
||||||
```typescript
|
|
||||||
interface SearchResult {
|
|
||||||
path: string;
|
|
||||||
isDirectory: boolean;
|
|
||||||
depth: number;
|
|
||||||
mountPoint?: string; // ← NEW
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### File Changes
|
|
||||||
|
|
||||||
**Modified:**
|
|
||||||
- ✅ `src/types.ts` - Added MountPoint, updated VaultConnection
|
|
||||||
- ✅ `src/services/vaultClient.ts` - Added listMounts()
|
|
||||||
- ✅ `src/services/vaultApi.ts` - Added verifyLoginAndGetMounts(), searchAllMounts()
|
|
||||||
- ✅ `src/components/PathSearch.tsx` - Added multi-mount search UI
|
|
||||||
- ✅ `src/components/PathSearch.css` - Styles for new UI elements
|
|
||||||
- ✅ `src/components/Dashboard.tsx` - Pass mountPoints to PathSearch
|
|
||||||
- ✅ `src/App.tsx` - Call verifyLoginAndGetMounts() on login
|
|
||||||
|
|
||||||
**New:**
|
|
||||||
- ✅ `MOUNT_POINTS.md` - Comprehensive documentation
|
|
||||||
|
|
||||||
## 🎯 Use Cases
|
|
||||||
|
|
||||||
### Use Case 1: I Don't Know Which Mount
|
|
||||||
|
|
||||||
**Scenario:** "I need the database credentials, but I don't know if they're in `secret/` or `team-secrets/`"
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
```
|
|
||||||
1. ☑ Enable "Search across all mount points"
|
|
||||||
2. Search: "database"
|
|
||||||
3. Results show secrets from ALL mounts with indicators
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use Case 2: Multi-Team Environment
|
|
||||||
|
|
||||||
**Scenario:** Organization with multiple teams, each with their own mount point
|
|
||||||
|
|
||||||
**Detected:**
|
|
||||||
```
|
|
||||||
- secret (shared)
|
|
||||||
- team-engineering-secrets
|
|
||||||
- team-ops-secrets
|
|
||||||
- team-data-secrets
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefit:** Search across all teams' secrets (if you have permission)
|
|
||||||
|
|
||||||
### Use Case 3: Fast Targeted Search
|
|
||||||
|
|
||||||
**Scenario:** "I know exactly where to look: `secret/prod/myapp/`"
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
```
|
|
||||||
1. ☐ Disable "Search across all mount points"
|
|
||||||
2. Base Path: secret/prod/myapp/
|
|
||||||
3. Search: "api-key"
|
|
||||||
4. Fast, targeted results
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔒 Security & Permissions
|
|
||||||
|
|
||||||
### Required Permissions
|
|
||||||
|
|
||||||
**For login verification and mount discovery:**
|
|
||||||
```hcl
|
|
||||||
path "sys/internal/ui/mounts" {
|
|
||||||
capabilities = ["read"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**For searching:**
|
|
||||||
```hcl
|
|
||||||
# KV v2
|
|
||||||
path "secret/metadata/*" {
|
|
||||||
capabilities = ["list"]
|
|
||||||
}
|
|
||||||
|
|
||||||
# KV v1
|
|
||||||
path "secret/*" {
|
|
||||||
capabilities = ["list"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Graceful Permission Handling
|
|
||||||
|
|
||||||
**What happens if you can't access a mount:**
|
|
||||||
```
|
|
||||||
🔍 Searching across 3 mount point(s)...
|
|
||||||
→ Searching in secret/
|
|
||||||
✓ 45 results
|
|
||||||
→ Searching in team-secrets/
|
|
||||||
✗ Error: permission denied
|
|
||||||
→ Searching in public-secrets/
|
|
||||||
✓ 23 results
|
|
||||||
|
|
||||||
✓ Found 68 total result(s) across all mounts
|
|
||||||
```
|
|
||||||
|
|
||||||
- ✅ Continues with other mounts
|
|
||||||
- ✅ Logs error in console
|
|
||||||
- ✅ Returns partial results
|
|
||||||
- ✅ No error thrown to user
|
|
||||||
|
|
||||||
## ⚡ Performance
|
|
||||||
|
|
||||||
### Caching Strategy
|
|
||||||
|
|
||||||
**Each mount path cached separately:**
|
|
||||||
```
|
|
||||||
Cache key: "server123:list:secret/"
|
|
||||||
Cache key: "server123:list:team-secrets/"
|
|
||||||
Cache key: "server123:list:public-secrets/"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits:**
|
|
||||||
- First search: API calls to all mounts
|
|
||||||
- Subsequent searches: Instant from cache
|
|
||||||
- Cache respects max size and expiration
|
|
||||||
|
|
||||||
### Search Optimization
|
|
||||||
|
|
||||||
**Sequential search with early exit:**
|
|
||||||
```
|
|
||||||
1. Search mount 1 → 400 results
|
|
||||||
2. Search mount 2 → 300 results
|
|
||||||
3. Search mount 3 → 300 results
|
|
||||||
4. Total: 1000 results → STOP (max reached)
|
|
||||||
5. Mount 4+ not searched
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎨 UI/UX Improvements
|
|
||||||
|
|
||||||
### Login Process
|
|
||||||
|
|
||||||
**Visual feedback:**
|
|
||||||
```
|
|
||||||
[Connecting...] → [✓ Login verified] → [✓ Found 3 mount points]
|
|
||||||
```
|
|
||||||
|
|
||||||
**On failure:**
|
|
||||||
```
|
|
||||||
[Connecting...] → [✗ Error: permission denied]
|
|
||||||
[Please check credentials]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Search Interface
|
|
||||||
|
|
||||||
**Dynamic UI:**
|
|
||||||
- Checkbox shows mount count: "(3 available)"
|
|
||||||
- Checkbox disabled if no mounts detected
|
|
||||||
- Base path field hidden when searching all mounts
|
|
||||||
- Results show mount indicator when relevant
|
|
||||||
|
|
||||||
**Helpful hints:**
|
|
||||||
```
|
|
||||||
ℹ️ Search Tips:
|
|
||||||
• Search all mounts: searches across all KV secret engines
|
|
||||||
(detected: secret, secret-v1, team-secrets)
|
|
||||||
• Base path: when not searching all mounts, specify starting path
|
|
||||||
• Results are cached to prevent excessive API calls
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📝 Console Output Examples
|
|
||||||
|
|
||||||
### Successful Login
|
|
||||||
```
|
|
||||||
⚡ Verifying login and fetching mount points...
|
|
||||||
✓ Found 3 KV mount point(s): ["secret", "secret-v1", "team-secrets"]
|
|
||||||
✓ Logged in successfully. Found 3 KV mount point(s).
|
|
||||||
```
|
|
||||||
|
|
||||||
### Failed Login
|
|
||||||
```
|
|
||||||
⚡ Verifying login and fetching mount points...
|
|
||||||
✗ Login verification failed: permission denied
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multi-Mount Search
|
|
||||||
```
|
|
||||||
🔍 Searching across 3 mount point(s)...
|
|
||||||
→ Searching in secret/
|
|
||||||
⚡ API call for list: secret/metadata/
|
|
||||||
⚡ API call for list: secret/metadata/prod/
|
|
||||||
⚡ API call for list: secret/metadata/dev/
|
|
||||||
✓ Cache hit for list: secret/metadata/shared/
|
|
||||||
→ Searching in secret-v1/
|
|
||||||
✓ Cache hit for list: secret-v1/
|
|
||||||
→ Searching in team-secrets/
|
|
||||||
⚡ API call for list: team-secrets/metadata/
|
|
||||||
✓ Found 145 total result(s) across all mounts
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Configuration
|
|
||||||
|
|
||||||
All existing configuration still applies:
|
|
||||||
|
|
||||||
**Cache Settings:**
|
|
||||||
- Max cache size (MB)
|
|
||||||
- Cache expiration time (minutes)
|
|
||||||
- Enable/disable caching
|
|
||||||
|
|
||||||
**Search Settings:**
|
|
||||||
- Max search depth (applies per mount)
|
|
||||||
- Max search results (applies globally across all mounts)
|
|
||||||
|
|
||||||
## 📚 Documentation
|
|
||||||
|
|
||||||
**New comprehensive guide:**
|
|
||||||
- `MOUNT_POINTS.md` - Everything about mount point detection and multi-mount search
|
|
||||||
|
|
||||||
**Updated guides:**
|
|
||||||
- `README.md` - Project overview
|
|
||||||
- `USAGE.md` - Usage instructions
|
|
||||||
- `FEATURES.md` - Feature list
|
|
||||||
|
|
||||||
## 🎯 Summary
|
|
||||||
|
|
||||||
✅ **Login verification** - No more blind login attempts
|
|
||||||
✅ **Auto-detect mount points** - Discover all KV secret engines
|
|
||||||
✅ **Search all mounts** - Optional cross-mount search
|
|
||||||
✅ **Mount indicators** - Know which mount each result is from
|
|
||||||
✅ **Smart KV handling** - Per-mount version detection
|
|
||||||
✅ **Graceful errors** - Continue on permission denied
|
|
||||||
✅ **Performance optimized** - Caching + early exit
|
|
||||||
✅ **Security conscious** - Respects Vault ACLs
|
|
||||||
|
|
||||||
This makes discovering secrets in large Vault deployments **much easier**! 🚀
|
|
||||||
|
|
||||||
395
MOUNT_POINTS.md
395
MOUNT_POINTS.md
@ -1,395 +0,0 @@
|
|||||||
# Mount Point Detection and Multi-Mount Search
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The application now automatically detects all available KV secret engine mount points when you log in, and allows you to search across all of them simultaneously.
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### 1. Login Verification
|
|
||||||
|
|
||||||
When you log in, the application:
|
|
||||||
|
|
||||||
1. **Verifies your credentials** by calling `/v1/sys/internal/ui/mounts`
|
|
||||||
2. **Discovers all mount points** available on the Vault server
|
|
||||||
3. **Filters for KV secret engines** (type: `kv` or `generic`)
|
|
||||||
4. **Detects KV versions** automatically from mount options
|
|
||||||
5. **Stores mount points** in the connection state
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Example mount points detected:
|
|
||||||
[
|
|
||||||
{ path: "secret", type: "kv", options: { version: "2" } },
|
|
||||||
{ path: "cubbyhole", type: "cubbyhole", options: {} },
|
|
||||||
{ path: "identity", type: "identity", options: {} },
|
|
||||||
{ path: "sys", type: "system", options: {} }
|
|
||||||
]
|
|
||||||
|
|
||||||
// Filtered to KV engines only:
|
|
||||||
[
|
|
||||||
{ path: "secret", type: "kv", options: { version: "2" } }
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Search Modes
|
|
||||||
|
|
||||||
The application supports two search modes:
|
|
||||||
|
|
||||||
#### Mode 1: Single Base Path (Default)
|
|
||||||
- ✅ Search within a specific mount point/path
|
|
||||||
- ✅ Fast and targeted
|
|
||||||
- ✅ Use when you know where to look
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Search only in secret/myapp/
|
|
||||||
Base Path: secret/myapp/
|
|
||||||
Search Term: database
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Mode 2: All Mount Points
|
|
||||||
- ✅ Search across all detected KV mount points
|
|
||||||
- ✅ Comprehensive coverage
|
|
||||||
- ✅ Use when you don't know which mount contains the secret
|
|
||||||
- ⚠️ Slower (searches multiple mount points sequentially)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Searches: secret/, secret-v1/, team-secrets/, etc.
|
|
||||||
☑ Search across all mount points (3 available)
|
|
||||||
Search Term: database
|
|
||||||
```
|
|
||||||
|
|
||||||
## UI Features
|
|
||||||
|
|
||||||
### Login Process
|
|
||||||
|
|
||||||
**Before (without verification):**
|
|
||||||
```
|
|
||||||
1. Enter credentials
|
|
||||||
2. Click "Connect"
|
|
||||||
3. Hope it works 🤞
|
|
||||||
```
|
|
||||||
|
|
||||||
**After (with verification):**
|
|
||||||
```
|
|
||||||
1. Enter credentials
|
|
||||||
2. Click "Connect"
|
|
||||||
3. ✓ Login verified via API call
|
|
||||||
4. ✓ Mount points detected
|
|
||||||
5. ✓ "Found 3 KV mount point(s)" message
|
|
||||||
6. If login fails: detailed error message
|
|
||||||
```
|
|
||||||
|
|
||||||
### Search Interface
|
|
||||||
|
|
||||||
**Checkbox:** "Search across all mount points"
|
|
||||||
- Shows number of available mounts: "(3 available)"
|
|
||||||
- Disabled if no mount points detected
|
|
||||||
- Off by default (single path search)
|
|
||||||
|
|
||||||
**When Enabled:**
|
|
||||||
- Base Path field hidden (not needed)
|
|
||||||
- Searches all detected KV mount points
|
|
||||||
- Results show mount point indicator: 📌
|
|
||||||
|
|
||||||
**When Disabled:**
|
|
||||||
- Base Path field visible
|
|
||||||
- Normal single-path search behavior
|
|
||||||
|
|
||||||
### Search Results
|
|
||||||
|
|
||||||
**With Single Path Search:**
|
|
||||||
```
|
|
||||||
📄 secret/myapp/database/credentials
|
|
||||||
Depth: 2
|
|
||||||
```
|
|
||||||
|
|
||||||
**With All Mounts Search:**
|
|
||||||
```
|
|
||||||
📄 secret/myapp/database/credentials
|
|
||||||
📌 secret
|
|
||||||
Depth: 2
|
|
||||||
|
|
||||||
📄 team-secrets/shared/database
|
|
||||||
📌 team-secrets
|
|
||||||
Depth: 1
|
|
||||||
```
|
|
||||||
|
|
||||||
## Technical Implementation
|
|
||||||
|
|
||||||
### API Call: `/v1/sys/internal/ui/mounts`
|
|
||||||
|
|
||||||
**Request:**
|
|
||||||
```http
|
|
||||||
GET /v1/sys/internal/ui/mounts HTTP/1.1
|
|
||||||
Host: vault.example.com
|
|
||||||
X-Vault-Token: your-token
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"secret/": {
|
|
||||||
"type": "kv",
|
|
||||||
"description": "key/value secret storage",
|
|
||||||
"accessor": "kv_abc123",
|
|
||||||
"config": {
|
|
||||||
"default_lease_ttl": 0,
|
|
||||||
"max_lease_ttl": 0
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"version": "2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cubbyhole/": {
|
|
||||||
"type": "cubbyhole",
|
|
||||||
"description": "per-token private secret storage",
|
|
||||||
...
|
|
||||||
},
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mount Point Filtering
|
|
||||||
|
|
||||||
Only KV secret engines are included:
|
|
||||||
- `type === 'kv'` - KV v1 or v2
|
|
||||||
- `type === 'generic'` - Legacy KV v1
|
|
||||||
|
|
||||||
Other types are excluded:
|
|
||||||
- `cubbyhole` - Per-token storage (not searchable)
|
|
||||||
- `identity` - Identity management
|
|
||||||
- `system` - System backend
|
|
||||||
- `pki` - PKI certificates
|
|
||||||
- `aws`, `azure`, `gcp` - Dynamic secrets
|
|
||||||
- etc.
|
|
||||||
|
|
||||||
### KV Version Detection
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// From mount options
|
|
||||||
const kvVersion = mount.options?.version === '2' ? 2 : 1;
|
|
||||||
|
|
||||||
// Automatically used for path transformation
|
|
||||||
// KV v2: secret/ → secret/metadata/ for LIST
|
|
||||||
// KV v1: secret/ → secret/ as-is
|
|
||||||
```
|
|
||||||
|
|
||||||
### Search Algorithm
|
|
||||||
|
|
||||||
**Single Path:**
|
|
||||||
```
|
|
||||||
1. Search in base path
|
|
||||||
2. Recurse into subdirectories
|
|
||||||
3. Return results
|
|
||||||
```
|
|
||||||
|
|
||||||
**All Mounts:**
|
|
||||||
```
|
|
||||||
1. For each KV mount point:
|
|
||||||
a. Detect KV version
|
|
||||||
b. Search from mount root
|
|
||||||
c. Recurse into subdirectories
|
|
||||||
d. Add results with mount indicator
|
|
||||||
e. Check max results limit
|
|
||||||
2. Combine all results
|
|
||||||
3. Return aggregated results
|
|
||||||
```
|
|
||||||
|
|
||||||
## Performance Considerations
|
|
||||||
|
|
||||||
### Caching Strategy
|
|
||||||
|
|
||||||
**All searches are cached:**
|
|
||||||
- Single path search: Cached per path
|
|
||||||
- All mounts search: Each mount path cached separately
|
|
||||||
- Cache key includes mount point
|
|
||||||
- Repeated searches are instant
|
|
||||||
|
|
||||||
**Cache Benefits:**
|
|
||||||
- Prevents redundant API calls
|
|
||||||
- Faster subsequent searches
|
|
||||||
- Respects DDoS prevention limits
|
|
||||||
|
|
||||||
### Search Limits
|
|
||||||
|
|
||||||
**Applied globally across all mounts:**
|
|
||||||
```typescript
|
|
||||||
config.search.maxResults = 1000; // Total across all mounts
|
|
||||||
config.search.maxDepth = 10; // Per mount point
|
|
||||||
|
|
||||||
// Example:
|
|
||||||
// Mount 1: 400 results, depth 8 ✓
|
|
||||||
// Mount 2: 300 results, depth 7 ✓
|
|
||||||
// Mount 3: 300 results, depth 5 ✓
|
|
||||||
// Total: 1000 results - stops here
|
|
||||||
```
|
|
||||||
|
|
||||||
### Performance Tips
|
|
||||||
|
|
||||||
1. **Use single path search** when you know the mount
|
|
||||||
2. **Enable all mounts search** for discovery
|
|
||||||
3. **Adjust max results** in settings if needed
|
|
||||||
4. **Use specific search terms** to limit results
|
|
||||||
5. **Check console** for per-mount progress
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
### Login Verification Fails
|
|
||||||
|
|
||||||
```
|
|
||||||
Error: Login verification failed: permission denied
|
|
||||||
|
|
||||||
Possible causes:
|
|
||||||
1. Invalid credentials
|
|
||||||
2. Token expired
|
|
||||||
3. No permission to list mounts
|
|
||||||
4. Network/CORS issues
|
|
||||||
|
|
||||||
Solution: Check credentials and permissions
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mount Point Access Denied
|
|
||||||
|
|
||||||
```
|
|
||||||
✓ Found 3 KV mount point(s): secret, secret-v1, team-secrets
|
|
||||||
→ Searching in secret/
|
|
||||||
✓ 45 results
|
|
||||||
→ Searching in secret-v1/
|
|
||||||
✗ Error: permission denied
|
|
||||||
→ Searching in team-secrets/
|
|
||||||
✓ 23 results
|
|
||||||
|
|
||||||
✓ Found 68 total result(s) across all mounts
|
|
||||||
```
|
|
||||||
|
|
||||||
**Behavior:**
|
|
||||||
- Continues with other mounts
|
|
||||||
- Logs error for failed mount
|
|
||||||
- Returns partial results
|
|
||||||
- User sees console warning
|
|
||||||
|
|
||||||
## Use Cases
|
|
||||||
|
|
||||||
### Use Case 1: Discovery
|
|
||||||
|
|
||||||
**Scenario:** "I know there's a database credential somewhere, but I don't remember which mount."
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
```
|
|
||||||
1. ☑ Enable "Search across all mount points"
|
|
||||||
2. Search Term: "database"
|
|
||||||
3. Get results from all mounts:
|
|
||||||
- secret/prod/database
|
|
||||||
- secret/dev/database
|
|
||||||
- team-secrets/shared/database
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use Case 2: Specific Search
|
|
||||||
|
|
||||||
**Scenario:** "I need to find all Redis configs in the production secrets."
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
```
|
|
||||||
1. ☐ Disable "Search across all mount points"
|
|
||||||
2. Base Path: secret/prod/
|
|
||||||
3. Search Term: "redis"
|
|
||||||
4. Fast, targeted results
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use Case 3: Multi-Team Environment
|
|
||||||
|
|
||||||
**Scenario:** Multiple teams with separate mount points
|
|
||||||
|
|
||||||
**Detected mounts:**
|
|
||||||
```
|
|
||||||
- secret (shared)
|
|
||||||
- team-a-secrets
|
|
||||||
- team-b-secrets
|
|
||||||
- team-c-secrets
|
|
||||||
```
|
|
||||||
|
|
||||||
**All mounts search:**
|
|
||||||
- Finds secrets across all team mounts
|
|
||||||
- Shows which mount each result is in
|
|
||||||
- Respects permissions (skips unauthorized mounts)
|
|
||||||
|
|
||||||
## Console Output Examples
|
|
||||||
|
|
||||||
### Login:
|
|
||||||
```
|
|
||||||
⚡ Verifying login and fetching mount points...
|
|
||||||
✓ Found 3 KV mount point(s): ["secret", "secret-v1", "team-secrets"]
|
|
||||||
✓ Logged in successfully. Found 3 KV mount point(s).
|
|
||||||
```
|
|
||||||
|
|
||||||
### Single Path Search:
|
|
||||||
```
|
|
||||||
⚡ API call for list: secret/myapp/
|
|
||||||
✓ Cache hit for list: secret/myapp/database/
|
|
||||||
🔍 Searching...
|
|
||||||
✓ Found 12 result(s) in 0.45s
|
|
||||||
```
|
|
||||||
|
|
||||||
### All Mounts Search:
|
|
||||||
```
|
|
||||||
🔍 Searching across 3 mount point(s)...
|
|
||||||
→ Searching in secret/
|
|
||||||
✓ 45 results from secret
|
|
||||||
→ Searching in secret-v1/
|
|
||||||
✓ 23 results from secret-v1
|
|
||||||
→ Searching in team-secrets/
|
|
||||||
✗ Error searching team-secrets: permission denied
|
|
||||||
✓ Found 68 total result(s) across all mounts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security Implications
|
|
||||||
|
|
||||||
### What's Exposed
|
|
||||||
|
|
||||||
**Mount point names and structure:**
|
|
||||||
- ✅ Visible to anyone who can log in
|
|
||||||
- ✅ No secret data exposed
|
|
||||||
- ✅ Only KV mounts shown
|
|
||||||
- ✅ Respects Vault ACLs
|
|
||||||
|
|
||||||
### Permissions
|
|
||||||
|
|
||||||
**Required permissions:**
|
|
||||||
```hcl
|
|
||||||
# To list mounts (login verification)
|
|
||||||
path "sys/internal/ui/mounts" {
|
|
||||||
capabilities = ["read"]
|
|
||||||
}
|
|
||||||
|
|
||||||
# To search in a mount
|
|
||||||
path "secret/metadata/*" { # KV v2
|
|
||||||
capabilities = ["list"]
|
|
||||||
}
|
|
||||||
|
|
||||||
path "secret/*" { # KV v1
|
|
||||||
capabilities = ["list"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Failed Permission Handling
|
|
||||||
|
|
||||||
- ✅ Graceful degradation
|
|
||||||
- ✅ Continues with accessible mounts
|
|
||||||
- ✅ Logs denied mounts
|
|
||||||
- ✅ No error thrown to user
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
✅ **Login now verifies credentials** via API call
|
|
||||||
✅ **Mount points auto-detected** on login
|
|
||||||
✅ **Search across all mounts** optional feature (off by default)
|
|
||||||
✅ **Mount indicator** in search results
|
|
||||||
✅ **Automatic KV version detection** per mount
|
|
||||||
✅ **Graceful error handling** for inaccessible mounts
|
|
||||||
✅ **Performance optimized** with caching
|
|
||||||
✅ **Security conscious** - respects Vault ACLs
|
|
||||||
|
|
||||||
This feature makes it much easier to discover secrets across large Vault deployments with multiple mount points!
|
|
||||||
|
|
||||||
210
README.md
210
README.md
@ -1,205 +1,45 @@
|
|||||||
# Browser Vault GUI
|
# Browser Vault GUI
|
||||||
|
|
||||||
A modern Vue 3 + TypeScript frontend for HashiCorp Vault with Tailwind CSS and DaisyUI. This is an alternative web interface that allows you to connect to multiple Vault servers and manage your secrets.
|
A modern, user-friendly web interface for HashiCorp Vault that runs entirely in your browser.
|
||||||
|
|
||||||
## Features
|
## Installation
|
||||||
|
|
||||||
- 🔐 **Multiple Vault Servers**: Add and manage connections to multiple Vault instances
|
|
||||||
- 🔑 **Multiple Auth Methods**: Support for Token, Username/Password, and LDAP authentication
|
|
||||||
- 🔍 **Recursive Path Search**: Search through vault paths recursively with configurable depth limits
|
|
||||||
- 💾 **Smart Caching**: API responses are cached in localStorage to prevent DDoS and reduce server load
|
|
||||||
- ⚙️ **Configurable Settings**: Adjust cache size, expiration time, search depth, and result limits
|
|
||||||
- 📊 **Cache Statistics**: Monitor cache usage with real-time statistics
|
|
||||||
- 🎨 **Modern UI**: Beautiful, responsive interface with dark/light mode support (Tailwind + DaisyUI)
|
|
||||||
- 🚀 **Fast**: Built with Vite for lightning-fast development and builds
|
|
||||||
- 🔒 **Secure by Default**: Credentials stored in memory only (optional localStorage with warnings)
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Node.js 18+ and npm (or yarn/pnpm)
|
You'll need:
|
||||||
|
- Node.js (version 18 or higher)
|
||||||
|
- A running HashiCorp Vault server
|
||||||
|
- Basic knowledge of your Vault server URL and credentials
|
||||||
|
|
||||||
### Installation
|
### Quick Start
|
||||||
|
|
||||||
1. Install dependencies:
|
1. **Download the project**
|
||||||
```bash
|
```bash
|
||||||
npm install
|
git clone <repository-url>
|
||||||
|
cd browser-vault-gui
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Start the development server:
|
2. **Install dependencies**
|
||||||
```bash
|
```bash
|
||||||
npm run dev
|
pnpm install
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Open your browser and navigate to `http://localhost:5173`
|
3. **Start the development server**
|
||||||
|
```bash
|
||||||
|
pnpm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Open your browser**
|
||||||
|
- Navigate to `http://localhost:5173`
|
||||||
|
- Add your Vault server details
|
||||||
|
- Log in with your Vault credentials
|
||||||
|
|
||||||
### Building for Production
|
### Building for Production
|
||||||
|
|
||||||
|
To create a production build:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
pnpm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
The built files will be in the `dist/` directory.
|
The built files will be in the `dist` folder, ready to deploy to any web server.
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
1. **Add a Vault Server**:
|
|
||||||
- Click "Add Server" button
|
|
||||||
- Enter server name, URL, and optional description
|
|
||||||
- Click "Add Server" to save
|
|
||||||
|
|
||||||
2. **Connect to a Server**:
|
|
||||||
- Select a server from the list
|
|
||||||
- Choose your authentication method
|
|
||||||
- Enter your credentials
|
|
||||||
- Click "Connect"
|
|
||||||
|
|
||||||
3. **Browse and Search Secrets**:
|
|
||||||
- Once connected, use the secret browser to read secrets
|
|
||||||
- Enter the path to your secret (e.g., `secret/data/myapp/config`)
|
|
||||||
- Click "Read Secret" or press Enter
|
|
||||||
- Or use the "🔍 Search" button to recursively search for paths
|
|
||||||
- Search results are cached automatically
|
|
||||||
|
|
||||||
4. **Configure Settings**:
|
|
||||||
- Click "⚙️ Settings" to adjust cache and search parameters
|
|
||||||
- Set maximum cache size (in MB)
|
|
||||||
- Configure cache expiration time
|
|
||||||
- Adjust maximum search depth and result limits
|
|
||||||
- View cache statistics and clear cache if needed
|
|
||||||
|
|
||||||
## Implementation Notes
|
|
||||||
|
|
||||||
This application includes a **working Vault API client** with the following features:
|
|
||||||
|
|
||||||
✅ **Implemented:**
|
|
||||||
- Read secrets from Vault
|
|
||||||
- List secrets at a given path
|
|
||||||
- Recursive path search with caching
|
|
||||||
- Configurable cache system
|
|
||||||
- Settings management
|
|
||||||
|
|
||||||
🚧 **To be implemented:**
|
|
||||||
- Write/update secrets
|
|
||||||
- Delete secrets
|
|
||||||
- Policy management
|
|
||||||
- Audit log viewing
|
|
||||||
- Authentication flows (currently requires pre-existing token/credentials)
|
|
||||||
|
|
||||||
### CORS Configuration
|
|
||||||
|
|
||||||
To use this with your Vault server, you'll need to configure CORS. Add the following to your Vault server configuration:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
ui = true
|
|
||||||
|
|
||||||
listener "tcp" {
|
|
||||||
address = "0.0.0.0:8200"
|
|
||||||
tls_disable = 1
|
|
||||||
|
|
||||||
cors_enabled = true
|
|
||||||
cors_allowed_origins = ["http://localhost:5173", "https://yourdomain.com"]
|
|
||||||
cors_allowed_headers = ["*"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Vault API Endpoints
|
|
||||||
|
|
||||||
The Vault HTTP API endpoints you'll need to implement:
|
|
||||||
|
|
||||||
- **Authentication**: `POST /v1/auth/<method>/login`
|
|
||||||
- **Read Secret**: `GET /v1/<path>`
|
|
||||||
- **Write Secret**: `POST /v1/<path>`
|
|
||||||
- **Delete Secret**: `DELETE /v1/<path>`
|
|
||||||
- **List Secrets**: `LIST /v1/<path>` (or `GET /v1/<path>?list=true`)
|
|
||||||
|
|
||||||
Remember to include the `X-Vault-Token` header with your authentication token for all authenticated requests.
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
⚠️ **Important Security Notes**:
|
|
||||||
|
|
||||||
- This application stores Vault server URLs and cached API responses in localStorage
|
|
||||||
- **Credentials are NOT persisted by default** - they are only kept in memory during the active session
|
|
||||||
- Cached responses may contain sensitive secret paths and secret data
|
|
||||||
- Always use HTTPS URLs for production Vault servers
|
|
||||||
- Be aware of CORS restrictions when connecting to Vault servers
|
|
||||||
|
|
||||||
### ⚠️ Optional Credential Saving (NOT RECOMMENDED)
|
|
||||||
|
|
||||||
The app includes an **optional** feature to save credentials locally:
|
|
||||||
|
|
||||||
- **Default**: Credentials are NEVER saved ✅ (Recommended)
|
|
||||||
- **Optional**: Users can choose to save credentials with explicit warnings ⚠️
|
|
||||||
|
|
||||||
**If you enable credential saving:**
|
|
||||||
- A prominent security warning is shown before saving
|
|
||||||
- Credentials are stored in **plain text** in localStorage
|
|
||||||
- Anyone with access to your browser can read them
|
|
||||||
- This violates most security policies
|
|
||||||
- **Only use for personal development/testing**
|
|
||||||
|
|
||||||
See `SECURITY_CREDENTIALS.md` for detailed security analysis.
|
|
||||||
|
|
||||||
### Cache Security
|
|
||||||
|
|
||||||
The cache stores:
|
|
||||||
- ✅ Secret paths and directory listings (for search performance)
|
|
||||||
- ❌ **Secret data is NEVER cached** (always fetched fresh for security)
|
|
||||||
- ⚠️ Credentials (ONLY if user explicitly enables with warnings)
|
|
||||||
|
|
||||||
Cache can be cleared manually from Settings.
|
|
||||||
|
|
||||||
## Technology Stack
|
|
||||||
|
|
||||||
- **Vue 3** - Progressive JavaScript framework with Composition API
|
|
||||||
- **TypeScript** - Type safety throughout
|
|
||||||
- **Tailwind CSS** - Utility-first CSS framework
|
|
||||||
- **DaisyUI** - Beautiful component library for Tailwind
|
|
||||||
- **Vite** - Lightning-fast build tool and dev server
|
|
||||||
- **Custom Vault Client** - Browser-compatible Vault HTTP API client with retries, timeouts, and error handling
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
### Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── components/ # Vue 3 components
|
|
||||||
│ ├── ServerSelector.vue
|
|
||||||
│ ├── LoginForm.vue
|
|
||||||
│ ├── Dashboard.vue
|
|
||||||
│ ├── PathSearch.vue
|
|
||||||
│ └── Settings.vue
|
|
||||||
├── services/
|
|
||||||
│ ├── vaultClient.ts # Low-level Vault HTTP API client
|
|
||||||
│ └── vaultApi.ts # High-level API with caching
|
|
||||||
├── utils/
|
|
||||||
│ └── cache.ts # Cache management system
|
|
||||||
├── types.ts # TypeScript type definitions
|
|
||||||
├── config.ts # Application configuration
|
|
||||||
├── App.vue # Main application component
|
|
||||||
├── main.ts # Application entry point
|
|
||||||
└── style.css # Tailwind CSS imports
|
|
||||||
```
|
|
||||||
|
|
||||||
### Scripts
|
|
||||||
|
|
||||||
- `npm run dev` - Start development server (Vite)
|
|
||||||
- `npm run build` - Build for production (Vue TSC + Vite)
|
|
||||||
- `npm run preview` - Preview production build
|
|
||||||
- `npm run lint` - Run ESLint (Vue + TypeScript)
|
|
||||||
|
|
||||||
### Migration Note
|
|
||||||
|
|
||||||
This project was recently migrated from React to Vue 3. See `VUE_MIGRATION.md` for details.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is open source and available under the MIT License.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
||||||
|
|
||||||
|
|||||||
@ -1,294 +0,0 @@
|
|||||||
# Saved Credentials Feature - Security Considerations
|
|
||||||
|
|
||||||
## ⚠️ WARNING: USE AT YOUR OWN RISK
|
|
||||||
|
|
||||||
This feature allows you to save Vault credentials (tokens, usernames, passwords) in your browser's localStorage for convenience. **This is NOT recommended for production or sensitive environments.**
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
### Saving Credentials
|
|
||||||
|
|
||||||
1. When logging in, check the **"⚠️ Save credentials locally"** checkbox
|
|
||||||
2. A security warning modal will appear on first use
|
|
||||||
3. Read and acknowledge the risks
|
|
||||||
4. Credentials are saved to localStorage (plain text)
|
|
||||||
5. On next login, credentials are pre-filled
|
|
||||||
|
|
||||||
### Visual Indicators
|
|
||||||
|
|
||||||
- Servers with saved credentials show a **🔓 Saved Credentials** badge
|
|
||||||
- The checkbox is pre-checked if credentials exist
|
|
||||||
- Warning styling (yellow/orange) throughout the UI
|
|
||||||
|
|
||||||
### Removing Saved Credentials
|
|
||||||
|
|
||||||
**Option 1: Uncheck the box**
|
|
||||||
- Uncheck "Save credentials locally"
|
|
||||||
- Login again
|
|
||||||
- Credentials are removed from localStorage
|
|
||||||
|
|
||||||
**Option 2: Remove the server**
|
|
||||||
- Delete the server from the list
|
|
||||||
- All associated data (including credentials) is removed
|
|
||||||
|
|
||||||
## Security Risks
|
|
||||||
|
|
||||||
### ❌ What's Wrong With Saving Credentials
|
|
||||||
|
|
||||||
1. **Plain Text Storage**
|
|
||||||
- Credentials are stored unencrypted in localStorage
|
|
||||||
- Easily accessible via browser DevTools (`localStorage.getItem('vaultServers')`)
|
|
||||||
- No encryption, obfuscation, or protection
|
|
||||||
|
|
||||||
2. **Browser Extension Access**
|
|
||||||
- Any browser extension can read localStorage
|
|
||||||
- Malicious extensions can steal credentials
|
|
||||||
- No way to restrict access
|
|
||||||
|
|
||||||
3. **Shared Computer Risk**
|
|
||||||
- Anyone with physical access can:
|
|
||||||
- Open browser DevTools
|
|
||||||
- Read localStorage
|
|
||||||
- Copy credentials
|
|
||||||
|
|
||||||
4. **XSS Vulnerability**
|
|
||||||
- If the app has an XSS vulnerability, credentials are exposed
|
|
||||||
- localStorage is accessible from JavaScript
|
|
||||||
|
|
||||||
5. **Browser Sync**
|
|
||||||
- Some browsers sync localStorage across devices
|
|
||||||
- Credentials might be synced to untrusted devices
|
|
||||||
- Shared across all synced browsers
|
|
||||||
|
|
||||||
6. **Compliance Issues**
|
|
||||||
- Violates most security policies
|
|
||||||
- Fails SOC 2, ISO 27001, PCI DSS requirements
|
|
||||||
- May violate company IT policies
|
|
||||||
|
|
||||||
7. **No Audit Trail**
|
|
||||||
- Can't track who accessed credentials
|
|
||||||
- No logging of credential usage
|
|
||||||
- Can't revoke access if device is lost
|
|
||||||
|
|
||||||
8. **Session Persistence**
|
|
||||||
- Credentials persist across browser restarts
|
|
||||||
- No automatic expiration
|
|
||||||
- Manual logout doesn't clear saved credentials
|
|
||||||
|
|
||||||
## Viewing Saved Credentials
|
|
||||||
|
|
||||||
Anyone can view saved credentials:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Open browser DevTools console
|
|
||||||
const servers = JSON.parse(localStorage.getItem('vaultServers'))
|
|
||||||
console.log(servers)
|
|
||||||
|
|
||||||
// View credentials for first server
|
|
||||||
console.log(servers[0].savedCredentials)
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"serverId": "my-vault",
|
|
||||||
"authMethod": "token",
|
|
||||||
"token": "hvs.CAESIJ5U8..." // ← Exposed!
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## When Is It (Maybe) Acceptable?
|
|
||||||
|
|
||||||
Use saved credentials ONLY if ALL of these are true:
|
|
||||||
|
|
||||||
### ✅ Acceptable Use Cases
|
|
||||||
|
|
||||||
1. **Development/Testing**
|
|
||||||
- Non-production Vault server
|
|
||||||
- Test data only, no real secrets
|
|
||||||
- Personal development machine
|
|
||||||
|
|
||||||
2. **Personal Use**
|
|
||||||
- Personal computer, not shared
|
|
||||||
- You understand the risks
|
|
||||||
- You accept responsibility
|
|
||||||
|
|
||||||
3. **Low-Value Secrets**
|
|
||||||
- Development API keys
|
|
||||||
- Non-sensitive test data
|
|
||||||
- Throwaway tokens
|
|
||||||
|
|
||||||
4. **Short-Lived Tokens**
|
|
||||||
- Tokens expire quickly (< 1 hour)
|
|
||||||
- Limited permissions
|
|
||||||
- Easy to rotate
|
|
||||||
|
|
||||||
### ❌ NEVER Use For
|
|
||||||
|
|
||||||
1. **Production Vault Servers**
|
|
||||||
2. **Shared Computers**
|
|
||||||
3. **Work/Corporate Laptops**
|
|
||||||
4. **Public Computers**
|
|
||||||
5. **Sensitive Data**
|
|
||||||
6. **Long-Lived Tokens**
|
|
||||||
7. **High-Privilege Accounts**
|
|
||||||
8. **Compliance-Required Systems**
|
|
||||||
|
|
||||||
## Better Alternatives
|
|
||||||
|
|
||||||
### Recommended: Don't Save Credentials
|
|
||||||
|
|
||||||
1. **Re-login Each Session**
|
|
||||||
- Most secure option
|
|
||||||
- Only credentials in memory
|
|
||||||
- Auto-cleared on logout/close
|
|
||||||
|
|
||||||
2. **Use Password Manager**
|
|
||||||
- Browser password manager
|
|
||||||
- 1Password, LastPass, Bitwarden
|
|
||||||
- Encrypted storage
|
|
||||||
- Auto-fill support
|
|
||||||
|
|
||||||
3. **Short-Lived Tokens**
|
|
||||||
- Generate tokens with short TTL
|
|
||||||
- Expire after 1-8 hours
|
|
||||||
- Automatically revoked
|
|
||||||
|
|
||||||
4. **SSO/OIDC Authentication**
|
|
||||||
- Use Vault's OIDC auth method
|
|
||||||
- Leverage existing SSO
|
|
||||||
- No password storage needed
|
|
||||||
|
|
||||||
5. **Auto-Logout Timer**
|
|
||||||
- Implement session timeout
|
|
||||||
- Auto-logout after inactivity
|
|
||||||
- Clear credentials from memory
|
|
||||||
|
|
||||||
## Implementation Details
|
|
||||||
|
|
||||||
### Where Credentials Are Stored
|
|
||||||
|
|
||||||
```
|
|
||||||
localStorage['vaultServers'] = JSON array of server objects
|
|
||||||
|
|
||||||
Each server object can contain:
|
|
||||||
{
|
|
||||||
"id": "server-id",
|
|
||||||
"name": "My Vault",
|
|
||||||
"url": "https://vault.example.com",
|
|
||||||
"kvVersion": 2,
|
|
||||||
"savedCredentials": { ← This is the dangerous part
|
|
||||||
"serverId": "server-id",
|
|
||||||
"authMethod": "token",
|
|
||||||
"token": "hvs.CAESIJ5U8..." ← Plain text!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Security Warning Modal
|
|
||||||
|
|
||||||
The app shows a prominent warning before saving credentials:
|
|
||||||
|
|
||||||
```
|
|
||||||
⚠️ Security Warning
|
|
||||||
|
|
||||||
This is NOT recommended for security reasons!
|
|
||||||
|
|
||||||
If you save credentials:
|
|
||||||
- Your token/password will be stored in plain text
|
|
||||||
- Anyone with access to your browser can read them
|
|
||||||
- Browser extensions can access localStorage
|
|
||||||
- If your computer is compromised, credentials are exposed
|
|
||||||
- This violates most security policies
|
|
||||||
|
|
||||||
Only use this if:
|
|
||||||
- You're on a personal, secure device
|
|
||||||
- You understand the security risks
|
|
||||||
- You're using a development/test Vault server
|
|
||||||
|
|
||||||
Better alternatives:
|
|
||||||
• Use short-lived tokens
|
|
||||||
• Re-login each session
|
|
||||||
• Use a password manager
|
|
||||||
• Enable auto-logout timeout
|
|
||||||
```
|
|
||||||
|
|
||||||
User must explicitly click "I Understand the Risks - Save Anyway"
|
|
||||||
|
|
||||||
## Console Warnings
|
|
||||||
|
|
||||||
The app logs warnings when credentials are saved:
|
|
||||||
|
|
||||||
```
|
|
||||||
⚠️ Credentials saved to localStorage (insecure!)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Future Improvements
|
|
||||||
|
|
||||||
Potential enhancements (not implemented):
|
|
||||||
|
|
||||||
1. **Encryption**
|
|
||||||
- Encrypt credentials with a master password
|
|
||||||
- Use Web Crypto API
|
|
||||||
- Still vulnerable but better than plain text
|
|
||||||
|
|
||||||
2. **Session Storage**
|
|
||||||
- Use sessionStorage instead of localStorage
|
|
||||||
- Cleared when tab is closed
|
|
||||||
- Doesn't persist across browser restarts
|
|
||||||
|
|
||||||
3. **Auto-Expiration**
|
|
||||||
- Automatically clear credentials after N days
|
|
||||||
- Require re-authentication
|
|
||||||
- Reduce exposure window
|
|
||||||
|
|
||||||
4. **Browser Warnings**
|
|
||||||
- Show persistent warning in UI when credentials are saved
|
|
||||||
- Remind user on each login
|
|
||||||
- Make it more obvious
|
|
||||||
|
|
||||||
5. **Credential Rotation**
|
|
||||||
- Prompt user to rotate tokens
|
|
||||||
- Integration with Vault's token renewal
|
|
||||||
- Automatic token refresh
|
|
||||||
|
|
||||||
## Comparison: Save vs Don't Save
|
|
||||||
|
|
||||||
| Aspect | Don't Save (Default) | Save Credentials |
|
|
||||||
|--------|---------------------|------------------|
|
|
||||||
| **Security** | ✅ Secure | ❌ Insecure |
|
|
||||||
| **Convenience** | ⚠️ Must re-login | ✅ Auto-login |
|
|
||||||
| **Compliance** | ✅ Compliant | ❌ Violates policies |
|
|
||||||
| **Risk if stolen** | ✅ Low | ❌ High |
|
|
||||||
| **Browser restart** | Must re-login | ✅ Stays logged in |
|
|
||||||
| **Shared computer** | ✅ Safe | ❌ Dangerous |
|
|
||||||
| **Audit trail** | ✅ Per-session | ❌ None |
|
|
||||||
| **Token expiration** | ✅ Natural | ⚠️ Manual |
|
|
||||||
|
|
||||||
## Responsible Disclosure
|
|
||||||
|
|
||||||
If you find saved credentials in localStorage:
|
|
||||||
|
|
||||||
1. **Don't use them** - That would be unauthorized access
|
|
||||||
2. **Report it** - Inform the credentials owner
|
|
||||||
3. **Secure the device** - Help secure the compromised device
|
|
||||||
4. **Rotate credentials** - All saved credentials should be rotated
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
### ⚠️ The Bottom Line
|
|
||||||
|
|
||||||
**Saving credentials is a convenience feature with serious security trade-offs.**
|
|
||||||
|
|
||||||
- ✅ **Convenient** for personal development
|
|
||||||
- ❌ **Dangerous** for anything sensitive
|
|
||||||
- ⚠️ **Use at your own risk**
|
|
||||||
|
|
||||||
**Default behavior (no saving) is recommended for everyone.**
|
|
||||||
|
|
||||||
If you choose to save credentials, you accept full responsibility for any security consequences.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*This feature exists because users requested it, but the developers strongly advise against using it in any security-conscious environment.*
|
|
||||||
|
|
||||||
240
USAGE.md
240
USAGE.md
@ -1,240 +0,0 @@
|
|||||||
# Usage Guide
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### 1. Install and Run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
Open http://localhost:5173 in your browser.
|
|
||||||
|
|
||||||
### 2. Add Your First Vault Server
|
|
||||||
|
|
||||||
1. Click **"+ Add Server"**
|
|
||||||
2. Fill in the form:
|
|
||||||
- **Server Name**: e.g., "Production Vault"
|
|
||||||
- **Server URL**: e.g., "https://vault.example.com"
|
|
||||||
- **Description**: Optional, e.g., "Production environment vault"
|
|
||||||
3. Click **"Add Server"**
|
|
||||||
|
|
||||||
### 3. Connect to Vault
|
|
||||||
|
|
||||||
1. Select your server from the list
|
|
||||||
2. Choose authentication method:
|
|
||||||
- **Token**: Paste your vault token
|
|
||||||
- **Username & Password**: Enter credentials
|
|
||||||
- **LDAP**: Enter LDAP credentials
|
|
||||||
3. Click **"Connect"**
|
|
||||||
|
|
||||||
### 4. Browse Secrets
|
|
||||||
|
|
||||||
#### Read a Secret Directly
|
|
||||||
|
|
||||||
1. Enter the full path in the "Secret Path" field
|
|
||||||
- Example: `secret/data/myapp/database`
|
|
||||||
2. Press Enter or click **"Read Secret"**
|
|
||||||
3. The secret data will appear below
|
|
||||||
|
|
||||||
#### Search for Secrets
|
|
||||||
|
|
||||||
1. Click **"🔍 Search"** button
|
|
||||||
2. Enter a **Base Path** (where to start searching)
|
|
||||||
- Example: `secret/`
|
|
||||||
3. Enter a **Search Term**
|
|
||||||
- Example: `database` (will find all paths containing "database")
|
|
||||||
4. Click **"Search"**
|
|
||||||
5. Results appear showing:
|
|
||||||
- 📁 Directories (not clickable)
|
|
||||||
- 📄 Secrets (clickable to view)
|
|
||||||
6. Click **"View"** on any secret to read it
|
|
||||||
|
|
||||||
### 5. Configure Settings
|
|
||||||
|
|
||||||
Click **"⚙️ Settings"** to adjust:
|
|
||||||
|
|
||||||
#### Cache Settings
|
|
||||||
- **Enable cache**: Toggle caching on/off
|
|
||||||
- **Maximum cache size**: How much data to cache (in MB)
|
|
||||||
- **Cache expiration**: How long cached data remains valid (in minutes)
|
|
||||||
|
|
||||||
#### Search Settings
|
|
||||||
- **Maximum search depth**: How deep to recurse (prevents infinite loops)
|
|
||||||
- **Maximum search results**: Limit number of results returned
|
|
||||||
|
|
||||||
#### Cache Statistics
|
|
||||||
View real-time cache usage:
|
|
||||||
- Total size of cached data
|
|
||||||
- Number of cached entries
|
|
||||||
- Age of oldest/newest entries
|
|
||||||
- **Clear Cache** button to reset
|
|
||||||
|
|
||||||
## Advanced Usage
|
|
||||||
|
|
||||||
### Understanding the Cache
|
|
||||||
|
|
||||||
The cache system prevents excessive API calls to your Vault server:
|
|
||||||
|
|
||||||
1. **First Request**: API call is made, result is cached
|
|
||||||
2. **Subsequent Requests**: Data returned from cache (instant)
|
|
||||||
3. **Cache Expiration**: After configured time, next request will hit the API again
|
|
||||||
|
|
||||||
**Cache Key Format**: `{serverId}:{operation}:{path}`
|
|
||||||
|
|
||||||
Example: `abc-123:list:secret/data/myapp/`
|
|
||||||
|
|
||||||
### Search Behavior
|
|
||||||
|
|
||||||
The recursive search:
|
|
||||||
1. Lists all items at the base path
|
|
||||||
2. For each item:
|
|
||||||
- If it matches the search term, it's added to results
|
|
||||||
- If it's a directory, recursively search inside it
|
|
||||||
3. Stops when:
|
|
||||||
- Max depth is reached
|
|
||||||
- Max results are found
|
|
||||||
- No more paths to explore
|
|
||||||
|
|
||||||
**Performance Tips**:
|
|
||||||
- Use specific base paths to limit search scope
|
|
||||||
- Results are cached, so repeated searches are fast
|
|
||||||
- Adjust max depth for deep directory structures
|
|
||||||
|
|
||||||
### Working with Different Auth Methods
|
|
||||||
|
|
||||||
#### Token Authentication
|
|
||||||
```
|
|
||||||
Token: s.1234567890abcdef
|
|
||||||
```
|
|
||||||
Best for: Development, CI/CD, service accounts
|
|
||||||
|
|
||||||
#### Username/Password
|
|
||||||
```
|
|
||||||
Username: john.doe
|
|
||||||
Password: ••••••••
|
|
||||||
```
|
|
||||||
Best for: Interactive users, testing
|
|
||||||
|
|
||||||
#### LDAP
|
|
||||||
```
|
|
||||||
Username: john.doe
|
|
||||||
Password: ••••••••
|
|
||||||
```
|
|
||||||
Best for: Enterprise users with LDAP integration
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Example 1: Finding Database Credentials
|
|
||||||
|
|
||||||
1. Connect to your vault server
|
|
||||||
2. Click "🔍 Search"
|
|
||||||
3. Base Path: `secret/`
|
|
||||||
4. Search Term: `database`
|
|
||||||
5. View results and click on the relevant secret
|
|
||||||
6. Copy the credentials you need
|
|
||||||
|
|
||||||
### Example 2: Browsing Application Secrets
|
|
||||||
|
|
||||||
1. Connect to vault
|
|
||||||
2. Enter path: `secret/data/myapp/`
|
|
||||||
3. Note the structure
|
|
||||||
4. Navigate to specific secrets:
|
|
||||||
- `secret/data/myapp/config`
|
|
||||||
- `secret/data/myapp/database`
|
|
||||||
- `secret/data/myapp/api-keys`
|
|
||||||
|
|
||||||
### Example 3: Managing Cache
|
|
||||||
|
|
||||||
1. Use the application normally
|
|
||||||
2. Open Settings to view cache stats
|
|
||||||
3. If cache grows too large or contains stale data:
|
|
||||||
- Adjust cache size limit
|
|
||||||
- Reduce expiration time
|
|
||||||
- Or click "Clear Cache"
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### CORS Errors
|
|
||||||
|
|
||||||
If you see CORS errors in the console:
|
|
||||||
|
|
||||||
1. Configure your Vault server to allow CORS
|
|
||||||
2. Add your frontend URL to `cors_allowed_origins`
|
|
||||||
3. Restart your Vault server
|
|
||||||
|
|
||||||
Example Vault config:
|
|
||||||
```hcl
|
|
||||||
listener "tcp" {
|
|
||||||
cors_enabled = true
|
|
||||||
cors_allowed_origins = ["http://localhost:5173"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Authentication Fails
|
|
||||||
|
|
||||||
- Verify your token/credentials are correct
|
|
||||||
- Check token hasn't expired
|
|
||||||
- Ensure you have proper permissions in Vault
|
|
||||||
- Check Vault server URL is correct
|
|
||||||
|
|
||||||
### Search Returns No Results
|
|
||||||
|
|
||||||
- Verify the base path exists and you have permission to list it
|
|
||||||
- Try a broader search term
|
|
||||||
- Check max depth isn't too low
|
|
||||||
- Ensure secrets exist at that path
|
|
||||||
|
|
||||||
### Cache Issues
|
|
||||||
|
|
||||||
- Clear cache from Settings if data seems stale
|
|
||||||
- Check cache expiration time isn't too long
|
|
||||||
- Verify localStorage isn't full (browser limit ~5-10MB)
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
1. **Security**:
|
|
||||||
- Always use HTTPS in production
|
|
||||||
- Don't share tokens
|
|
||||||
- Log out when done
|
|
||||||
- Clear cache if on shared computer
|
|
||||||
|
|
||||||
2. **Performance**:
|
|
||||||
- Use specific base paths for searches
|
|
||||||
- Adjust cache settings based on your usage
|
|
||||||
- Increase cache expiration if secrets change rarely
|
|
||||||
- Decrease for frequently updated secrets
|
|
||||||
|
|
||||||
3. **Organization**:
|
|
||||||
- Name servers clearly
|
|
||||||
- Add descriptions to help identify servers
|
|
||||||
- Use consistent path naming in Vault
|
|
||||||
- Structure secrets logically
|
|
||||||
|
|
||||||
4. **Maintenance**:
|
|
||||||
- Periodically clear cache
|
|
||||||
- Review cache statistics
|
|
||||||
- Adjust settings as usage patterns change
|
|
||||||
- Remove unused vault server configurations
|
|
||||||
|
|
||||||
## Keyboard Shortcuts
|
|
||||||
|
|
||||||
- **Enter** in Secret Path field: Read the secret
|
|
||||||
- **Enter** in Search Term field: Start search
|
|
||||||
- **Esc** in Settings modal: Close settings
|
|
||||||
|
|
||||||
## Data Storage
|
|
||||||
|
|
||||||
### What's Stored in localStorage:
|
|
||||||
- ✅ Vault server configurations (name, URL, description)
|
|
||||||
- ✅ Application settings (cache size, search limits, etc.)
|
|
||||||
- ✅ Cached API responses (paths, secrets)
|
|
||||||
|
|
||||||
### What's NOT Stored:
|
|
||||||
- ❌ Vault tokens
|
|
||||||
- ❌ Passwords
|
|
||||||
- ❌ Any credentials
|
|
||||||
|
|
||||||
Credentials are only kept in memory during your active session and are lost when you logout or close the tab.
|
|
||||||
|
|
||||||
345
VUE_MIGRATION.md
345
VUE_MIGRATION.md
@ -1,345 +0,0 @@
|
|||||||
# Vue 3 Migration Complete! 🎉
|
|
||||||
|
|
||||||
## What Changed
|
|
||||||
|
|
||||||
### ✅ Complete Rewrite from React to Vue 3 + Tailwind + DaisyUI
|
|
||||||
|
|
||||||
The entire UI layer has been converted while keeping all business logic intact!
|
|
||||||
|
|
||||||
## New Stack
|
|
||||||
|
|
||||||
- **Vue 3** with Composition API (`<script setup>`)
|
|
||||||
- **TypeScript** (fully typed)
|
|
||||||
- **Tailwind CSS** for utility-first styling
|
|
||||||
- **DaisyUI** for beautiful pre-built components
|
|
||||||
- **Vite** (already using it, no change)
|
|
||||||
|
|
||||||
## What Stayed the Same
|
|
||||||
|
|
||||||
**All business logic is untouched:**
|
|
||||||
- ✅ `src/services/vaultClient.ts` - Core Vault client
|
|
||||||
- ✅ `src/services/vaultApi.ts` - API with caching
|
|
||||||
- ✅ `src/utils/cache.ts` - Cache management
|
|
||||||
- ✅ `src/config.ts` - Configuration system
|
|
||||||
- ✅ `src/types.ts` - TypeScript interfaces
|
|
||||||
|
|
||||||
These are pure TypeScript and work identically in Vue!
|
|
||||||
|
|
||||||
## New Files
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
- `tailwind.config.js` - Tailwind + DaisyUI config
|
|
||||||
- `postcss.config.js` - PostCSS config for Tailwind
|
|
||||||
- `src/env.d.ts` - Vue type definitions
|
|
||||||
- `src/main.ts` - Vue app entry point (replaces main.tsx)
|
|
||||||
- `src/style.css` - Tailwind imports + custom styles
|
|
||||||
|
|
||||||
### Components (All .vue files)
|
|
||||||
- `src/App.vue`
|
|
||||||
- `src/components/ServerSelector.vue`
|
|
||||||
- `src/components/LoginForm.vue`
|
|
||||||
- `src/components/Dashboard.vue`
|
|
||||||
- `src/components/PathSearch.vue`
|
|
||||||
- `src/components/Settings.vue`
|
|
||||||
|
|
||||||
## Deleted Files
|
|
||||||
|
|
||||||
**All React files removed:**
|
|
||||||
- ❌ Deleted all `.tsx` files
|
|
||||||
- ❌ Deleted all `.css` component files
|
|
||||||
- ❌ Deleted React-specific config
|
|
||||||
|
|
||||||
## Setup Instructions
|
|
||||||
|
|
||||||
### 1. Install Dependencies
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
This will install:
|
|
||||||
- Vue 3
|
|
||||||
- Tailwind CSS
|
|
||||||
- DaisyUI
|
|
||||||
- Vue TypeScript compiler
|
|
||||||
- All necessary dev dependencies
|
|
||||||
|
|
||||||
### 2. Run Development Server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Build for Production
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Vue 3 Patterns Used
|
|
||||||
|
|
||||||
### Composition API with `<script setup>`
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
const count = ref(0)
|
|
||||||
const increment = () => count.value++
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<button @click="increment">Count: {{ count }}</button>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
### TypeScript Props & Emits
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup lang="ts">
|
|
||||||
interface Props {
|
|
||||||
server: VaultServer
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
|
||||||
const emit = defineEmits<{
|
|
||||||
login: [credentials: VaultCredentials]
|
|
||||||
}>()
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reactivity
|
|
||||||
|
|
||||||
- `ref()` - for primitive values (`ref(0)`, `ref('')`)
|
|
||||||
- `reactive()` - for objects (not used much, ref works for everything)
|
|
||||||
- `computed()` - for derived values
|
|
||||||
- `watch()` / `watchEffect()` - for side effects
|
|
||||||
|
|
||||||
## DaisyUI Components Used
|
|
||||||
|
|
||||||
### Buttons
|
|
||||||
```html
|
|
||||||
<button class="btn btn-primary">Primary</button>
|
|
||||||
<button class="btn btn-error">Danger</button>
|
|
||||||
<button class="btn btn-sm">Small</button>
|
|
||||||
<button class="btn loading">Loading</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Cards
|
|
||||||
```html
|
|
||||||
<div class="card bg-base-100 shadow-xl">
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 class="card-title">Title</h2>
|
|
||||||
<p>Content</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Forms
|
|
||||||
```html
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Label</span>
|
|
||||||
</label>
|
|
||||||
<input type="text" class="input input-bordered" />
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Alerts
|
|
||||||
```html
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<svg>...</svg>
|
|
||||||
<span>Info message</span>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Modal
|
|
||||||
```html
|
|
||||||
<div class="modal modal-open">
|
|
||||||
<div class="modal-box">
|
|
||||||
<!-- content -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Feature Comparison
|
|
||||||
|
|
||||||
| Feature | React Version | Vue Version | Status |
|
|
||||||
|---------|--------------|-------------|--------|
|
|
||||||
| Server Management | ✅ | ✅ | Identical |
|
|
||||||
| Multi-Auth Support | ✅ | ✅ | Identical |
|
|
||||||
| Login Verification | ✅ | ✅ | Identical |
|
|
||||||
| Mount Point Detection | ✅ | ✅ | Identical |
|
|
||||||
| Secret Reading | ✅ | ✅ | Identical |
|
|
||||||
| Recursive Search | ✅ | ✅ | Identical |
|
|
||||||
| Multi-Mount Search | ✅ | ✅ | Identical |
|
|
||||||
| Caching System | ✅ | ✅ | Identical |
|
|
||||||
| Settings Panel | ✅ | ✅ | Identical |
|
|
||||||
| KV v1/v2 Support | ✅ | ✅ | Identical |
|
|
||||||
| Dark/Light Mode | ✅ | ✅ | Improved (DaisyUI themes) |
|
|
||||||
| Responsive Design | ✅ | ✅ | Improved (Tailwind) |
|
|
||||||
|
|
||||||
## Benefits of Vue Version
|
|
||||||
|
|
||||||
### Code Quality
|
|
||||||
- ✅ **Less Code**: Vue templates are more concise than JSX
|
|
||||||
- ✅ **Better Separation**: Logic in `<script>`, template in `<template>`
|
|
||||||
- ✅ **Cleaner State**: No need for `setState`, just mutate `.value`
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
- ✅ **Smaller Bundle**: Vue is ~30% smaller than React
|
|
||||||
- ✅ **Faster**: Vue's reactivity system is more efficient
|
|
||||||
- ✅ **Better Tree-Shaking**: Unused features are removed
|
|
||||||
|
|
||||||
### Developer Experience
|
|
||||||
- ✅ **Less Boilerplate**: No need for `useState`, `useEffect` everywhere
|
|
||||||
- ✅ **Better TypeScript**: Vue 3 has excellent TS support
|
|
||||||
- ✅ **Tailwind**: Utility-first CSS is faster than custom CSS
|
|
||||||
- ✅ **DaisyUI**: Pre-built components are beautiful and consistent
|
|
||||||
|
|
||||||
### Styling
|
|
||||||
- ✅ **Tailwind Utilities**: Faster development with utility classes
|
|
||||||
- ✅ **DaisyUI Components**: Beautiful, accessible components out of the box
|
|
||||||
- ✅ **Theme Support**: Easy dark/light mode switching
|
|
||||||
- ✅ **Less CSS**: ~500 lines of custom CSS → ~50 lines
|
|
||||||
|
|
||||||
## File Size Comparison
|
|
||||||
|
|
||||||
### React Version
|
|
||||||
- `node_modules/`: ~250 MB
|
|
||||||
- Bundle size: ~145 KB (gzipped)
|
|
||||||
|
|
||||||
### Vue Version (Estimated)
|
|
||||||
- `node_modules/`: ~180 MB
|
|
||||||
- Bundle size: ~95 KB (gzipped)
|
|
||||||
|
|
||||||
**30-35% smaller!**
|
|
||||||
|
|
||||||
## Browser Support
|
|
||||||
|
|
||||||
Same as before:
|
|
||||||
- Chrome/Edge 90+
|
|
||||||
- Firefox 90+
|
|
||||||
- Safari 14+
|
|
||||||
|
|
||||||
## Breaking Changes
|
|
||||||
|
|
||||||
**None!**
|
|
||||||
|
|
||||||
All functionality is preserved. The API is the same, features work identically, and all your data (servers, cache) persists in localStorage.
|
|
||||||
|
|
||||||
## Migration Notes
|
|
||||||
|
|
||||||
### What Developers Need to Know
|
|
||||||
|
|
||||||
If you're familiar with React:
|
|
||||||
|
|
||||||
1. **No JSX**: Use Vue templates instead
|
|
||||||
2. **No useState**: Use `ref()` instead
|
|
||||||
3. **No useEffect**: Use `onMounted()`, `watch()`, `watchEffect()`
|
|
||||||
4. **No props destructuring**: Use `props.propName`
|
|
||||||
5. **Events**: Emit events with `emit('eventName', data)`
|
|
||||||
6. **v-model**: Two-way binding (simpler than React controlled components)
|
|
||||||
7. **Directives**: `v-if`, `v-for`, `v-show`, `@click`, `:class`, etc.
|
|
||||||
|
|
||||||
### Example Conversion
|
|
||||||
|
|
||||||
**React:**
|
|
||||||
```tsx
|
|
||||||
const [count, setCount] = useState(0)
|
|
||||||
useEffect(() => {
|
|
||||||
console.log('Count changed:', count)
|
|
||||||
}, [count])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button onClick={() => setCount(count + 1)}>
|
|
||||||
{count}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Vue:**
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
const count = ref(0)
|
|
||||||
watch(count, (newCount) => {
|
|
||||||
console.log('Count changed:', newCount)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<button @click="count++">
|
|
||||||
{{ count }}
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Theme Switching
|
|
||||||
|
|
||||||
DaisyUI supports multiple themes. To switch themes:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<!-- In index.html -->
|
|
||||||
<html data-theme="dark"> <!-- or "light", "cupcake", etc. -->
|
|
||||||
```
|
|
||||||
|
|
||||||
Or dynamically:
|
|
||||||
```typescript
|
|
||||||
document.documentElement.setAttribute('data-theme', 'light')
|
|
||||||
```
|
|
||||||
|
|
||||||
Available themes: dark, light, cupcake, bumblebee, emerald, corporate, synthwave, retro, cyberpunk, valentine, halloween, garden, forest, aqua, lofi, pastel, fantasy, wireframe, black, luxury, dracula, cmyk, autumn, business, acid, lemonade, night, coffee, winter
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
### Recommended Enhancements
|
|
||||||
|
|
||||||
1. **Add Vue Router** (if you want multiple pages)
|
|
||||||
2. **Add Pinia** (Vue's state management, like Redux)
|
|
||||||
3. **Add VueUse** (collection of useful composition utilities)
|
|
||||||
4. **Add animations** with Vue transitions
|
|
||||||
5. **PWA support** with Vite PWA plugin
|
|
||||||
|
|
||||||
### Optional Improvements
|
|
||||||
|
|
||||||
1. **Virtual scrolling** for large result lists
|
|
||||||
2. **Drag & drop** for organizing servers
|
|
||||||
3. **Keyboard shortcuts** with Vue composables
|
|
||||||
4. **Export/import** server configurations
|
|
||||||
5. **Secret editing** UI with forms
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
To test the conversion:
|
|
||||||
|
|
||||||
1. `npm install`
|
|
||||||
2. `npm run dev`
|
|
||||||
3. Add a server (should work like before)
|
|
||||||
4. Login (mount points should be detected)
|
|
||||||
5. Read a secret
|
|
||||||
6. Try search (single path and all mounts)
|
|
||||||
7. Check settings panel
|
|
||||||
8. Verify cache statistics
|
|
||||||
|
|
||||||
Everything should work identically to the React version!
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
All previous documentation still applies:
|
|
||||||
- `README.md` - Updated for Vue
|
|
||||||
- `USAGE.md` - Same usage, new UI
|
|
||||||
- `KV_VERSIONS.md` - No changes
|
|
||||||
- `MOUNT_POINTS.md` - No changes
|
|
||||||
- `CORS_AND_CLIENT.md` - No changes
|
|
||||||
|
|
||||||
## Conclusion
|
|
||||||
|
|
||||||
✅ **Migration Complete!**
|
|
||||||
✅ **All features preserved**
|
|
||||||
✅ **Smaller bundle size**
|
|
||||||
✅ **Better performance**
|
|
||||||
✅ **Modern UI with Tailwind + DaisyUI**
|
|
||||||
✅ **Cleaner codebase**
|
|
||||||
|
|
||||||
The Vue version is production-ready! 🚀
|
|
||||||
|
|
||||||
6
main.py
6
main.py
@ -1,6 +0,0 @@
|
|||||||
def main():
|
|
||||||
print("Hello from browser-vault-gui!")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
[project]
|
|
||||||
name = "browser-vault-gui"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Add your description here"
|
|
||||||
readme = "README.md"
|
|
||||||
requires-python = ">=3.13"
|
|
||||||
dependencies = []
|
|
||||||
13
src/App.vue
13
src/App.vue
@ -13,13 +13,7 @@ const selectedServer = ref<VaultServer | null>(null)
|
|||||||
const activeConnection = ref<VaultConnection | null>(null)
|
const activeConnection = ref<VaultConnection | null>(null)
|
||||||
|
|
||||||
const { error } = useSweetAlert()
|
const { error } = useSweetAlert()
|
||||||
const { modalState, closePolicyModal, showPolicyModal } = usePolicyModal()
|
const { modalState, closePolicyModal } = usePolicyModal()
|
||||||
|
|
||||||
// Test function for debugging
|
|
||||||
const testPolicyModal = () => {
|
|
||||||
console.log('🔍 Testing policy modal')
|
|
||||||
showPolicyModal('secret/myapp/config', 'write', 'permission denied', 'Test Policy Modal')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load servers from localStorage on mount
|
// Load servers from localStorage on mount
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -116,11 +110,6 @@ const handleLogout = () => {
|
|||||||
|
|
||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<main class="flex-1 container mx-auto px-4 py-8">
|
<main class="flex-1 container mx-auto px-4 py-8">
|
||||||
<!-- Debug button -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<button class="btn btn-sm btn-secondary" @click="testPolicyModal">🔍 Test Policy Modal</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="!activeConnection" class="grid md:grid-cols-2 gap-8">
|
<div v-if="!activeConnection" class="grid md:grid-cols-2 gap-8">
|
||||||
<!-- Server Selection -->
|
<!-- Server Selection -->
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user