using System.Text; using BlueMine.Redmine; using System.IO; using System.Security.Cryptography; // For encryption using System.Text.Json; namespace BlueMine { public class SettingsManager { // Save to: C:\Users\Username\AppData\Roaming\YourAppName\settings.json private readonly string _filePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Blueberry", "settings.json"); public RedmineConfig Load() { if (!File.Exists(_filePath)) return new RedmineConfig(); try { var json = File.ReadAllText(_filePath); var config = JsonSerializer.Deserialize(json); if(config == null) return new RedmineConfig(); // Decrypt the API Key if it exists if (!string.IsNullOrEmpty(config.ApiKey)) { config.ApiKey = Unprotect(config.ApiKey); } return config; } catch { // If file is corrupted, return default return new RedmineConfig(); } } public void Save(RedmineConfig config) { // Create directory if it doesn't exist Directory.CreateDirectory(Path.GetDirectoryName(_filePath) ?? throw new NullReferenceException("Config directory path creation failed.")); // Create a copy to encrypt so we don't mess up the runtime object var copy = new RedmineConfig { RedmineUrl = config.RedmineUrl, // Encrypt the key before saving ApiKey = Protect(config.ApiKey), ProjectCacheDuration = config.ProjectCacheDuration, // ... copy other fields }; var json = JsonSerializer.Serialize(copy, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_filePath, json); } // --- ENCRYPTION HELPERS (DPAPI) --- // This encrypts data using the current user's Windows credentials. // Only this user on this machine can decrypt it. private string Protect(string clearText) { if (string.IsNullOrEmpty(clearText)) return ""; byte[] clearBytes = Encoding.UTF8.GetBytes(clearText); byte[] encryptedBytes = ProtectedData.Protect(clearBytes, null, DataProtectionScope.CurrentUser); return Convert.ToBase64String(encryptedBytes); } private string Unprotect(string encryptedText) { if (string.IsNullOrEmpty(encryptedText)) return ""; try { byte[] encryptedBytes = Convert.FromBase64String(encryptedText); byte[] clearBytes = ProtectedData.Unprotect(encryptedBytes, null, DataProtectionScope.CurrentUser); return Encoding.UTF8.GetString(clearBytes); } catch { return ""; // Decryption failed (maybe different user/machine) } } } }