Skip to content

Instantly share code, notes, and snippets.

@alasvant
Created July 6, 2020 16:42
Show Gist options
  • Save alasvant/62243055f7edcf3f5b4c19ed6f42fe57 to your computer and use it in GitHub Desktop.
Save alasvant/62243055f7edcf3f5b4c19ed6f42fe57 to your computer and use it in GitHub Desktop.
Sample code related to Episerver custom activity registration and access rights change activity log logging.
using System;
using System.Collections.Generic;
using System.Linq;
using EPiServer.DataAbstraction;
using EPiServer.DataAbstraction.Activities;
using EPiServer.DataAbstraction.Activities.Internal;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.Logging;
using EPiServer.Security;
namespace EpiAuditLogging.Web.Features.AuditLogging
{
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class AuditLogInitializationModule : IInitializableModule
{
private static readonly ILogger Logger = LogManager.GetLogger(typeof(AuditLogInitializationModule));
private IActivityRepository _activityRepository;
public void Initialize(InitializationEngine context)
{
// just DEMO CODE - separate catches just to log the details what really happened
// and return used in each catch just to be able to switch the order or if something is later added
// if there is an Exception we should not continue any execution because the dependencies are not working
try
{
// register the custom ContentSecurityActivity and the action types
RegisterContentSecurityActivity(context.Locate.Advanced.GetInstance<IActivityTypeRegistry>());
}
catch (Exception ex)
{
Logger.Error("Failed to register ContentSecurityActivity and action types.", ex);
return;
}
try
{
_activityRepository = context.Locate.Advanced.GetInstance<IActivityRepository>();
}
catch (Exception ex)
{
Logger.Error("Failed to get IActivityRepository service.", ex);
return;
}
try
{
var repo = context.Locate.Advanced.GetInstance<IContentSecurityRepository>();
repo.ContentSecuritySaved += ContentSecuritySaved;
}
catch (Exception ex)
{
Logger.Error("Failed to register content security saved handler.", ex);
return;
}
}
public void Uninitialize(InitializationEngine context)
{
try
{
var repo = context.Locate.Advanced.GetInstance<IContentSecurityRepository>();
repo.ContentSecuritySaved -= ContentSecuritySaved;
}
catch (Exception ex)
{
Logger.Error("Failed to uninitialize the content security saved handler.", ex);
}
}
private void ContentSecuritySaved(object sender, ContentSecurityEventArg e)
{
try
{
// what access rights changes were made, target can be user or group (including visitor groups if those are set to be usable to protect content)
var permissions = e.ContentSecurityDescriptor?.Entries?.Select(entry => $"{entry.EntityType}: {entry.Name} access level set to: {entry.Access}.");
string creator = e.ContentSecurityDescriptor.Creator; // this is always null/empty, why?
string userFromContext = PrincipalInfo.CurrentPrincipal.Identity.Name; // this is guranteed to return a valid principal
string msg = $"Access rights changed by '{userFromContext}' (creator value: '{creator}') to content id {e.ContentLink}, save type: {e.SecuritySaveType}. Following changes were made: {string.Join(" ", permissions)}";
// just log for reference in the demo
Logger.Information(msg);
// the logged data to activity log
// you could have multiple keys for example to format the data in the 'change log' view
Dictionary<string, string> activityData = new Dictionary<string, string>
{
{ "message", msg }
};
// ContentSecurityActionType uses same values as SecuritySaveType so just casting from e.SecuritySaveType
var activity = new ContentSecurityActivity((ContentSecurityActionType)e.SecuritySaveType, activityData);
var result = _activityRepository.SaveAsync(activity).GetAwaiter().GetResult();
Logger.Information($"New activity saved with id: {result}.");
}
catch (Exception ex)
{
Logger.Error("Failed to handle content security saved event.", ex);
}
}
private void RegisterContentSecurityActivity(IActivityTypeRegistry activityTypeRegistry)
{
if (activityTypeRegistry == null)
{
throw new ArgumentNullException(nameof(activityTypeRegistry));
}
// this is similiar code that the Episerver implementations use to register the activities
// NOTE! The enum value None, value zero is excluded from the list as the UI will never show that
// in the dropdown filter in 'change log' view, assumed to be filter that shows all
// so remember not to use the value zero in your type for anything that 'means' something
ActivityType activityType = new ActivityType(ContentSecurityActivity.ActivityTypeName,
from ContentSecurityActionType x in Enum.GetValues(typeof(ContentSecurityActionType))
where x != ContentSecurityActionType.None
select new ActionType((int)x, x.ToString()));
// the implementation calls AddOrUpdate so it is safe to always call it
// the backing type currently is ConcurrentDictionary not database
activityTypeRegistry.Register(activityType);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment