Identity collectors - Gather contacts associated with a visitor

This guide covers the process of creating an identity collector. Data protection laws such as the European Union’s GDPR often specify that visitors to a site must be able to see their personal data that has been collected, and they can request that it be removed. Identity collection is a key part of this process.

You can read more about GDPR compliance in the Xperience by Kentico Documentation.

Data protection series - prerequisites

This article is part of a series that showcases how you can implement data protection features in Xperience by Kentico. 

We advise starting from the beginning of the Data protection series here, to implement Consent and cookie management.

Alternatively, you can start with this guide to implement data collection and erasure alone. In this case, start with the main branch of this repository.

The example presented in the Data protection guide series is a valid and complete implementation of data protection in Xperience by Kentico. You can simply copy-paste the code samples into your own solution.

However, if you choose to do so, make sure to consult your legal team to determine whether the implementation, texts, and consent levels meet the requirements of your region and market.

Understand the components

In the Configuration → Data Protection application in Xperience by Kentico, you’ll notice tabs for Data portability, Right to access, and Right to be forgotten.

These correspond to aspects of the GDPR but are applicable to other regulations as well, and each is used for dealing with visitors’ personal data.

The Data portability tab, once implemented, allows administrators to gather a visitor’s data in a machine-readable format. In contrast, the Right to access tab collects data in a human-readable format. Finally, the Right to be forgotten tab allows a visitor’s data to be wiped from the site.

However, the code that carries out these actions needs to be implemented, as there is no one-size-fits-all solution to the virtually limitless ways a site could collect and store personal information. This guide will cover just one example of a site with its own specific requirements.

Identify the first step

Data collection and erasure require an Identity collector as the first step.

Identity collectors are used to gather a collection of Xperience objects representing the visitor, called Identities. For example, real-world people could be represented by a User object, one or more Contact objects, or any custom objects, such as Customer, Author, Contractor, etc.

Once the identity collector gathers these identities, it passes them to other components. These components can use these identities to find objects that reference them, e.g. activities performed by a contact.

This guide will cover the process of creating an identity collector, the first step of this process.

Note: The system uses identifiers, such as an email address, to find Identity objects. It is critical that any data you collect and store always contains one of the following, so that it can be found.

  • a reference to one of the identity objects
  • the identifier itself

Consider an example where you use an email address as the identifier and create a form that collects the first and last name of a visitor but no email address. In such a case, there would be no way to find that information using a supplied email address, and GDPR compliance would be impossible when a visitor exercises their right to be forgotten.

Implement the identity collector

This quickstart guide example site uses only contacts as identities — it does not capture visitor data as users or custom objects.

  1. Create a Collectors folder in ~/Features/DataProtection of the TrainingGuides.Web project.

  2. Add a ContactIdentityCollector.cs file to the folder, and add a namespace matching the folder structure (TrainingGuides.Web.Features.DataProtection.Collectors).

  3. Copy the example identity collector from the documentation, and add it to the same namespace.

  4. Add a constant to replace the hard-coded “email”  string, as it occurs more than once.

  5. Define additional logic that creates a new contact with the supplied email address if the query finds none.

    It is possible for form data containing an email address to exist even if there is no contact in the database with that email. Creating this dummy contact allows the Data collector, which will be covered in the next part of this series, to find form data associated with the provided email address in such a case.

ContactIdentityCollector.cs


using CMS.ContactManagement;
using CMS.DataEngine;
using CMS.DataProtection;

namespace TrainingGuides.Web.Features.DataProtection.Collectors;

public class ContactIdentityCollector : IIdentityCollector
{
    private const string EMAIL_KEY = "email";

    public void Collect(IDictionary<string, object> dataSubjectFilter, List<BaseInfo> identities)
    {
        if (!dataSubjectFilter.ContainsKey(EMAIL_KEY))
        {
            return;
        }

        string email = dataSubjectFilter[EMAIL_KEY] as string;

        if (string.IsNullOrWhiteSpace(email))
        {
            return;
        }

        var contacts = ContactInfo.Provider
            .Get()
            .WhereEquals(nameof(ContactInfo.ContactEmail), email)
            .ToList();

        if (contacts.Count() == 0)
        {
            contacts.Add(new ContactInfo() { ContactEmail = email });
        }

        identities.AddRange(contacts);
    }
}

Register the Identity collector

Now that the Identity collector is in place, it can be registered.

  1. Create a new DataProtectionRegistrationModule class in ~/Features/DataProtection/Shared folder of the TrainingGuides.Web project.
  2. Inherit from Xperience’s Module class, and override OnInit to add ContactIdentityCollector to the IdentityCollectorRegister.
  3. Register the module using assembly attribute.
DataProtectionRegistrationModule.cs


using CMS;
using CMS.Core;
using CMS.DataEngine;
using CMS.DataProtection;
using TrainingGuides.Web.Features.DataProtection.Collectors;
using TrainingGuides.Web.Features.DataProtection.Erasers;
using TrainingGuides.Web.Features.DataProtection.Shared;

[assembly: RegisterModule(
    type: typeof(DataProtectionRegistrationModule))]

namespace TrainingGuides.Web.Features.DataProtection.Shared;

public class DataProtectionRegistrationModule : Module
{
    public DataProtectionRegistrationModule()
        : base("DataProtectionRegistration")
    {
    }

    // Contains initialization code that is executed when the application starts
    protected override void OnInit(ModuleInitParameters parameters)
    {
        var serviceProvider = parameters.Services.GetRequiredService<IServiceProvider>();

        base.OnInit(parameters);

        // Adds the ContactIdentityCollector to the collection of registered identity collectors
        IdentityCollectorRegister.Instance.Add(new ContactIdentityCollector());
    }
}

Now, Xperience will know how to utilize the ContactIdentityCollector class for data portability, collection, and erasure requests. When you implement a Data collector, covered in the next guide, Xperience will use the identity collector class to gather a list of contacts and pass them to the data collector to retrieve personal data associated with them.

The IdentityCollectorRegister, like the other data protection registers, is a queue that can contain multiple collectors. If the same class is registered to this queue more than once, it will be called multiple times.

What’s next?

The following guide in this series will cover the process of creating a Data collector and some utility classes to help it run smoothly.