Audit Logging with CAP
Introduction
This section deals with Audit Logging for reading sensitive data and changes to personal data. As a prerequisite, you have indicated entities and elements in your domain model, which will contain personal data.
In CAP, audit logging can be handled mostly automatically by adding certain annotations to your business entity definitions and adding some configuration to your project.
❗ Data Subject and Data Object
For each audit log on a data object (like a Sales Order) a valid data subject (like a Customer) is needed. The application has to clarify that this link between data object and data subject - which is typically induced by an annotation like Customer @PersonalData.FieldSemantics : 'DataSubjectID';
- is never broken. Thus, semantically correct audit logs can only be written on top of a semantically correct built application.
Make sure that the data subject is a valid CAP entity, otherwise the metadata-driven automatism will not work.
Audited Object is Data Subject
In our case Customers
is the main master data entity with the semantics of 'Data Subject'.
using { cuid, Country } from '@sap/cds/common';
entity Customers : cuid, managed {
email : String;
firstName : String;
lastName : String;
creditCardNo : String;
dateOfBirth : Date;
postalAddress : Association to CustomerPostalAddress on postalAddress.Customer = $self;
}
using { cuid, Country } from '@sap/cds/common';
entity Customers : cuid, managed {
email : String;
firstName : String;
lastName : String;
creditCardNo : String;
dateOfBirth : Date;
postalAddress : Association to CustomerPostalAddress on postalAddress.Customer = $self;
}
This entity is annotated in the db/data-privacy.cds file.
annotate bookshop.Customers with @PersonalData : {
DataSubjectRole : 'Customer',
EntitySemantics : 'DataSubject'
}
{
ID @PersonalData.FieldSemantics : 'DataSubjectID';
emailAddress @PersonalData.IsPotentiallyPersonal;
firstName @PersonalData.IsPotentiallyPersonal;
lastName @PersonalData.IsPotentiallyPersonal;
creditCardNo @PersonalData.IsPotentiallySensitive;
dateOfBirth @PersonalData.IsPotentiallyPersonal;
}
annotate bookshop.Customers with @AuditLog.Operation : {
Read : true,
Insert : true,
Update : true,
Delete : true
};
annotate bookshop.Customers with @PersonalData : {
DataSubjectRole : 'Customer',
EntitySemantics : 'DataSubject'
}
{
ID @PersonalData.FieldSemantics : 'DataSubjectID';
emailAddress @PersonalData.IsPotentiallyPersonal;
firstName @PersonalData.IsPotentiallyPersonal;
lastName @PersonalData.IsPotentiallyPersonal;
creditCardNo @PersonalData.IsPotentiallySensitive;
dateOfBirth @PersonalData.IsPotentiallyPersonal;
}
annotate bookshop.Customers with @AuditLog.Operation : {
Read : true,
Insert : true,
Update : true,
Delete : true
};
Here we again have the four levels of annotations as already described in the chapter Indicate Personal Data in Your Domain Model.
When you've annotated your (business) entity like this, the audit logs for read access and data modifications will be written automatically by the underlying CAP framework.
In the context of audit logging, the @PersonalData.IsPotentiallyPersonal
field-level annotation is relevant for inducing audit logs for Insert, Update, and Delete, whereas the @PersonalData.IsPotentiallySensitive
annotation is relevant for Read access audit logs.
Warning
The @PersonalData.IsPotentiallySensitive
annotation induces an audit log for each and every Read access. --- Only use this annotation in relevant cases. --- Avoid unnecessary logging activities in your application.
Audited Object is Data Subject Details
In the first example, the audited object was identical to the data subject, but this is not always the case.
In many cases you have additional master data describing more details of the data subject stored in a separate entity. In our terminology this has the semantics 'Data Subject Details'.
In our example we have the additional entity CustomerPostalAddress
which contains additional master data belonging to a certain 'Customer', but which are stored in a separate entity, for better clarity or better separation of concerns.
entity CustomerPostalAddress : cuid, managed {
Customer : Association to one Customers;
street : String(128);
town : String(128);
country : Country;
someOtherField : String(128);
};
entity CustomerPostalAddress : cuid, managed {
Customer : Association to one Customers;
street : String(128);
town : String(128);
country : Country;
someOtherField : String(128);
};
This entity is annotated in the db/data-privacy.cds file.
annotate bookshop.CustomerPostalAddress with @PersonalData : {
DataSubjectRole : 'Customer',
EntitySemantics : 'DataSubjectDetails'
}
{
Customer @PersonalData.FieldSemantics : 'DataSubjectID';
street @PersonalData.IsPotentiallyPersonal;
town @PersonalData.IsPotentiallyPersonal;
country @PersonalData.IsPotentiallyPersonal;
}
annotate bookshop.CustomerPostalAddress with @AuditLog.Operation : {
Read : true,
Insert : true,
Update : true,
Delete : true
};
annotate bookshop.CustomerPostalAddress with @PersonalData : {
DataSubjectRole : 'Customer',
EntitySemantics : 'DataSubjectDetails'
}
{
Customer @PersonalData.FieldSemantics : 'DataSubjectID';
street @PersonalData.IsPotentiallyPersonal;
town @PersonalData.IsPotentiallyPersonal;
country @PersonalData.IsPotentiallyPersonal;
}
annotate bookshop.CustomerPostalAddress with @AuditLog.Operation : {
Read : true,
Insert : true,
Update : true,
Delete : true
};
Very similarly to the section on 'Data Subject' this entity is as well annotated in four levels. More details on these annotations can be found in the chapter Indicate Personal Data in Your Domain Model.
Audited Object is Transactional Data
In the section on 'Data Subject' and 'Data Subject Details' we have seen, how to annotate the master data entities carrying the semantical information of the 'Data Subject'.
Now we have a look at classical transactional data.
In the Personal Data Terminology all transactional data like 'Sales Orders', 'Shipments', 'Payments' are summarizes under the classification 'Other', which means they are relevant for Data Privacy, but they are neither 'Data Subject' nor 'Data Subject Details'. More details on this Terminology can be found in the chapter Indicate Personal Data in Your Domain Model.
In our example we have the entity 'Orders'
entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many OrderItems on Items.parent = $self;
currency : Currency;
Customer : Association to Customers;
personalComment : String;
}
entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many OrderItems on Items.parent = $self;
currency : Currency;
Customer : Association to Customers;
personalComment : String;
}
To ensure proper audit logging we annotate using the usual four levels as described in the chapter Indicate Personal Data in Your Domain Model.
annotate bookshop.Orders with @PersonalData.EntitySemantics : 'Other'
{
ID @PersonalData.FieldSemantics : 'ContractRelatedID';
Customer @PersonalData.FieldSemantics : 'DataSubjectID';
personalComment @PersonalData.IsPotentiallyPersonal;
}
annotate bookshop.Orders with @AuditLog.Operation : {
Read : true,
Insert : true,
Update : true,
Delete : true
};
annotate bookshop.Orders with @PersonalData.EntitySemantics : 'Other'
{
ID @PersonalData.FieldSemantics : 'ContractRelatedID';
Customer @PersonalData.FieldSemantics : 'DataSubjectID';
personalComment @PersonalData.IsPotentiallyPersonal;
}
annotate bookshop.Orders with @AuditLog.Operation : {
Read : true,
Insert : true,
Update : true,
Delete : true
};
Finally, we annotate all standard operations (Read
, Insert
, Update
, Delete
) as relevant for the audit log - which should be the default case for most of the relevant business entities.