blob: 64c138fdb39a6fbf857216515fd1087a88e8217b (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
using System.Security.Claims;
namespace WhatApi.Database;
public class AppDatabase(DbContextOptions<AppDatabase> options, IHttpContextAccessor httpContextAccessor, IConfiguration configuration) : DbContext(options)
{
public DbSet<Content> Content => Set<Content>();
public DbSet<Place> Places => Set<Place>();
public DbSet<User> Users => Set<User>();
public DbSet<AuditTrail> AuditTrails => Set<AuditTrail>();
protected override void OnModelCreating(ModelBuilder b) {
b.HasPostgresExtension("postgis");
b.ApplyConfiguration(new AuditTrailConfiguration());
b.ApplyConfiguration(new PlaceConfiguration());
b.ApplyConfiguration(new UserConfiguration());
b.ApplyConfiguration(new ContentConfiguration());
base.OnModelCreating(b);
}
public override int SaveChanges() {
if (configuration.GetValue<bool>("DISABLE_AUDIT_TRAILS")) return base.SaveChanges();
SetAuditableProperties();
var auditEntries = GetActiveAuditTrails();
if (auditEntries.Count != 0)
AuditTrails.AddRange(auditEntries);
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) {
if (configuration.GetValue<bool>("DISABLE_AUDIT_TRAILS")) return await base.SaveChangesAsync(cancellationToken);
SetAuditableProperties();
var auditEntries = GetActiveAuditTrails();
if (auditEntries.Count != 0)
await AuditTrails.AddRangeAsync(auditEntries, cancellationToken);
return await base.SaveChangesAsync(cancellationToken);
}
private List<AuditTrail> GetActiveAuditTrails() {
var userId = GetUserId();
var entries = ChangeTracker.Entries<IAuditableEntity>()
.Where(e => e.State is EntityState.Added or EntityState.Modified or EntityState.Deleted);
var auditTrails = new List<AuditTrail>();
foreach (var entry in entries) {
var audit = new AuditTrail {
Id = Guid.NewGuid(),
UserId = userId,
EntityName = entry.Entity.GetType().Name,
DateUtc = DateTimeOffset.UtcNow
};
foreach (var prop in entry.Properties) {
if (prop.Metadata.IsPrimaryKey()) {
audit.PrimaryKey = prop.CurrentValue?.ToString();
continue;
}
if (prop.Metadata.PropertyInfo?.CustomAttributes.FirstOrDefault(c => c.AttributeType == typeof(AuditTrailIgnoreAttribute)) != null)
continue;
var name = prop.Metadata.Name;
switch (entry.State) {
case EntityState.Added:
audit.TrailType = TrailType.Create;
audit.NewValues[name] = prop.CurrentValue;
break;
case EntityState.Deleted:
audit.TrailType = TrailType.Delete;
audit.OldValues[name] = prop.OriginalValue;
break;
case EntityState.Modified:
if (!Equals(prop.OriginalValue, prop.CurrentValue)) {
audit.TrailType = TrailType.Update;
audit.ChangedColumns.Add(name);
audit.OldValues[name] = prop.OriginalValue;
audit.NewValues[name] = prop.CurrentValue;
}
break;
}
}
if (audit.TrailType != TrailType.None)
auditTrails.Add(audit);
}
return auditTrails;
}
private Guid GetUserId() {
var system = new Guid("e87ab078-55bc-4655-86d9-c5b2ecad7162");
var userIdString = httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
return string.IsNullOrWhiteSpace(userIdString) ? system : new Guid(userIdString);
}
private void SetAuditableProperties() {
var actor = GetUserId();
foreach (var entry in ChangeTracker.Entries<IAuditableEntity>()) {
switch (entry.State) {
case EntityState.Added:
entry.Entity.CreatedAtUtc = DateTimeOffset.UtcNow;
entry.Entity.CreatedBy = actor;
break;
case EntityState.Modified:
entry.Entity.UpdatedAtUtc = DateTimeOffset.UtcNow;
entry.Entity.UpdatedBy = actor;
break;
}
}
}
}
|