public void Log(AuditObjectData auditObject) { AuditSubjectData auditSubjectData = _auditDataService.Get(); AuditEntity audit = new AuditEntity( auditObjectData: auditObject, auditSubjectData: auditSubjectData); _dbContext.Audit.Add(audit); int changes = _dbContext.SaveChanges(); if (changes <= 0) { _logger.LogError($"Failed to add audit data"); } }
public async Task LogAsync(AuditObjectData auditObject) { AuditSubjectData auditSubjectData = _auditDataService.Get(); AuditEntity audit = new AuditEntity( auditObjectData: auditObject, auditSubjectData: auditSubjectData); _dbContext.Audit.Add(audit); int changes = await _dbContext.SaveChangesAsync(); if (changes <= 0) { _logger.LogError($"Failed to add audit"); } }
public static async Task <ProccessChangeTrackerResult> ProccessChangeTrackerAsync(ChangeTracker changeTracker, AuditOptions auditOptions = null, CancellationToken cancellationToken = default) { if (auditOptions == null) { auditOptions = new AuditOptions(); } List <AuditObjectData> dbAuditDataList = new List <AuditObjectData>(); bool requiresCustomBatch = false; foreach (EntityEntry entry in changeTracker.Entries()) { if (entry.Entity.GetType().GetCustomAttributes(typeof(AuditIgnoreAttribute), true).Any()) { continue; } switch (entry.State) { case Microsoft.EntityFrameworkCore.EntityState.Added: { AuditObjectData auditData; if (entry.IsKeySet) { auditData = new AuditObjectData( actionType: ActionTypes.Add, objectType: entry.Entity.GetType().Name, objectIdentifier: entry.GetPrimaryKey(), objectMetadata: entry.GetMetadata()); } else { PropertyEntry objectIdentifierProperty = entry.Metadata.FindPrimaryKey().Properties .Select(x => entry.Property(x.Name)) .FirstOrDefault(); auditData = new AuditObjectData( actionType: ActionTypes.Add, objectType: entry.Entity.GetType().Name, objectIdentifierProperty: objectIdentifierProperty, objectMetadata: entry.GetMetadata()); requiresCustomBatch = true; } dbAuditDataList.Add(auditData); break; } case Microsoft.EntityFrameworkCore.EntityState.Modified: { AuditObjectData auditData = new AuditObjectData( actionType: ActionTypes.Update, objectType: entry.Entity.GetType().Name, objectIdentifier: entry.GetPrimaryKey(), objectMetadata: entry.GetMetadata(onlyModified: true)); dbAuditDataList.Add(auditData); break; } case Microsoft.EntityFrameworkCore.EntityState.Deleted: { if (auditOptions.AuditCascadeDelete) { IEnumerable <AuditObjectData> cascadeDeleteAuditData = await CascadeDelete(entry, changeTracker, cancellationToken); dbAuditDataList.AddRange(cascadeDeleteAuditData); } AuditObjectData auditData = new AuditObjectData( actionType: ActionTypes.Delete, objectType: entry.Entity.GetType().Name, objectIdentifier: entry.GetPrimaryKey(), objectMetadata: entry.GetMetadata()); dbAuditDataList.Add(auditData); break; } } } return(new ProccessChangeTrackerResult( requiresCustomBatch: requiresCustomBatch, auditObjectData: dbAuditDataList)); }
private static async Task <IEnumerable <AuditObjectData> > CascadeDelete(EntityEntry entityEntry, ChangeTracker changeTracker, CancellationToken cancellationToken) { if (entityEntry.Entity.GetType().GetCustomAttributes(typeof(AuditIgnoreCascadeAttribute), true).Any()) { return(new List <AuditObjectData>()); } List <AuditObjectData> auditObjects = new List <AuditObjectData>(); foreach (NavigationEntry navigation in entityEntry.Navigations) { if (navigation.Metadata.ForeignKey.PrincipalEntityType.ClrType == entityEntry.Entity.GetType() && navigation.Metadata.ForeignKey.DeleteBehavior == Microsoft.EntityFrameworkCore.DeleteBehavior.Cascade) { if (navigation.Metadata.PropertyInfo.GetCustomAttributes(typeof(AuditIgnoreCascadeAttribute)).Any()) { continue; } if (!navigation.IsLoaded) { await navigation.LoadAsync(); } Type type; if (navigation.Metadata.ClrType.IsGenericType) { if (navigation.Metadata.ClrType.GetGenericArguments().Length != 1) { //TODO: log unsupported Trace.TraceInformation($"Unsupported navigation type for cascade delete. {navigation.Metadata.Name}"); continue; } type = navigation.Metadata.ClrType.GetGenericArguments()[0]; } else { type = navigation.Metadata.ClrType; } IEnumerable <EntityEntry> navigationEntries = changeTracker.Entries() .Where(x => x.Entity.GetType() == type); foreach (EntityEntry navigationEntry in navigationEntries) { IEnumerable <AuditObjectData> childrenAuditObjects = await CascadeDelete(navigationEntry, changeTracker, cancellationToken); auditObjects.AddRange(childrenAuditObjects); AuditObjectData auditObjectData = new AuditObjectData( actionType: ActionTypes.Delete, objectType: type.Name, objectIdentifier: navigationEntry.GetPrimaryKey(), objectMetadata: navigationEntry.GetMetadata()); auditObjects.Add(auditObjectData); } } } return(auditObjects); }
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { if (context.HttpContext.Items.TryGetValue(AUDIT_PROCCESSINT_KEY, out object auditProccessing)) { await next(); return; } context.HttpContext.Items.Add(AUDIT_PROCCESSINT_KEY, null); ActionExecutedContext resultContext = await next(); ControllerActionDescriptor actionDescriptor = ((ControllerActionDescriptor)context.ActionDescriptor); if (actionDescriptor.MethodInfo.GetCustomAttributes(true).Any(x => x.GetType() == typeof(AuditIgnoreAttribute))) { return; } if (!actionDescriptor.MethodInfo.CustomAttributes.Any(x => x.AttributeType == typeof(AuditAttribute)) && actionDescriptor.ControllerTypeInfo.GetCustomAttributes(true).Any(x => x.GetType() == typeof(AuditIgnoreAttribute))) { return; } IAuditLogger auditLogger = context.HttpContext.RequestServices.GetRequiredService <IAuditLogger>(); AuditOptions auditOptions = context.HttpContext.RequestServices.GetRequiredService <IOptions <AuditOptions> >().Value; KeyValuePair <string, object> objectIdentifierResult; AuditObjectIdentifierKey objectIdentitiferKey = actionDescriptor.MethodInfo.GetCustomAttribute <AuditObjectIdentifierKey>(); if (objectIdentitiferKey != null) { objectIdentifierResult = resultContext.RouteData.Values .Where(x => x.Key == objectIdentitiferKey.ObjectKey) .SingleOrDefault(); } else { objectIdentifierResult = resultContext.RouteData.Values .Where(x => x.Key != "area") .Where(x => x.Key != "controller") .Where(x => x.Key != "action") .LastOrDefault(); } string objectIdentifier = null; if (objectIdentifierResult.Value != null) { objectIdentifier = objectIdentifierResult.Value.ToString(); } switch (resultContext.Result) { case OkObjectResult _: { OkObjectResult okObjectResult = (OkObjectResult)resultContext.Result; AuditObjectData auditObject = new AuditObjectData( actionType: ActionTypes.Get, objectType: okObjectResult.Value.GetType().GetNameWithGeneric(), objectIdentifier: objectIdentifier, objectMetadata: JsonConvert.SerializeObject(okObjectResult.Value)); await auditLogger.LogAsync(auditObject); break; } case ViewResult _: { ViewResult viewResult = (ViewResult)resultContext.Result; List <AuditObjectData> auditObjects = new List <AuditObjectData>(); if (viewResult.Model != null) { AuditObjectData auditObject = new AuditObjectData( actionType: ActionTypes.Get, objectType: viewResult.GetType().Name, objectIdentifier: objectIdentifier, objectMetadata: JsonConvert.SerializeObject(viewResult.Model)); auditObjects.Add(auditObject); } if (auditOptions.AuditViewData && viewResult.ViewData.Any()) { AuditObjectData auditObject = new AuditObjectData( actionType: ActionTypes.Get, objectType: viewResult.ViewData.GetType().Name, objectIdentifier: objectIdentifier, objectMetadata: JsonConvert.SerializeObject(viewResult.ViewData)); auditObjects.Add(auditObject); } await auditLogger.LogAsync(auditObjects); break; } case BadRequestObjectResult _: { if (!auditOptions.AuditBadRequest) { break; } BadRequestObjectResult badRequestObjectResult = (BadRequestObjectResult)resultContext.Result; AuditObjectData auditObject = new AuditObjectData( actionType: ActionTypes.BadRequest, objectType: badRequestObjectResult.Value.GetType().Name, objectIdentifier: objectIdentifier, objectMetadata: JsonConvert.SerializeObject(badRequestObjectResult.Value)); await auditLogger.LogAsync(auditObject); break; } case FileContentResult _: { FileContentResult fileContentResult = (FileContentResult)resultContext.Result; AuditObjectData auditObjectData = new AuditObjectData( actionType: ActionTypes.Get, objectType: resultContext.Result.GetType().Name, objectIdentifier: objectIdentifier, objectMetadata: JsonConvert.SerializeObject(new { fileContentResult.ContentType, fileContentResult.FileDownloadName })); await auditLogger.LogAsync(auditObjectData); break; } case RedirectResult _: case RedirectToActionResult _: case RedirectToPageResult _: case RedirectToRouteResult _: case LocalRedirectResult _: { break; } default: { if (resultContext.Result != null) { AuditObjectData auditObject = new AuditObjectData( actionType: ActionTypes.Get, objectType: resultContext.Result.GetType().Name, objectIdentifier: objectIdentifier, objectMetadata: null); await auditLogger.LogAsync(auditObject); } break; } } }