ContentLink ArgumentNullException while saving AccessControlList on EPiServer page created programmatically

I received a disturbing ArgumentNullException today while removing read access for the Everyone role on new pages created in codebehind. Not really difficult once you figure it out, but I thought I’d provide an example to show how it may be done.

EPiServer ContentLink value cannot be null calling ACL.Save method

At first glance it may seem as it is a good idea to take the newly created EPiServer page (line 1 below), update the AccessControlList (ACL) with new values, and then just call the provided save method.

var page = _contentRepository.GetDefault<ArticlePage>(parentReferece, languageSelector);
page.ACL.AddEntry(new AccessControlEntry(EveryoneRole.RoleName, AccessLevel.NoAccess, SecurityEntityType.Role));
page.ACL.Save(SecuritySaveType.Replace);
_contentRepository.Save(page, action, AccessLevel.NoAccess);

The ACL save call (3) however will result in a null reference exception.

System.ArgumentNullException: Value cannot be null.
Parameter name: ContentLink
   at EPiServer.Core.DefaultContentSecurityRepository .Save(ContentReference contentLink, IContentSecurityDescriptor contentSecurityDescriptor, SecuritySaveType securitySaveType)

Even if you swap the order and save the page before trying to affect it’s ACL, you will end up with this exception. In order to get a valid page reference you will have to do something like below.

var page = _contentRepository.GetDefault<ArticlePage>(parentReferece, languageSelector);
var reference = _contentRepository.Save(page, action, AccessLevel.NoAccess);
var acl = new PageAccessControlList(reference) { IsInherited = false };
acl.AddEntry(new AccessControlEntry(EveryoneRole.RoleName, AccessLevel.NoAccess, SecurityEntityType.Role));
acl.AddEntry(new AccessControlEntry("Administrators", AccessLevel.FullAccess, SecurityEntityType.Role));
acl.Save(SecuritySaveType.Replace);

Use the valid ContentReference returned while saving the new page to the EPiServer database in order to get hold of the AccessControlList object. This may then be altered and persisted accordingly. Note that I set the IsInherited property to false as I did not want to use the parent page’s security settings in this example.