Compare commits

...

2 Commits

Author SHA1 Message Date
D050513
673fec080a more 2023-07-30 22:49:59 +02:00
D050513
6f7b786ca0 first set of adjustments 2023-07-30 22:30:12 +02:00
6 changed files with 147 additions and 241 deletions

View File

@@ -1,16 +1,22 @@
using { managed, cuid, sap.common.CodeList } from '@sap/cds/common'; using {
managed,
cuid,
sap.common.CodeList
} from '@sap/cds/common';
namespace sap.capire.auditLog; namespace sap.capire.auditLog;
entity AuditLogStore : cuid { entity AuditLogStore : cuid {
Action : String enum{DataAccess; DataModification}; Action : String enum {
DataAccess;
DataModification
};
User : String; User : String;
Timestamp : Timestamp; Timestamp : Timestamp;
Tenant : String; Tenant : String;
Channel : String; Channel : String;
DataSubjectType : String; // Bussiness Partner DataSubjectType : String; // Bussiness Partner
DataSubjectRole : String; // Customer // Employee // ... DataSubjectRole : String; // Customer // Employee // ...
DataSubjectID : LargeString; // key value pair as JSON DataSubjectID : LargeString; // key value pair as JSON
@@ -20,4 +26,3 @@ entity AuditLogStore : cuid {
Blob : LargeString; // Payload: DataModification or Data Access as BLOB Blob : LargeString; // Payload: DataModification or Data Access as BLOB
} }

View File

@@ -2,65 +2,37 @@
using {sap.capire.bookshop} from './schema'; using {sap.capire.bookshop} from './schema';
// annotations for Data Privacy // annotations for Data Privacy
annotate bookshop.Customers with @PersonalData : { annotate bookshop.Customers with @PersonalData: {
DataSubjectRole : 'Customer', DataSubjectRole: 'Customer',
EntitySemantics : 'DataSubject' EntitySemantics: 'DataSubject'
} } {
{
ID @PersonalData.FieldSemantics : 'DataSubjectID'; ID @PersonalData.FieldSemantics : 'DataSubjectID';
email @PersonalData.IsPotentiallyPersonal; email @PersonalData.IsPotentiallyPersonal;
firstName @PersonalData.IsPotentiallyPersonal; firstName @PersonalData.IsPotentiallyPersonal;
lastName @PersonalData.IsPotentiallyPersonal; lastName @PersonalData.IsPotentiallyPersonal;
// creditCardNo @PersonalData.IsPotentiallySensitive;
dateOfBirth @PersonalData.IsPotentiallyPersonal; dateOfBirth @PersonalData.IsPotentiallyPersonal;
} }
annotate bookshop.CustomerBillingData with @PersonalData : { annotate bookshop.BillingData with @PersonalData: {
DataSubjectRole : 'Customer', DataSubjectRole: 'Customer',
EntitySemantics : 'DataSubjectDetails' EntitySemantics: 'DataSubjectDetails'
} } {
{ customer @PersonalData.FieldSemantics : 'DataSubjectID';
creditCardNo @PersonalData.IsPotentiallySensitive; creditCardNo @PersonalData.IsPotentiallySensitive;
} }
annotate bookshop.CustomerPostalAddress with @PersonalData : { annotate bookshop.Addresses with @PersonalData: {
DataSubjectRole : 'Customer', DataSubjectRole: 'Customer',
EntitySemantics : 'DataSubjectDetails' EntitySemantics: 'DataSubjectDetails'
} } {
{ customer @PersonalData.FieldSemantics : 'DataSubjectID';
Customer @PersonalData.FieldSemantics : 'DataSubjectID';
street @PersonalData.IsPotentiallyPersonal; street @PersonalData.IsPotentiallyPersonal;
town @PersonalData.IsPotentiallyPersonal; town @PersonalData.IsPotentiallyPersonal;
country @PersonalData.IsPotentiallyPersonal; country @PersonalData.IsPotentiallyPersonal;
} }
annotate bookshop.Orders with @PersonalData.EntitySemantics : 'Other' annotate bookshop.Orders with @PersonalData.EntitySemantics: 'Other' {
{
ID @PersonalData.FieldSemantics : 'ContractRelatedID'; ID @PersonalData.FieldSemantics : 'ContractRelatedID';
Customer @PersonalData.FieldSemantics : 'DataSubjectID'; customer @PersonalData.FieldSemantics : 'DataSubjectID';
personalComment @PersonalData.IsPotentiallyPersonal; 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
};

View File

@@ -1,13 +1,17 @@
// Proxy for importing schema from bookshop sample // Proxy for importing schema from bookshop sample
using { sap.capire.bookshop.Books } from '../../bookshop/db/schema'; using {sap.capire.bookshop.Books} from '../../bookshop/db/schema';
using { sap.capire.orders.Orders } from '../../orders/db/schema'; using {sap.capire.orders.Orders} from '../../orders/db/schema';
using { sap.capire.orders.OrderItems } from '../../orders/db/schema'; using {sap.capire.orders.OrderItems} from '../../orders/db/schema';
using { Country, managed, cuid } from '@sap/cds/common'; using {
Country,
managed,
cuid
} from '@sap/cds/common';
namespace sap.capire.bookshop; namespace sap.capire.bookshop;
extend Orders with { extend Orders with {
Customer : Association to Customers; customer : Association to Customers;
personalComment : String; personalComment : String;
} }
@@ -15,14 +19,15 @@ entity Customers : cuid, managed {
email : String; email : String;
firstName : String; firstName : String;
lastName : String; lastName : String;
// creditCardNo : String;
dateOfBirth : Date; dateOfBirth : Date;
billingData : Composition of one CustomerBillingData on billingData.Customer = $self; billingData : Composition of BillingData
postalAddress : Composition of one CustomerPostalAddress on postalAddress.Customer = $self; on billingData.customer = $self;
addresses : Composition of Addresses
on addresses.customer = $self;
} }
entity CustomerPostalAddress : cuid, managed { entity Addresses : cuid, managed {
Customer : Association to one Customers; customer : Association to one Customers;
street : String(128); street : String(128);
town : String(128); town : String(128);
country : Country; country : Country;
@@ -30,8 +35,7 @@ entity CustomerPostalAddress : cuid, managed {
}; };
entity CustomerBillingData : cuid, managed { entity BillingData : cuid, managed {
Customer : Association to one Customers; customer : Association to one Customers;
creditCardNo : String; creditCardNo : String;
}; };

View File

@@ -1,8 +1,8 @@
const cds = require('@sap/cds') const cds = require('@sap/cds')
// FIXME: no longer works like this with new audit logging plugin
module.exports = class MyAuditLogService extends cds.AuditLogService { module.exports = class MyAuditLogService extends cds.AuditLogService {
async init() { async init() {
// console.log('My Audit Log'); // console.log('My Audit Log');
// call AuditLogService's init // call AuditLogService's init
await super.init() await super.init()
@@ -12,132 +12,59 @@ module.exports = class MyAuditLogService extends cds.AuditLogService {
// register custom handlers // register custom handlers
this.on('dataAccessLog', async req => { this.on('dataAccessLog', async req => {
const logs = []
const logs = []; const action = 'DataAccess'
const user = req.user.id
const timestamp = req.timestamp
const tenant = req.tenant
const channel = req.channel
const action = 'DataAccess'; req.data.accesses.forEach(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({ logs.push({
Action: action, Action: action,
User: user, User: user,
Timestamp: timestamp, Timestamp: timestamp,
Tenant: tenant, Tenant: tenant,
Channel: channel, Channel: channel,
DataSubjectType: dataAccess.dataSubject.type, DataSubjectType: dataAccess.data_subject.type,
DataSubjectRole: dataAccess.dataSubject.role, DataSubjectRole: dataAccess.data_subject.role,
DataSubjectID: JSON.stringify(dataAccess.dataSubject.id), DataSubjectID: JSON.stringify(dataAccess.data_subject.id),
ObjectType: dataAccess.dataObject.type, ObjectType: dataAccess.object.type,
ObjectKey: JSON.stringify(dataAccess.dataObject.id), ObjectKey: JSON.stringify(dataAccess.object.id),
Blob: JSON.stringify(dataAccess) Blob: JSON.stringify(dataAccess)
}) } })
) })
await INSERT.into(AuditLogStore).entries(logs) await INSERT.into(AuditLogStore).entries(logs)
} })
)
this.on('dataModificationLog', async req => { 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'; req.data.modifications.forEach(dataModification => {
const user = req.user.id;
const timestamp = req.timestamp;
const tenant = req.tenant;
const channel = req.channel;
req.data.modifications.forEach( dataModification => {
mods.push({ mods.push({
Action: action, Action: action,
User: user, User: user,
Timestamp: timestamp, Timestamp: timestamp,
Tenant: tenant, Tenant: tenant,
Channel: channel, Channel: channel,
DataSubjectType: dataModification.dataSubject.type, DataSubjectType: dataModification.data_subject.type,
DataSubjectRole: dataModification.dataSubject.role, DataSubjectRole: dataModification.data_subject.role,
DataSubjectID: JSON.stringify(dataModification.dataSubject.id), DataSubjectID: JSON.stringify(dataModification.data_subject.id),
ObjectType: dataModification.dataObject.type, ObjectType: dataModification.object.type,
ObjectKey: JSON.stringify(dataModification.dataObject.id), ObjectKey: JSON.stringify(dataModification.object.id),
Blob: JSON.stringify(dataModification) Blob: JSON.stringify(dataModification)
}) } })
) })
await INSERT.into(AuditLogStore).entries(mods) 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;
}
*/

View File

@@ -6,9 +6,8 @@ using {sap.capire.auditLog as log} from '../db/AuditLogStore.cds';
service LogService { service LogService {
entity Customers as projection on db.Customers; entity Customers as projection on db.Customers;
entity CustomerPostalAddress as projection on db.CustomerPostalAddress; entity Addresses as projection on db.Addresses;
entity Orders as projection on dbo.Orders; entity Orders as projection on dbo.Orders;
entity AuditLogStore as projection on log.AuditLogStore; entity AuditLogStore as projection on log.AuditLogStore;
}; };

View File

@@ -6,39 +6,38 @@ using {sap.capire.orders.OrderItems} from '../db/data-privacy';
//@requires: 'PersonalDataManagerUser' // security check //@requires: 'PersonalDataManagerUser' // security check
service PDMService { service PDMService {
// Data Privacy annotations on 'Customers', 'Addresses', and 'BillingData' are derived from original entity definitions
entity Customers as projection on db.Customers; entity Customers as projection on db.Customers;
entity CustomerPostalAddress as projection on db.CustomerPostalAddress; entity Addresses as projection on db.Addresses;
entity CustomerBillingData as projection on db.CustomerBillingData; entity BillingData as projection on db.BillingData;
// create view on Orders and Items as flat projection // create view on Orders and Items as flat projection
entity OrderItemView as entity OrderItemView as
select from Orders { select from Orders {
ID, ID,
key Items.ID as Item_ID, key Items.ID as item_ID,
OrderNo, OrderNo,
Customer.ID as Customer_ID, customer.ID as customer_ID,
Customer.email as Customer_Email, customer.email as customer_email,
Items.book.ID as Item_Book_ID, Items.book.ID as item_Book_ID,
Items.amount as Item_Amount, Items.amount as item_Amount,
Items.netAmount as Item_NetAmount Items.netAmount as item_NetAmount
}; };
// annotate new view // annotate new view
annotate PDMService.OrderItemView with @(PersonalData.EntitySemantics : 'Other') { annotate PDMService.OrderItemView with @(PersonalData.EntitySemantics: 'Other') {
Item_ID @PersonalData.FieldSemantics : 'ContractRelatedID'; item_ID @PersonalData.FieldSemantics: 'ContractRelatedID';
Customer_ID @PersonalData.FieldSemantics : 'DataSubjectID'; customer_ID @PersonalData.FieldSemantics: 'DataSubjectID';
Customer_Email @PersonalData.IsPotentiallyPersonal; customer_email @PersonalData.IsPotentiallyPersonal;
}; };
// annotations for Personal Data Manager - Search Fields // annotations for Personal Data Manager - Search Fields
annotate Customers with @(Communication.Contact : { annotate Customers with @(Communication.Contact: {
n : { n : {
surname : lastName, surname: lastName,
given : firstName given : firstName
}, },
bday : dateOfBirth bday: dateOfBirth
}); });
// Data Privacy annotations on 'Customers' and 'CustomerPostalAddress' are derived from original entity definitions
}; };