audit log
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
// Incorporate pre-build extensions from...
|
// Incorporate pre-build extensions from...
|
||||||
using from '@capire/common';
|
using from '../../common';
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ service CatalogService @(path:'/browse') {
|
|||||||
} excluding { createdBy, modifiedBy };
|
} excluding { createdBy, modifiedBy };
|
||||||
|
|
||||||
@requires_: 'authenticated-user'
|
@requires_: 'authenticated-user'
|
||||||
action submitOrder (book : Books.ID, amount: Integer);
|
action submitOrder (book : Integer, amount: Integer);
|
||||||
}
|
}
|
||||||
|
|||||||
23
gdpr/db/AuditLogStore.cds
Normal file
23
gdpr/db/AuditLogStore.cds
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using { managed, cuid, sap.common.CodeList } from '@sap/cds/common';
|
||||||
|
|
||||||
|
namespace sap.capire.auditLog;
|
||||||
|
|
||||||
|
entity AuditLogStore : cuid {
|
||||||
|
|
||||||
|
Action : String enum{DataAccess; DataModification};
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Blob : LargeString; // Payload: DataModification or Data Access as BLOB
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace sap.capire.gdpr; //> important for reflection
|
namespace sap.capire.gdpr; //> important for reflection
|
||||||
using from './db/schema';
|
using from './db/schema';
|
||||||
using from './srv/pdm-service';
|
using from './srv/pdm-service';
|
||||||
|
using from './srv/log-service';
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
"@capire/bookshop": "../bookshop",
|
"@capire/bookshop": "../bookshop",
|
||||||
"@capire/common": "../common",
|
"@capire/common": "../common",
|
||||||
"@capire/orders": "../orders",
|
"@capire/orders": "../orders",
|
||||||
"@sap/audit-logging": "^5.0.0",
|
|
||||||
"@sap/cds": "^5",
|
"@sap/cds": "^5",
|
||||||
"@sap/hana-client": "^2.4.177",
|
"@sap/hana-client": "^2.4.177",
|
||||||
"@sap/xsenv": "^3.1.0",
|
"@sap/xsenv": "^3.1.0",
|
||||||
@@ -24,7 +23,11 @@
|
|||||||
},
|
},
|
||||||
"uaa": {
|
"uaa": {
|
||||||
"kind": "xsuaa"
|
"kind": "xsuaa"
|
||||||
|
},
|
||||||
|
"audit-log": {
|
||||||
|
"impl": "srv/customAuditLog.js"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"features": {"audit_personal_data": true}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
143
gdpr/srv/customAuditLog.js
Normal file
143
gdpr/srv/customAuditLog.js
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
|
module.exports = class MyAuditLogService extends cds.AuditLogService {
|
||||||
|
async init() {
|
||||||
|
|
||||||
|
// console.log('My Audit Log');
|
||||||
|
// call AuditLogService's init
|
||||||
|
await super.init()
|
||||||
|
|
||||||
|
const db = await cds.connect.to('db')
|
||||||
|
const { AuditLogStore } = db.entities('sap.capire.auditLog')
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
this.on('dataModificationLog', async req => {
|
||||||
|
|
||||||
|
const mods = [];
|
||||||
|
|
||||||
|
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 => {
|
||||||
|
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)
|
||||||
|
}) }
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
*/
|
||||||
13
gdpr/srv/log-service.cds
Normal file
13
gdpr/srv/log-service.cds
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using {sap.capire.bookshop as db} 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 db.Orders;
|
||||||
|
|
||||||
|
entity AuditLogStore as projection on log.AuditLogStore;
|
||||||
|
|
||||||
|
};
|
||||||
@@ -3,7 +3,7 @@ using {sap.capire.bookshop.Books} from '../db/data-privacy';
|
|||||||
using {sap.capire.bookshop.Orders} from '../db/data-privacy';
|
using {sap.capire.bookshop.Orders} from '../db/data-privacy';
|
||||||
using {sap.capire.bookshop.OrderItems} from '../db/data-privacy';
|
using {sap.capire.bookshop.OrderItems} from '../db/data-privacy';
|
||||||
|
|
||||||
@requires: 'PersonalDataManagerUser' // security check
|
//@requires: 'PersonalDataManagerUser' // security check
|
||||||
service PDMService {
|
service PDMService {
|
||||||
|
|
||||||
entity Customers as projection on db.Customers;
|
entity Customers as projection on db.Customers;
|
||||||
|
|||||||
25
gdpr/test.http
Normal file
25
gdpr/test.http
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
###
|
||||||
|
|
||||||
|
get http://localhost:4004/log/AuditLogStore
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
get http://localhost:4004/log/Customers
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
post http://localhost:4004/log/Customers
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"ID": "22e718c9-ff99-47f1-8ca3-950c850777d4",
|
||||||
|
"createdAt": "2019-01-30T00:00:00.000Z",
|
||||||
|
"createdBy": "admin@business.com",
|
||||||
|
"modifiedAt": "2019-04-04T00:00:00.000Z",
|
||||||
|
"modifiedBy": "admin@business.com",
|
||||||
|
"email": "johanna.doe@company.org",
|
||||||
|
"firstName": "Queen Johanna",
|
||||||
|
"lastName": "Doe",
|
||||||
|
"creditCardNo": "1313-7171-5656-7878",
|
||||||
|
"dateOfBirth": "2001-11-11"
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using { sap.capire.bookshop.Books } from '@capire/bookshop';
|
using { sap.capire.bookshop.Books } from '../../bookshop/db/schema';
|
||||||
using { Currency, managed, cuid } from '@sap/cds/common';
|
using { Currency, managed, cuid } from '@sap/cds/common';
|
||||||
namespace sap.capire.bookshop;
|
namespace sap.capire.bookshop;
|
||||||
|
|
||||||
entity Orders : cuid, managed {
|
entity Orders : cuid, managed {
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -5,12 +5,12 @@
|
|||||||
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
|
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
|
||||||
"author": "daniel.hutzel@sap.com",
|
"author": "daniel.hutzel@sap.com",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capire/bookshop": "file:bookshop",
|
"@capire/bookshop": "./bookshop",
|
||||||
"@capire/common": "file:common",
|
"@capire/common": "./common",
|
||||||
"@capire/fiori": "file:fiori",
|
"@capire/fiori": "./fiori",
|
||||||
"@capire/media": "file:media",
|
"@capire/media": "./media",
|
||||||
"@capire/orders": "file:orders",
|
"@capire/orders": "./orders",
|
||||||
"@capire/reviews": "file:reviews"
|
"@capire/reviews": "./reviews"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user