using System.Text; using System.Security.Cryptography; // For encryption using System.Text.Json; namespace Blueberry.Redmine { public class RedmineSettingsManager { private readonly string _filePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Blueberry", "settings.json"); public RedmineConfig Load() { if (!File.Exists(_filePath)) return new RedmineConfig() { IsInitiating = true, }; try { var json = File.ReadAllText(_filePath); var config = JsonSerializer.Deserialize(json); if(config == null) return new RedmineConfig(); if (!string.IsNullOrEmpty(config.ApiKey)) { config.ApiKey = Unprotect(config.ApiKey); } return config; } catch { return new RedmineConfig(); } } public void Save(RedmineConfig config) { Directory.CreateDirectory(Path.GetDirectoryName(_filePath) ?? throw new NullReferenceException("Config directory path creation failed.")); var copy = new RedmineConfig { RedmineUrl = config.RedmineUrl, ApiKey = Protect(config.ApiKey), ProjectCacheDuration = config.ProjectCacheDuration, CacheFilePath = config.CacheFilePath, ConcurrencyLimit = config.ConcurrencyLimit, IssueCacheDuration = config.IssueCacheDuration, MaxRetries = config.MaxRetries, IsInitiating = false }; var json = JsonSerializer.Serialize(copy, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_filePath, json); } 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) } } } }