2018-03-20 15:48:18 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using System.Web;
|
|
|
|
|
|
using Umbraco.Core.Events;
|
|
|
|
|
|
using Umbraco.Core.Models;
|
|
|
|
|
|
using Umbraco.Core.Models.Membership;
|
|
|
|
|
|
using Umbraco.Core.Security;
|
|
|
|
|
|
using Umbraco.Core.Services;
|
2018-03-22 17:41:13 +01:00
|
|
|
|
using Umbraco.Core.Services.Implement;
|
2018-03-20 15:48:18 +01:00
|
|
|
|
|
2018-08-29 01:15:46 +10:00
|
|
|
|
namespace Umbraco.Core.Components
|
2018-03-20 15:48:18 +01:00
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
public sealed class AuditEventsComponent : UmbracoComponentBase, IUmbracoCoreComponent
|
2018-03-20 15:48:18 +01:00
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
private IAuditService _auditService;
|
|
|
|
|
|
private IUserService _userService;
|
|
|
|
|
|
private IEntityService _entityService;
|
2018-03-20 15:48:18 +01:00
|
|
|
|
|
|
|
|
|
|
private IUser CurrentPerformingUser
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
var identity = Thread.CurrentPrincipal?.GetUmbracoIdentity();
|
|
|
|
|
|
return identity == null
|
|
|
|
|
|
? new User { Id = 0, Name = "SYSTEM", Email = "" }
|
2018-03-22 17:41:13 +01:00
|
|
|
|
: _userService.GetUserById(Convert.ToInt32(identity.Id));
|
2018-03-20 15:48:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private IUser GetPerformingUser(int userId)
|
|
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
var found = userId >= 0 ? _userService.GetUserById(userId) : null;
|
2018-03-20 15:48:18 +01:00
|
|
|
|
return found ?? new User {Id = 0, Name = "SYSTEM", Email = ""};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string PerformingIp
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
var httpContext = HttpContext.Current == null ? (HttpContextBase) null : new HttpContextWrapper(HttpContext.Current);
|
|
|
|
|
|
var ip = httpContext.GetCurrentRequestIpAddress();
|
|
|
|
|
|
if (ip.ToLowerInvariant().StartsWith("unknown")) ip = "";
|
|
|
|
|
|
return ip;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
public void Initialize(IAuditService auditService, IUserService userService, IEntityService entityService)
|
2018-03-20 15:48:18 +01:00
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService = auditService;
|
|
|
|
|
|
_userService = userService;
|
|
|
|
|
|
_entityService = entityService;
|
2018-03-20 15:48:18 +01:00
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
UserService.SavedUserGroup += OnSavedUserGroupWithUsers;
|
2018-03-20 15:48:18 +01:00
|
|
|
|
|
|
|
|
|
|
UserService.SavedUser += OnSavedUser;
|
|
|
|
|
|
UserService.DeletedUser += OnDeletedUser;
|
|
|
|
|
|
UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned;
|
|
|
|
|
|
|
|
|
|
|
|
MemberService.Saved += OnSavedMember;
|
|
|
|
|
|
MemberService.Deleted += OnDeletedMember;
|
|
|
|
|
|
MemberService.AssignedRoles += OnAssignedRoles;
|
|
|
|
|
|
MemberService.RemovedRoles += OnRemovedRoles;
|
|
|
|
|
|
MemberService.Exported += OnMemberExported;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string FormatEmail(IMember member)
|
|
|
|
|
|
{
|
|
|
|
|
|
return member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string FormatEmail(IUser user)
|
|
|
|
|
|
{
|
|
|
|
|
|
return user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnRemovedRoles(IMemberService sender, RolesEventArgs args)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
var roles = string.Join(", ", args.Roles);
|
|
|
|
|
|
var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x);
|
|
|
|
|
|
foreach (var id in args.MemberIds)
|
|
|
|
|
|
{
|
|
|
|
|
|
members.TryGetValue(id, out var member);
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
|
|
|
|
|
|
"umbraco/member/roles/removed", $"roles modified, removed {roles}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnAssignedRoles(IMemberService sender, RolesEventArgs args)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
var roles = string.Join(", ", args.Roles);
|
|
|
|
|
|
var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x);
|
|
|
|
|
|
foreach (var id in args.MemberIds)
|
|
|
|
|
|
{
|
|
|
|
|
|
members.TryGetValue(id, out var member);
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
|
|
|
|
|
|
"umbraco/member/roles/assigned", $"roles modified, assigned {roles}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnMemberExported(IMemberService sender, ExportedMemberEventArgs exportedMemberEventArgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
var member = exportedMemberEventArgs.Member;
|
|
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
|
|
|
|
|
"umbraco/member/exported", "exported member data");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnSavedUserGroupWithUsers(IUserService sender, SaveEventArgs<UserGroupWithUsers> saveEventArgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
foreach (var groupWithUser in saveEventArgs.SavedEntities)
|
|
|
|
|
|
{
|
|
|
|
|
|
var group = groupWithUser.UserGroup;
|
|
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
var dp = string.Join(", ", ((UserGroup)group).GetWereDirtyProperties());
|
2018-03-20 15:48:18 +01:00
|
|
|
|
var sections = ((UserGroup)group).WasPropertyDirty("AllowedSections")
|
|
|
|
|
|
? string.Join(", ", group.AllowedSections)
|
|
|
|
|
|
: null;
|
|
|
|
|
|
var perms = ((UserGroup)group).WasPropertyDirty("Permissions")
|
|
|
|
|
|
? string.Join(", ", group.Permissions)
|
|
|
|
|
|
: null;
|
|
|
|
|
|
|
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
|
sb.Append($"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)};");
|
|
|
|
|
|
if (sections != null)
|
|
|
|
|
|
sb.Append($", assigned sections: {sections}");
|
|
|
|
|
|
if (perms != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (sections != null)
|
|
|
|
|
|
sb.Append(", ");
|
|
|
|
|
|
sb.Append($"default perms: {perms}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
-1, $"User Group {group.Id} \"{group.Name}\" ({group.Alias})",
|
|
|
|
|
|
"umbraco/user-group/save", $"{sb}");
|
|
|
|
|
|
|
|
|
|
|
|
// now audit the users that have changed
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var user in groupWithUser.RemovedUsers)
|
|
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
user.Id, $"User \"{user.Name}\" {FormatEmail(user)}",
|
|
|
|
|
|
"umbraco/user-group/save", $"Removed user \"{user.Name}\" {FormatEmail(user)} from group {group.Id} \"{group.Name}\" ({group.Alias})");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var user in groupWithUser.AddedUsers)
|
|
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
user.Id, $"User \"{user.Name}\" {FormatEmail(user)}",
|
|
|
|
|
|
"umbraco/user-group/save", $"Added user \"{user.Name}\" {FormatEmail(user)} to group {group.Id} \"{group.Name}\" ({group.Alias})");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void UserGroupPermissionAssigned(IUserService sender, SaveEventArgs<EntityPermission> saveEventArgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
var perms = saveEventArgs.SavedEntities;
|
|
|
|
|
|
foreach (var perm in perms)
|
|
|
|
|
|
{
|
|
|
|
|
|
var group = sender.GetUserGroupById(perm.UserGroupId);
|
|
|
|
|
|
var assigned = string.Join(", ", perm.AssignedPermissions);
|
2018-03-22 17:41:13 +01:00
|
|
|
|
var entity = _entityService.Get(perm.EntityId);
|
2018-03-20 15:48:18 +01:00
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
-1, $"User Group {group.Id} \"{group.Name}\" ({group.Alias})",
|
|
|
|
|
|
"umbraco/user-group/permissions-change", $"assigning {(string.IsNullOrWhiteSpace(assigned) ? "(nothing)" : assigned)} on id:{perm.EntityId} \"{entity.Name}\"");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnSavedMember(IMemberService sender, SaveEventArgs<IMember> saveEventArgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
var members = saveEventArgs.SavedEntities;
|
|
|
|
|
|
foreach (var member in members)
|
|
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
var dp = string.Join(", ", ((Member) member).GetWereDirtyProperties());
|
2018-03-20 15:48:18 +01:00
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
|
|
|
|
|
"umbraco/member/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnDeletedMember(IMemberService sender, DeleteEventArgs<IMember> deleteEventArgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
var members = deleteEventArgs.DeletedEntities;
|
|
|
|
|
|
foreach (var member in members)
|
|
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
|
|
|
|
|
"umbraco/member/delete", $"delete member id:{member.Id} \"{member.Name}\" {FormatEmail(member)}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnSavedUser(IUserService sender, SaveEventArgs<IUser> saveEventArgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
var affectedUsers = saveEventArgs.SavedEntities;
|
|
|
|
|
|
foreach (var affectedUser in affectedUsers)
|
|
|
|
|
|
{
|
|
|
|
|
|
var groups = affectedUser.WasPropertyDirty("Groups")
|
|
|
|
|
|
? string.Join(", ", affectedUser.Groups.Select(x => x.Alias))
|
|
|
|
|
|
: null;
|
|
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
var dp = string.Join(", ", ((User)affectedUser).GetWereDirtyProperties());
|
2018-03-20 15:48:18 +01:00
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
|
|
|
|
|
"umbraco/user/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}{(groups == null ? "" : "; groups assigned: " + groups)}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnDeletedUser(IUserService sender, DeleteEventArgs<IUser> deleteEventArgs)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingUser = CurrentPerformingUser;
|
|
|
|
|
|
var affectedUsers = deleteEventArgs.DeletedEntities;
|
|
|
|
|
|
foreach (var affectedUser in affectedUsers)
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
|
|
|
|
|
"umbraco/user/delete", "delete user");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void WriteAudit(int performingId, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
|
|
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
var performingUser = _userService.GetUserById(performingId);
|
2018-03-20 15:48:18 +01:00
|
|
|
|
|
|
|
|
|
|
var performingDetails = performingUser == null
|
|
|
|
|
|
? $"User UNKNOWN:{performingId}"
|
|
|
|
|
|
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
|
|
|
|
|
|
|
|
|
|
|
|
WriteAudit(performingId, performingDetails, affectedId, ipAddress, eventType, eventDetails, affectedDetails);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void WriteAudit(IUser performingUser, int affectedId, string ipAddress, string eventType, string eventDetails)
|
|
|
|
|
|
{
|
|
|
|
|
|
var performingDetails = performingUser == null
|
|
|
|
|
|
? $"User UNKNOWN"
|
|
|
|
|
|
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
|
|
|
|
|
|
|
|
|
|
|
|
WriteAudit(performingUser?.Id ?? 0, performingDetails, affectedId, ipAddress, eventType, eventDetails);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void WriteAudit(int performingId, string performingDetails, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (affectedDetails == null)
|
|
|
|
|
|
{
|
2018-03-22 17:41:13 +01:00
|
|
|
|
var affectedUser = _userService.GetUserById(affectedId);
|
2018-03-20 15:48:18 +01:00
|
|
|
|
affectedDetails = affectedUser == null
|
|
|
|
|
|
? $"User UNKNOWN:{affectedId}"
|
|
|
|
|
|
: $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-03-22 17:41:13 +01:00
|
|
|
|
_auditService.Write(performingId, performingDetails,
|
2018-03-20 15:48:18 +01:00
|
|
|
|
ipAddress,
|
|
|
|
|
|
DateTime.UtcNow,
|
|
|
|
|
|
affectedId, affectedDetails,
|
|
|
|
|
|
eventType, eventDetails);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|