using System.Text; using Microsoft.AspNetCore.Authentication; using SharepointToolbox.Web.Core.Models; using SharepointToolbox.Web.Infrastructure.Persistence; using SharepointToolbox.Web.Services.Export; using SharepointToolbox.Web.Services.Session; namespace SharepointToolbox.Web.Services.Audit; public class AuditService : IAuditService { private readonly AuditRepository _repo; private readonly IUserContextAccessor _userContext; public AuditService(AuditRepository repo, IUserContextAccessor userContext) { _repo = repo; _userContext = userContext; } public async Task LogAsync(string action, string clientName, IEnumerable sites, string details = "") { var entry = new AuditEntry { Action = action, ClientName = clientName, Sites = sites.ToList(), Details = details, UserEmail = _userContext.Email, UserDisplay = _userContext.DisplayName, UserRole = _userContext.Role }; await _repo.AppendAsync(entry); } public Task> GetAllAsync() => _repo.LoadAllAsync(); public async Task ExportCsvAsync() { var entries = await _repo.LoadAllAsync(); var sb = new StringBuilder(); sb.AppendLine("Timestamp,UserEmail,UserDisplay,UserRole,Action,Client,Sites,Details"); foreach (var e in entries.OrderByDescending(x => x.Timestamp)) { // CsvSanitizer adds spreadsheet formula-injection guards (= + - @) on top of // RFC 4180 quoting; the user/display/client/site fields are user-controlled. sb.AppendLine(string.Join(",", CsvSanitizer.Escape(e.Timestamp.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")), CsvSanitizer.Escape(e.UserEmail), CsvSanitizer.Escape(e.UserDisplay), CsvSanitizer.Escape(e.UserRole.ToString()), CsvSanitizer.Escape(e.Action), CsvSanitizer.Escape(e.ClientName), CsvSanitizer.Escape(string.Join("; ", e.Sites)), CsvSanitizer.Escape(e.Details))); } return sb.ToString(); } }