diff --git a/gdpr/db/AuditLogStore.cds b/gdpr/db/AuditLogStore.cds index cb76a917..b4731d6e 100644 --- a/gdpr/db/AuditLogStore.cds +++ b/gdpr/db/AuditLogStore.cds @@ -1,23 +1,28 @@ -using { managed, cuid, sap.common.CodeList } from '@sap/cds/common'; +using { + managed, + cuid, + sap.common.CodeList +} from '@sap/cds/common'; namespace sap.capire.auditLog; entity AuditLogStore : cuid { - Action : String enum{DataAccess; DataModification}; + Action : String enum { + DataAccess; + DataModification + }; - User : String; - Timestamp : Timestamp; - Tenant : String; - Channel : String; + User : String; + Timestamp : Timestamp; + Tenant : String; + Channel : String; + DataSubjectType : String; // Bussiness Partner + DataSubjectRole : String; // Customer // Employee // ... + DataSubjectID : LargeString; // key value pair as JSON + ObjectType : String; // like SalesOrder + ObjectKey : LargeString; // key value pair as JSON - DataSubjectType : String; // Bussiness Partner - DataSubjectRole : String; // Customer // Employee // ... - DataSubjectID : LargeString; // key value pair as JSON - ObjectType : String; // like SalesOrder - ObjectKey : LargeString; // key value pair as JSON - - Blob : LargeString; // Payload: DataModification or Data Access as BLOB - -} + Blob : LargeString; // Payload: DataModification or Data Access as BLOB +} diff --git a/gdpr/db/data-privacy.cds b/gdpr/db/data-privacy.cds index 10aa6020..ceba64af 100644 --- a/gdpr/db/data-privacy.cds +++ b/gdpr/db/data-privacy.cds @@ -2,65 +2,37 @@ using {sap.capire.bookshop} from './schema'; // annotations for Data Privacy -annotate bookshop.Customers with @PersonalData : { - DataSubjectRole : 'Customer', - EntitySemantics : 'DataSubject' -} -{ - ID @PersonalData.FieldSemantics : 'DataSubjectID'; - email @PersonalData.IsPotentiallyPersonal; - firstName @PersonalData.IsPotentiallyPersonal; - lastName @PersonalData.IsPotentiallyPersonal; - // creditCardNo @PersonalData.IsPotentiallySensitive; - dateOfBirth @PersonalData.IsPotentiallyPersonal; +annotate bookshop.Customers with @PersonalData: { + DataSubjectRole: 'Customer', + EntitySemantics: 'DataSubject' +} { + ID @PersonalData.FieldSemantics : 'DataSubjectID'; + email @PersonalData.IsPotentiallyPersonal; + firstName @PersonalData.IsPotentiallyPersonal; + lastName @PersonalData.IsPotentiallyPersonal; + dateOfBirth @PersonalData.IsPotentiallyPersonal; } -annotate bookshop.CustomerBillingData with @PersonalData : { - DataSubjectRole : 'Customer', - EntitySemantics : 'DataSubjectDetails' -} -{ +annotate bookshop.BillingData with @PersonalData: { + DataSubjectRole: 'Customer', + EntitySemantics: 'DataSubjectDetails' +} { + customer @PersonalData.FieldSemantics : 'DataSubjectID'; creditCardNo @PersonalData.IsPotentiallySensitive; } -annotate bookshop.CustomerPostalAddress with @PersonalData : { - DataSubjectRole : 'Customer', - EntitySemantics : 'DataSubjectDetails' -} -{ - Customer @PersonalData.FieldSemantics : 'DataSubjectID'; +annotate bookshop.Addresses with @PersonalData: { + DataSubjectRole: 'Customer', + EntitySemantics: 'DataSubjectDetails' +} { + customer @PersonalData.FieldSemantics : 'DataSubjectID'; street @PersonalData.IsPotentiallyPersonal; town @PersonalData.IsPotentiallyPersonal; country @PersonalData.IsPotentiallyPersonal; } -annotate bookshop.Orders with @PersonalData.EntitySemantics : 'Other' -{ - ID @PersonalData.FieldSemantics : 'ContractRelatedID'; - Customer @PersonalData.FieldSemantics : 'DataSubjectID'; +annotate bookshop.Orders with @PersonalData.EntitySemantics: 'Other' { + ID @PersonalData.FieldSemantics : 'ContractRelatedID'; + customer @PersonalData.FieldSemantics : 'DataSubjectID'; personalComment @PersonalData.IsPotentiallyPersonal; } - -// annotations for Audit Log -annotate bookshop.Customers with @AuditLog.Operation : { - Read : true, - Insert : true, - Update : true, - Delete : true -}; - -// annotations for Audit Log -annotate bookshop.CustomerPostalAddress with @AuditLog.Operation : { - Read : true, - Insert : true, - Update : true, - Delete : true -}; - -// annotations for Audit Log -annotate bookshop.Orders with @AuditLog.Operation : { - Read : true, - Insert : true, - Update : true, - Delete : true -}; diff --git a/gdpr/db/schema.cds b/gdpr/db/schema.cds index c9b01c08..e8112892 100644 --- a/gdpr/db/schema.cds +++ b/gdpr/db/schema.cds @@ -1,37 +1,41 @@ // Proxy for importing schema from bookshop sample -using { sap.capire.bookshop.Books } from '../../bookshop/db/schema'; -using { sap.capire.orders.Orders } from '../../orders/db/schema'; -using { sap.capire.orders.OrderItems } from '../../orders/db/schema'; -using { Country, managed, cuid } from '@sap/cds/common'; +using {sap.capire.bookshop.Books} from '../../bookshop/db/schema'; +using {sap.capire.orders.Orders} from '../../orders/db/schema'; +using {sap.capire.orders.OrderItems} from '../../orders/db/schema'; +using { + Country, + managed, + cuid +} from '@sap/cds/common'; namespace sap.capire.bookshop; -extend Orders with { - Customer : Association to Customers; +extend Orders with { + customer : Association to Customers; personalComment : String; -} +} entity Customers : cuid, managed { - email : String; - firstName : String; - lastName : String; - // creditCardNo : String; - dateOfBirth : Date; - billingData : Composition of one CustomerBillingData on billingData.Customer = $self; - postalAddress : Composition of one CustomerPostalAddress on postalAddress.Customer = $self; + email : String; + firstName : String; + lastName : String; + dateOfBirth : Date; + billingData : Composition of BillingData + on billingData.customer = $self; + addresses : Composition of Addresses + on addresses.customer = $self; } -entity CustomerPostalAddress : cuid, managed { - Customer : Association to one Customers; +entity Addresses : cuid, managed { + customer : Association to one Customers; street : String(128); town : String(128); - country : Country; + country : Country; someOtherField : String(128); }; -entity CustomerBillingData : cuid, managed { - Customer : Association to one Customers; - creditCardNo : String; +entity BillingData : cuid, managed { + customer : Association to one Customers; + creditCardNo : String; }; - diff --git a/gdpr/srv/customAuditLog.js b/gdpr/srv/customAuditLog.js index e4c81073..ec5354ff 100644 --- a/gdpr/srv/customAuditLog.js +++ b/gdpr/srv/customAuditLog.js @@ -1,8 +1,8 @@ const cds = require('@sap/cds') +// FIXME: no longer works like this with new audit logging plugin module.exports = class MyAuditLogService extends cds.AuditLogService { async init() { - // console.log('My Audit Log'); // call AuditLogService's init await super.init() @@ -12,132 +12,59 @@ module.exports = class MyAuditLogService extends cds.AuditLogService { // register custom handlers this.on('dataAccessLog', async req => { - - const logs = []; - - const action = 'DataAccess'; - const user = req.user.id; - const timestamp = req.timestamp; - const tenant = req.tenant; - const channel = req.channel; - - req.data.accesses.forEach( dataAccess => { - logs.push({ - Action: action, - User: user, - Timestamp: timestamp, - Tenant: tenant, - Channel: channel, - DataSubjectType: dataAccess.dataSubject.type, - DataSubjectRole: dataAccess.dataSubject.role, - DataSubjectID: JSON.stringify(dataAccess.dataSubject.id), - ObjectType: dataAccess.dataObject.type, - ObjectKey: JSON.stringify(dataAccess.dataObject.id), - Blob: JSON.stringify(dataAccess) - }) } - ) - - - await INSERT.into(AuditLogStore).entries(logs) - } - ) + const logs = [] + + const action = 'DataAccess' + const user = req.user.id + const timestamp = req.timestamp + const tenant = req.tenant + const channel = req.channel + + req.data.accesses.forEach(dataAccess => { + logs.push({ + Action: action, + User: user, + Timestamp: timestamp, + Tenant: tenant, + Channel: channel, + DataSubjectType: dataAccess.data_subject.type, + DataSubjectRole: dataAccess.data_subject.role, + DataSubjectID: JSON.stringify(dataAccess.data_subject.id), + ObjectType: dataAccess.object.type, + ObjectKey: JSON.stringify(dataAccess.object.id), + Blob: JSON.stringify(dataAccess) + }) + }) + + await INSERT.into(AuditLogStore).entries(logs) + }) this.on('dataModificationLog', async req => { + const mods = [] - const mods = []; - - const action = 'DataModification'; - const user = req.user.id; - const timestamp = req.timestamp; - const tenant = req.tenant; - const channel = req.channel; + const action = 'DataModification' + const user = req.user.id + const timestamp = req.timestamp + const tenant = req.tenant + const channel = req.channel - req.data.modifications.forEach( dataModification => { + req.data.modifications.forEach(dataModification => { mods.push({ - Action: action, - User: user, - Timestamp: timestamp, - Tenant: tenant, - Channel: channel, - DataSubjectType: dataModification.dataSubject.type, - DataSubjectRole: dataModification.dataSubject.role, - DataSubjectID: JSON.stringify(dataModification.dataSubject.id), - ObjectType: dataModification.dataObject.type, - ObjectKey: JSON.stringify(dataModification.dataObject.id), - Blob: JSON.stringify(dataModification) - }) } - ) - - + Action: action, + User: user, + Timestamp: timestamp, + Tenant: tenant, + Channel: channel, + DataSubjectType: dataModification.data_subject.type, + DataSubjectRole: dataModification.data_subject.role, + DataSubjectID: JSON.stringify(dataModification.data_subject.id), + ObjectType: dataModification.object.type, + ObjectKey: JSON.stringify(dataModification.object.id), + Blob: JSON.stringify(dataModification) + }) + }) await INSERT.into(AuditLogStore).entries(mods) - } - ) - } - - - - - - - - } - -/* -service AuditLogService { - - // SEC-254: Log read access to sensitive personal data - event dataAccessLog { - accesses : array of Access; - }; - - // SEC-265: Log changes to personal data - event dataModificationLog : { - c : array of DataModification; - }; -} -*/ - - -/* -define type KeyValuePair { - keyName : String; - value : String; -}; - -define type DataObject { - type : String; - id : array of KeyValuePair; -}; - -define type DataSubject { - type : String; - id : array of KeyValuePair; - role : String; -}; - -define type Attribute { - name : String; -}; - - -define type Access { - dataObject : DataObject; - dataSubject : DataSubject; - attributes : array of Attribute; - attachments : array of Attachment; -}; - -define type ChangedAttribute { - name : String; - oldValue : String; - newValue : String; -}; - -define type DataModification { - dataObject : DataObject; - dataSubject : DataSubject; - action : String @assert.range enum { Create; Update; Delete; }; - attributes : array of ChangedAttribute; + }) + } } -*/ \ No newline at end of file diff --git a/gdpr/srv/log-service.cds b/gdpr/srv/log-service.cds index 03ba7267..819f6c26 100644 --- a/gdpr/srv/log-service.cds +++ b/gdpr/srv/log-service.cds @@ -1,14 +1,13 @@ -using {sap.capire.bookshop as db} from '../db/data-privacy'; -using {sap.capire.orders as dbo} from '../db/data-privacy'; -using {sap.capire.auditLog as log} from '../db/AuditLogStore.cds'; +using {sap.capire.bookshop as db} from '../db/data-privacy'; +using {sap.capire.orders as dbo} from '../db/data-privacy'; +using {sap.capire.auditLog as log} from '../db/AuditLogStore.cds'; //@requires: 'PersonalDataManagerUser' // security check service LogService { - entity Customers as projection on db.Customers; - entity CustomerPostalAddress as projection on db.CustomerPostalAddress; - entity Orders as projection on dbo.Orders; + entity Customers as projection on db.Customers; + entity Addresses as projection on db.Addresses; + entity Orders as projection on dbo.Orders; + entity AuditLogStore as projection on log.AuditLogStore; - entity AuditLogStore as projection on log.AuditLogStore; - }; diff --git a/gdpr/srv/pdm-service.cds b/gdpr/srv/pdm-service.cds index 94150d61..458ffc8a 100644 --- a/gdpr/srv/pdm-service.cds +++ b/gdpr/srv/pdm-service.cds @@ -1,44 +1,43 @@ -using {sap.capire.bookshop as db} from '../db/data-privacy'; -using {sap.capire.bookshop.Books} from '../db/data-privacy'; -using {sap.capire.orders.Orders} from '../db/data-privacy'; -using {sap.capire.orders.OrderItems} from '../db/data-privacy'; +using {sap.capire.bookshop as db} from '../db/data-privacy'; +using {sap.capire.bookshop.Books} from '../db/data-privacy'; +using {sap.capire.orders.Orders} from '../db/data-privacy'; +using {sap.capire.orders.OrderItems} from '../db/data-privacy'; //@requires: 'PersonalDataManagerUser' // security check service PDMService { - entity Customers as projection on db.Customers; - entity CustomerPostalAddress as projection on db.CustomerPostalAddress; - entity CustomerBillingData as projection on db.CustomerBillingData; + // Data Privacy annotations on 'Customers', 'Addresses', and 'BillingData' are derived from original entity definitions + entity Customers as projection on db.Customers; + entity Addresses as projection on db.Addresses; + entity BillingData as projection on db.BillingData; // create view on Orders and Items as flat projection - entity OrderItemView as + entity OrderItemView as select from Orders { ID, - key Items.ID as Item_ID, + key Items.ID as item_ID, OrderNo, - Customer.ID as Customer_ID, - Customer.email as Customer_Email, - Items.book.ID as Item_Book_ID, - Items.amount as Item_Amount, - Items.netAmount as Item_NetAmount + customer.ID as customer_ID, + customer.email as customer_email, + Items.book.ID as item_Book_ID, + Items.amount as item_Amount, + Items.netAmount as item_NetAmount }; // annotate new view - annotate PDMService.OrderItemView with @(PersonalData.EntitySemantics : 'Other') { - Item_ID @PersonalData.FieldSemantics : 'ContractRelatedID'; - Customer_ID @PersonalData.FieldSemantics : 'DataSubjectID'; - Customer_Email @PersonalData.IsPotentiallyPersonal; + annotate PDMService.OrderItemView with @(PersonalData.EntitySemantics: 'Other') { + item_ID @PersonalData.FieldSemantics: 'ContractRelatedID'; + customer_ID @PersonalData.FieldSemantics: 'DataSubjectID'; + customer_email @PersonalData.IsPotentiallyPersonal; }; // annotations for Personal Data Manager - Search Fields - annotate Customers with @(Communication.Contact : { - n : { - surname : lastName, - given : firstName + annotate Customers with @(Communication.Contact: { + n : { + surname: lastName, + given : firstName }, - bday : dateOfBirth + bday: dateOfBirth }); - // Data Privacy annotations on 'Customers' and 'CustomerPostalAddress' are derived from original entity definitions - };