Skip to content

Commit 02b7a7c

Browse files
feat(audit-log-to-alsng): Support for sending generic audit log events (#176)
Co-authored-by: D050513 <sebastian.van.syckel@sap.com> Co-authored-by: sjvans <30337871+sjvans@users.noreply.github.com>
1 parent e4d57d6 commit 02b7a7c

File tree

5 files changed

+61
-15
lines changed

5 files changed

+61
-15
lines changed

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ All notable changes to this project will be documented in this file.
44
This project adheres to [Semantic Versioning](http://semver.org/).
55
The format is based on [Keep a Changelog](http://keepachangelog.com/).
66

7-
## Version 1.0.2 - tbd
7+
## Version 1.1.0 - 2025-09-08
8+
9+
### Added
10+
11+
- `audit-log-to-alsng`: Support for sending generic audit log events
812

913
### Fixed
1014

11-
- Correctly retrieve `appId` from the `VCAP_APPLICATION` environment variable
15+
- `audit-log-to-alsng`: Correctly retrieve `appId` from the `VCAP_APPLICATION` environment variable
1216

1317
## Version 1.0.1 - 2025-08-05
1418

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@cap-js/audit-logging",
3-
"version": "1.0.2",
3+
"version": "1.1.0",
44
"description": "CDS plugin providing integration to the SAP Audit Log service as well as out-of-the-box personal data-related audit logging based on annotations.",
55
"repository": "cap-js/audit-logging",
66
"author": "SAP SE (https://www.sap.com)",

srv/log2alsng.js

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const cds = require('@sap/cds')
2-
32
const LOG = cds.log('audit-log')
43

54
const https = require('https')
@@ -24,12 +23,14 @@ module.exports = class AuditLog2ALSNG extends AuditLogService {
2423
}
2524

2625
eventMapper(event, data) {
27-
return {
26+
const known = {
2827
PersonalDataModified: () => this.logEvent('dppDataModification', data),
2928
SensitiveDataRead: () => this.logEvent('dppDataAccess', data),
3029
ConfigurationModified: () => this.logEvent('configurationChange', data),
3130
SecurityEvent: () => this.logEvent('legacySecurityWrapper', data)
32-
}[event]()
31+
}
32+
const dfault = () => this.logEvent(event, data)
33+
return (known[event] ?? dfault)()
3334
}
3435

3536
flattenAndSortIdObject(id) {
@@ -49,7 +50,8 @@ module.exports = class AuditLog2ALSNG extends AuditLogService {
4950
const oldValue = attributes[0]['old'] ?? ''
5051
const newValue = attributes[0]['new'] ?? ''
5152
const dataSubjectId = this.flattenAndSortIdObject(subject['id'])
52-
return {
53+
54+
const known = {
5355
dppDataModification: {
5456
objectType: object['type'],
5557
objectId: objectId,
@@ -84,7 +86,26 @@ module.exports = class AuditLog2ALSNG extends AuditLogService {
8486
: data.data
8587
})
8688
}
87-
}[event]
89+
}
90+
if (event in known) return known[event]
91+
92+
// For unknown events, remove common audit log entry fields from the event payload
93+
if (typeof data === 'object' && data !== null) {
94+
const rest = this.removeCommonAuditLogFields(data)
95+
return rest
96+
}
97+
98+
return data
99+
}
100+
101+
removeCommonAuditLogFields(obj) {
102+
if (typeof obj !== 'object' || obj === null) return obj
103+
const { ...rest } = obj
104+
delete rest.uuid
105+
delete rest.user
106+
delete rest.time
107+
delete rest.tenant
108+
return rest
88109
}
89110

90111
eventPayload(event, data) {
@@ -126,14 +147,18 @@ module.exports = class AuditLog2ALSNG extends AuditLogService {
126147
return JSON.stringify([this.eventPayload(event, data)])
127148
}
128149

129-
const eventData = data['attributes']?.map(attr => {
130-
return this.eventPayload(event, {
131-
...data,
132-
attributes: [attr]
150+
if (event in { dppDataModification: 1, dppDataAccess: 1, configurationChange: 1 }) {
151+
const eventData = data['attributes']?.map(attr => {
152+
return this.eventPayload(event, {
153+
...data,
154+
attributes: [attr]
155+
})
133156
})
134-
})
157+
return JSON.stringify(eventData || [])
158+
}
135159

136-
return JSON.stringify(eventData || [])
160+
// Always wrap event in an envelope for custom events
161+
return JSON.stringify([this.eventPayload(event, data)])
137162
}
138163

139164
logEvent(event, data) {

test/integration/ng.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,22 @@ describe('Log to Audit Log Service NG ', () => {
5353
POST('/integration/passthrough', { event: 'PersonalDataModified', data: '{}' }, { auth: ALICE })
5454
).rejects.toThrow('Request failed with: 403 - Forbidden')
5555
})
56+
57+
test('writes log for custom event tenantOnboarding', async () => {
58+
const customEvent = 'tenantOnboarding'
59+
const data = JSON.stringify({
60+
tenantId: 'test-tenant'
61+
})
62+
const res = await POST('/integration/passthrough', { event: customEvent, data }, { auth: ALICE })
63+
expect(res).toMatchObject({ status: 204 })
64+
})
65+
66+
test('writes log for custom event userLogoff', async () => {
67+
const customEvent = 'userLogoff'
68+
const data = JSON.stringify({
69+
logoffType: 'UNSPECIFIED'
70+
})
71+
const res = await POST('/integration/passthrough', { event: customEvent, data }, { auth: ALICE })
72+
expect(res).toMatchObject({ status: 204 })
73+
})
5674
})

test/personal-data/crud.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,6 @@ describe('personal data audit logging in CRUD', () => {
10581058
{ name: 'description', old: '***' },
10591059
{ name: 'todo', old: oldAttachments[1].todo }
10601060
]
1061-
10621061
})
10631062
expect(_logs).toContainMatchObject({
10641063
user: 'alice',

0 commit comments

Comments
 (0)