---
title: Write custom attributes
slug: cms-12/docs/writing-custom-attributes
docTags: 
createdAt: 2026-08-10T15:56:01.000Z
---

## Restrict access for editing a certain property

You can restrict access in the following ways:

- Create a `ValidationAttribute` to add server-side validation when data is saved.
- Implement `IDisplayMetadataProvider` for your attribute to add metadata that affects the editing experience of the property.

You can combine the implementation of the `IDisplayMetadataProvider` attribute with a `ValidationAttribut`e to control which roles can edit a specific property.

The following example shows how to make a property read-only in the editorial interface and adds server-side validation if the content is changed outside the editorial interface through an attribute:

```csharp
using EPiServer.Core;
using EPiServer.Security;
using EPiServer.Shell.ObjectEditing;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace EPiServer.Samples {
  public class PropertyEditRestrictionAttribute: ValidationAttribute, IDisplayMetadataProvider {
    public PropertyEditRestrictionAttribute(string[] allowedRoles) {
      AllowedRoles = allowedRoles;
    }

    public string[] AllowedRoles {
      get;
      set;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
      if (validationContext.ObjectInstance is IContentData content) {
        if (!content.Property[validationContext.MemberName].IsModified) {
          return ValidationResult.Success;
        }

        return IsInRole ? ValidationResult.Success : new ValidationResult("You do not have access");
      }

      //This attribute only handles instances of IContentData. return ValidationResult.Success;
      return ValidationResult.Success;
    }

    public void CreateDisplayMetadata(DisplayMetadataProviderContext context) {
      if (IsInRole) {
        return;
      }

      var extendedMetadata = context.DisplayMetadata.AdditionalValues[ExtendedMetadata.ExtendedMetadataDisplayKey] as ExtendedMetadata;
      if (extendedMetadata == null) {
        return;
      }
      extendedMetadata.IsReadOnly = true;
    }

    private bool IsInRole => AllowedRoles.Any(r => PrincipalInfo.CurrentPrincipal.IsInRole(r));

  }
}
```

Usage on the model should look as follows. The example uses the group name **administrators2** to ensure you are not a member of this group.

```csharp
[PropertyEditRestriction(new string[] {
  "administrators2"
})]
public virtual XhtmlString MainBody {
  get;
  set;
}
```

Go to the Optimizely edit view to see that the editor was disabled in the **All Properties** editing view:

![Optimizely edit view shows that the editor was disabled in the All Properties editing view](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/T9QMoIGVp_OczdUPD32QR-20260722-184800.png "readonly_thumb.png")

In the **On-page** editing view, the property looks editable. Still, if you cannot edit the property and click on the overlay, edit view loads a read-only editor and removes the overlay entirely.

![the property looks editable](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/i44Cwy1TqGPFk6YTXqzE0-20260722-184800.png "readonlyonpageedit_thumb.png")

## Validate the server

The example above adds server validation to the same attribute from the example in the previous section, so you cannot post a fake property update to the server without the correct access.

You can comment out the line that makes the property read-only and edit the property to validate that the server validation works.
