Initial commit: DashCaddy v1.0

Full codebase including API server (32 modules + routes), dashboard frontend,
DashCA certificate distribution, installer script, and deployment skills.
This commit is contained in:
2026-03-05 02:26:12 -08:00
commit f61e85d9a7
337 changed files with 75282 additions and 0 deletions

View File

@@ -0,0 +1,236 @@
/**
* Keychain Manager for DashCaddy
* Provides secure credential storage using OS-native keychains
* Falls back to encrypted file storage if keychain is unavailable
*/
const { execSync } = require('child_process');
const os = require('os');
const crypto = require('crypto');
const SERVICE_NAME = 'DashCaddy';
const ACCOUNT_PREFIX = 'dashcaddy';
class KeychainManager {
constructor() {
this.platform = os.platform();
this.available = this.checkAvailability();
}
/**
* Check if OS keychain is available
* @returns {boolean}
*/
checkAvailability() {
try {
if (this.platform === 'win32') {
// Check if PowerShell is available
execSync('powershell -Command "Get-Command Get-Credential"', { stdio: 'ignore' });
return true;
} else if (this.platform === 'darwin') {
// Check if security command is available
execSync('which security', { stdio: 'ignore' });
return true;
} else if (this.platform === 'linux') {
// Check if secret-tool (libsecret) is available
try {
execSync('which secret-tool', { stdio: 'ignore' });
return true;
} catch {
// Try gnome-keyring
execSync('which gnome-keyring-daemon', { stdio: 'ignore' });
return true;
}
}
return false;
} catch {
console.warn('[Keychain] OS keychain not available, will use encrypted file storage');
return false;
}
}
/**
* Store a credential in the OS keychain
* @param {string} key - Credential identifier
* @param {string} value - Credential value
* @returns {Promise<boolean>} Success status
*/
async store(key, value) {
if (!this.available) {
return false;
}
const account = `${ACCOUNT_PREFIX}.${key}`;
try {
if (this.platform === 'win32') {
return await this.storeWindows(account, value);
} else if (this.platform === 'darwin') {
return await this.storeMacOS(account, value);
} else if (this.platform === 'linux') {
return await this.storeLinux(account, value);
}
return false;
} catch (error) {
console.error(`[Keychain] Failed to store ${key}:`, error.message);
return false;
}
}
/**
* Retrieve a credential from the OS keychain
* @param {string} key - Credential identifier
* @returns {Promise<string|null>} Credential value or null
*/
async retrieve(key) {
if (!this.available) {
return null;
}
const account = `${ACCOUNT_PREFIX}.${key}`;
try {
if (this.platform === 'win32') {
return await this.retrieveWindows(account);
} else if (this.platform === 'darwin') {
return await this.retrieveMacOS(account);
} else if (this.platform === 'linux') {
return await this.retrieveLinux(account);
}
return null;
} catch (error) {
console.error(`[Keychain] Failed to retrieve ${key}:`, error.message);
return null;
}
}
/**
* Delete a credential from the OS keychain
* @param {string} key - Credential identifier
* @returns {Promise<boolean>} Success status
*/
async delete(key) {
if (!this.available) {
return false;
}
const account = `${ACCOUNT_PREFIX}.${key}`;
try {
if (this.platform === 'win32') {
return await this.deleteWindows(account);
} else if (this.platform === 'darwin') {
return await this.deleteMacOS(account);
} else if (this.platform === 'linux') {
return await this.deleteLinux(account);
}
return false;
} catch (error) {
console.error(`[Keychain] Failed to delete ${key}:`, error.message);
return false;
}
}
// Windows Credential Manager implementation
async storeWindows(account, value) {
const escapedValue = value.replace(/"/g, '""');
const script = `
$password = ConvertTo-SecureString -String "${escapedValue}" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential("${account}", $password)
cmdkey /generic:"${SERVICE_NAME}:${account}" /user:"${account}" /pass:"${escapedValue}"
`;
execSync(`powershell -Command "${script.replace(/\n/g, ' ')}"`, { stdio: 'ignore' });
return true;
}
async retrieveWindows(account) {
try {
const script = `
$cred = cmdkey /list:"${SERVICE_NAME}:${account}"
if ($cred -match "Password: (.+)") { $matches[1] }
`;
const result = execSync(`powershell -Command "${script.replace(/\n/g, ' ')}"`, { encoding: 'utf8' });
return result.trim() || null;
} catch {
return null;
}
}
async deleteWindows(account) {
execSync(`cmdkey /delete:"${SERVICE_NAME}:${account}"`, { stdio: 'ignore' });
return true;
}
// macOS Keychain implementation
async storeMacOS(account, value) {
// Delete existing entry first
try {
execSync(`security delete-generic-password -s "${SERVICE_NAME}" -a "${account}"`, { stdio: 'ignore' });
} catch {
// Ignore if doesn't exist
}
// Add new entry
execSync(`security add-generic-password -s "${SERVICE_NAME}" -a "${account}" -w "${value}"`, { stdio: 'ignore' });
return true;
}
async retrieveMacOS(account) {
try {
const result = execSync(`security find-generic-password -s "${SERVICE_NAME}" -a "${account}" -w`, { encoding: 'utf8' });
return result.trim() || null;
} catch {
return null;
}
}
async deleteMacOS(account) {
execSync(`security delete-generic-password -s "${SERVICE_NAME}" -a "${account}"`, { stdio: 'ignore' });
return true;
}
// Linux Secret Service implementation
async storeLinux(account, value) {
try {
// Try secret-tool first (libsecret)
execSync(`secret-tool store --label="${SERVICE_NAME}:${account}" service "${SERVICE_NAME}" account "${account}"`, {
input: value,
stdio: ['pipe', 'ignore', 'ignore']
});
return true;
} catch {
// Fallback to gnome-keyring if available
try {
const script = `
echo "${value}" | gnome-keyring-daemon --unlock
echo "${value}" | gnome-keyring --set-password "${SERVICE_NAME}:${account}"
`;
execSync(script, { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
}
async retrieveLinux(account) {
try {
// Try secret-tool first
const result = execSync(`secret-tool lookup service "${SERVICE_NAME}" account "${account}"`, { encoding: 'utf8' });
return result.trim() || null;
} catch {
return null;
}
}
async deleteLinux(account) {
try {
execSync(`secret-tool clear service "${SERVICE_NAME}" account "${account}"`, { stdio: 'ignore' });
return true;
} catch {
return false;
}
}
}
module.exports = new KeychainManager();