using Microsoft.Extensions.Logging; using System.Security.Cryptography; using System.Text; using System.Text.Json; namespace RmmAgent.Security; public class ConfigStore { private readonly ILogger _logger; private readonly string _tokenPath; private readonly string _statePath; private AgentState _state; public ConfigStore(string dataDir, ILogger logger) { _logger = logger; var secureDir = Path.Combine(dataDir, "secure"); Directory.CreateDirectory(secureDir); _tokenPath = Path.Combine(secureDir, "agent.token.enc"); _statePath = Path.Combine(dataDir, "state.json"); _state = LoadState(); } public AgentState State => _state; public bool HasAgentToken() => File.Exists(_tokenPath); public string? GetAgentToken() { if (!HasAgentToken()) return null; try { var encrypted = File.ReadAllBytes(_tokenPath); var decrypted = ProtectedData.Unprotect(encrypted, null, DataProtectionScope.LocalMachine); return Encoding.UTF8.GetString(decrypted); } catch (Exception ex) { _logger.LogError(ex, "Failed to decrypt agent token"); return null; } } public void SaveAgentToken(string token, string agentId) { var encrypted = ProtectedData.Protect(Encoding.UTF8.GetBytes(token), null, DataProtectionScope.LocalMachine); File.WriteAllBytes(_tokenPath, encrypted); _state.AgentId = agentId; _state.EnrolledAt = DateTime.UtcNow; SaveState(); _logger.LogInformation("Agent token saved (encrypted DPAPI). AgentId={AgentId}", agentId); } public void ClearAgentToken() { try { File.Delete(_tokenPath); } catch { } _state.AgentId = null; _state.EnrolledAt = null; SaveState(); } private AgentState LoadState() { try { if (File.Exists(_statePath)) { var json = File.ReadAllText(_statePath); return JsonSerializer.Deserialize(json) ?? new AgentState(); } } catch (Exception ex) { _logger.LogWarning(ex, "Failed to load state"); } return new AgentState(); } public void SaveState() { try { var json = JsonSerializer.Serialize(_state, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_statePath, json); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to save state"); } } } public class AgentState { public string? AgentId { get; set; } public DateTime? EnrolledAt { get; set; } }