Created
July 6, 2020 16:42
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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