2015-01-19 15:12:34 +11:00
|
|
|
using System;
|
2017-09-07 17:27:37 +10:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2015-07-23 20:04:40 +02:00
|
|
|
using Umbraco.Core.Events;
|
2015-01-19 18:37:48 +11:00
|
|
|
using Umbraco.Core.Logging;
|
2015-01-19 15:12:34 +11:00
|
|
|
using Umbraco.Core.Models;
|
|
|
|
|
using Umbraco.Core.Persistence;
|
2017-09-07 17:27:37 +10:00
|
|
|
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
|
|
|
|
using Umbraco.Core.Persistence.Querying;
|
2015-01-19 15:12:34 +11:00
|
|
|
using Umbraco.Core.Persistence.UnitOfWork;
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Core.Services
|
|
|
|
|
{
|
2017-01-19 15:37:17 +11:00
|
|
|
public sealed class AuditService : ScopeRepositoryService, IAuditService
|
2015-01-19 15:12:34 +11:00
|
|
|
{
|
2018-02-08 10:16:53 +01:00
|
|
|
private readonly Lazy<bool> _isAvailable;
|
|
|
|
|
|
2015-07-23 20:04:40 +02:00
|
|
|
public AuditService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory)
|
|
|
|
|
: base(provider, repositoryFactory, logger, eventMessagesFactory)
|
2018-02-08 10:16:53 +01:00
|
|
|
{
|
|
|
|
|
_isAvailable = new Lazy<bool>(DetermineIsAvailable);
|
|
|
|
|
}
|
2015-01-19 15:12:34 +11:00
|
|
|
|
|
|
|
|
public void Add(AuditType type, string comment, int userId, int objectId)
|
|
|
|
|
{
|
2017-01-25 13:34:41 +01:00
|
|
|
using (var uow = UowProvider.GetUnitOfWork())
|
2015-01-19 15:12:34 +11:00
|
|
|
{
|
2017-01-25 13:34:41 +01:00
|
|
|
var repo = RepositoryFactory.CreateAuditRepository(uow);
|
2015-01-19 15:12:34 +11:00
|
|
|
repo.AddOrUpdate(new AuditItem(objectId, comment, type, userId));
|
|
|
|
|
uow.Commit();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-07 17:27:37 +10:00
|
|
|
|
2017-09-07 22:03:49 +10:00
|
|
|
/// <summary>
|
|
|
|
|
/// Returns paged items in the audit trail for a given entity
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="entityId"></param>
|
|
|
|
|
/// <param name="pageIndex"></param>
|
|
|
|
|
/// <param name="pageSize"></param>
|
|
|
|
|
/// <param name="totalRecords"></param>
|
|
|
|
|
/// <param name="orderDirection">
|
|
|
|
|
/// By default this will always be ordered descending (newest first)
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="auditTypeFilter">
|
|
|
|
|
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
|
|
|
|
|
/// so we need to do that here
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="customFilter">
|
|
|
|
|
/// Optional filter to be applied
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public IEnumerable<IAuditItem> GetPagedItemsByEntity(int entityId, long pageIndex, int pageSize, out long totalRecords,
|
|
|
|
|
Direction orderDirection = Direction.Descending,
|
|
|
|
|
AuditType[] auditTypeFilter = null,
|
|
|
|
|
IQuery<IAuditItem> customFilter = null)
|
2017-09-07 17:27:37 +10:00
|
|
|
{
|
|
|
|
|
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
|
|
|
|
|
Mandate.ParameterCondition(pageSize > 0, "pageSize");
|
|
|
|
|
|
2017-09-07 22:03:49 +10:00
|
|
|
if (entityId == Constants.System.Root || entityId <= 0)
|
2017-09-07 17:27:37 +10:00
|
|
|
{
|
|
|
|
|
totalRecords = 0;
|
|
|
|
|
return Enumerable.Empty<IAuditItem>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
|
|
|
|
{
|
|
|
|
|
var repository = RepositoryFactory.CreateAuditRepository(uow);
|
|
|
|
|
|
2017-09-07 22:03:49 +10:00
|
|
|
var query = Query<IAuditItem>.Builder.Where(x => x.Id == entityId);
|
2017-09-07 17:27:37 +10:00
|
|
|
|
2017-09-07 22:03:49 +10:00
|
|
|
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderDirection, auditTypeFilter, customFilter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns paged items in the audit trail for a given user
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="userId"></param>
|
|
|
|
|
/// <param name="pageIndex"></param>
|
|
|
|
|
/// <param name="pageSize"></param>
|
|
|
|
|
/// <param name="totalRecords"></param>
|
|
|
|
|
/// <param name="orderDirection">
|
|
|
|
|
/// By default this will always be ordered descending (newest first)
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="auditTypeFilter">
|
|
|
|
|
/// Since we currently do not have enum support with our expression parser, we cannot query on AuditType in the query or the custom filter
|
|
|
|
|
/// so we need to do that here
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="customFilter">
|
|
|
|
|
/// Optional filter to be applied
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public IEnumerable<IAuditItem> GetPagedItemsByUser(int userId, long pageIndex, int pageSize, out long totalRecords, Direction orderDirection = Direction.Descending, AuditType[] auditTypeFilter = null, IQuery<IAuditItem> customFilter = null)
|
|
|
|
|
{
|
|
|
|
|
Mandate.ParameterCondition(pageIndex >= 0, "pageIndex");
|
|
|
|
|
Mandate.ParameterCondition(pageSize > 0, "pageSize");
|
|
|
|
|
|
|
|
|
|
if (userId < 0)
|
|
|
|
|
{
|
|
|
|
|
totalRecords = 0;
|
|
|
|
|
return Enumerable.Empty<IAuditItem>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
|
|
|
|
{
|
|
|
|
|
var repository = RepositoryFactory.CreateAuditRepository(uow);
|
|
|
|
|
|
|
|
|
|
var query = Query<IAuditItem>.Builder.Where(x => x.UserId == userId);
|
|
|
|
|
|
|
|
|
|
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderDirection, auditTypeFilter, customFilter);
|
2017-09-07 17:27:37 +10:00
|
|
|
}
|
|
|
|
|
}
|
2018-02-06 19:07:54 +01:00
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public IAuditEntry Write(int performingUserId, string perfomingDetails, string performingIp, DateTime eventDate, int affectedUserId, string affectedDetails, string eventType, string eventDetails)
|
|
|
|
|
{
|
2018-02-12 17:37:52 +11:00
|
|
|
if (performingUserId < 0) throw new ArgumentOutOfRangeException(nameof(performingUserId));
|
|
|
|
|
if (string.IsNullOrWhiteSpace(perfomingDetails)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(perfomingDetails));
|
|
|
|
|
if (string.IsNullOrWhiteSpace(eventType)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventType));
|
|
|
|
|
if (string.IsNullOrWhiteSpace(eventDetails)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventDetails));
|
|
|
|
|
|
|
|
|
|
//validate the eventType - must contain a forward slash, no spaces, no special chars
|
|
|
|
|
var eventTypeParts = eventType.ToCharArray();
|
|
|
|
|
if (eventTypeParts.Contains('/') == false || eventTypeParts.All(c => char.IsLetterOrDigit(c) || c == '/' || c == '-') == false)
|
|
|
|
|
throw new ArgumentException(nameof(eventType) + " must contain only alphanumeric characters, hyphens and at least one '/' defining a category");
|
|
|
|
|
|
2018-02-06 19:07:54 +01:00
|
|
|
var entry = new AuditEntry
|
|
|
|
|
{
|
|
|
|
|
PerformingUserId = performingUserId,
|
|
|
|
|
PerformingDetails = perfomingDetails,
|
|
|
|
|
PerformingIp = performingIp,
|
|
|
|
|
EventDate = eventDate,
|
|
|
|
|
AffectedUserId = affectedUserId,
|
|
|
|
|
AffectedDetails = affectedDetails,
|
|
|
|
|
EventType = eventType,
|
|
|
|
|
EventDetails = eventDetails
|
|
|
|
|
};
|
|
|
|
|
|
2018-02-08 10:16:53 +01:00
|
|
|
if (_isAvailable.Value == false) return entry;
|
|
|
|
|
|
2018-02-06 19:07:54 +01:00
|
|
|
using (var uow = UowProvider.GetUnitOfWork())
|
|
|
|
|
{
|
|
|
|
|
var repository = RepositoryFactory.CreateAuditEntryRepository(uow);
|
|
|
|
|
repository.AddOrUpdate(entry);
|
|
|
|
|
uow.Commit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public IEnumerable<IAuditEntry> Get()
|
|
|
|
|
{
|
2018-02-08 10:16:53 +01:00
|
|
|
if (_isAvailable.Value == false) return Enumerable.Empty<IAuditEntry>();
|
|
|
|
|
|
2018-02-06 19:07:54 +01:00
|
|
|
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
|
|
|
|
{
|
|
|
|
|
var repository = RepositoryFactory.CreateAuditEntryRepository(uow);
|
|
|
|
|
return repository.GetAll();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public IEnumerable<IAuditEntry> GetPage(long pageIndex, int pageCount, out long records)
|
|
|
|
|
{
|
2018-02-08 10:16:53 +01:00
|
|
|
if (_isAvailable.Value == false)
|
|
|
|
|
{
|
|
|
|
|
records = 0;
|
|
|
|
|
return Enumerable.Empty<IAuditEntry>();
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-06 19:07:54 +01:00
|
|
|
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
|
|
|
|
{
|
|
|
|
|
var repository = RepositoryFactory.CreateAuditEntryRepository(uow);
|
|
|
|
|
return repository.GetPage(pageIndex, pageCount, out records);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-08 10:16:53 +01:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Determines whether the repository is available.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool DetermineIsAvailable()
|
|
|
|
|
{
|
|
|
|
|
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
|
|
|
|
{
|
|
|
|
|
var repository = RepositoryFactory.CreateAuditEntryRepository(uow);
|
|
|
|
|
return repository.IsAvailable();
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-01-19 15:12:34 +11:00
|
|
|
}
|
2018-02-06 19:07:54 +01:00
|
|
|
}
|