From 543515790704b1fda8aca54ff3a357e3939c0d93 Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 5 Dec 2025 11:42:21 +0100 Subject: [PATCH 01/10] feat!: remove kind 'audit-log-to-als' --- package.json | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index bdb4487..4da481b 100644 --- a/package.json +++ b/package.json @@ -51,17 +51,13 @@ "audit-log-to-console": { "impl": "@cap-js/audit-logging/srv/log2console" }, - "audit-log-to-als": { - "impl": "@cap-js/audit-logging/srv/log2als" - }, "audit-log-to-restv2": { "impl": "@cap-js/audit-logging/srv/log2restv2", - "vcap": { - "label": "auditlog" - } + "vcap": { "label": "auditlog" } }, "audit-log-to-alsng": { - "impl": "@cap-js/audit-logging/srv/log2alsng" + "impl": "@cap-js/audit-logging/srv/log2alsng", + "vcap": { "tag": "auditlog-ng" } } } } From 1a790bdef02a483740efd9568edd56218e3fb479 Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 5 Dec 2025 11:42:50 +0100 Subject: [PATCH 02/10] feat: stop manually resolving binding info --- srv/log2alsng.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/srv/log2alsng.js b/srv/log2alsng.js index 1841488..775469d 100644 --- a/srv/log2alsng.js +++ b/srv/log2alsng.js @@ -8,9 +8,7 @@ const AuditLogService = require('./service') module.exports = class AuditLog2ALSNG extends AuditLogService { constructor() { super() - this._vcap = JSON.parse(process.env.VCAP_SERVICES || '{}') - this._userProvided = this._vcap['user-provided']?.find(obj => obj.tags.includes('auditlog-ng')) || {} - if (!this._userProvided.credentials) throw new Error('No credentials found for SAP Audit Log Service NG') + if (!this.options.credentials) throw new Error('No credentials found for SAP Audit Log Service NG') this._vcapApplication = JSON.parse(process.env.VCAP_APPLICATION || '{}') } @@ -115,7 +113,7 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { const eventData = { id: cds.utils.uuid(), specversion: 1, - source: `/${this._userProvided.credentials?.region}/${this._userProvided.credentials?.namespace}/${tenant}`, + source: `/${this.options.credentials?.region}/${this.options.credentials?.namespace}/${tenant}`, type: event, time: timestamp, data: { @@ -162,8 +160,8 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { } logEvent(event, data) { - const passphrase = this._userProvided.credentials?.keyPassphrase - const url = new URL(`${this._userProvided.credentials?.url}/ingestion/v1/events`) + const passphrase = this.options.credentials?.keyPassphrase + const url = new URL(`${this.options.credentials?.url}/ingestion/v1/events`) const eventData = this.formatEventData(event, data) const options = { @@ -172,8 +170,8 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(eventData) }, - key: this._userProvided.credentials?.key, - cert: this._userProvided.credentials?.cert, + key: this.options.credentials?.key, + cert: this.options.credentials?.cert, ...(passphrase !== undefined && { passphrase }) } From bb06638a09cc5da919f987b9531b5c05d889d1fc Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 5 Dec 2025 11:50:35 +0100 Subject: [PATCH 03/10] test: prevent access property of undefined --- test/integration/premium.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/premium.test.js b/test/integration/premium.test.js index 80318bc..ca0dd9e 100644 --- a/test/integration/premium.test.js +++ b/test/integration/premium.test.js @@ -6,7 +6,7 @@ const log = cds.test.log() cds.env.requires['audit-log'].credentials = process.env.ALS_CREDS_PREMIUM && JSON.parse(process.env.ALS_CREDS_PREMIUM) // stay in provider account (i.e., use "$PROVIDER" and avoid x-zid header when fetching oauth2 token) -cds.env.requires.auth.users.alice.tenant = cds.env.requires['audit-log'].credentials.uaa.tenantid +cds.env.requires.auth.users.alice.tenant = cds.env.requires['audit-log'].credentials?.uaa.tenantid cds.env.log.levels['audit-log'] = 'debug' From 2e44b4b902707b239b8bb120bd2346288c320e72 Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 5 Dec 2025 11:53:34 +0100 Subject: [PATCH 04/10] test: prevent access property of undefined --- test/integration/oauth2.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/oauth2.test.js b/test/integration/oauth2.test.js index 554ed7f..134cdc1 100644 --- a/test/integration/oauth2.test.js +++ b/test/integration/oauth2.test.js @@ -6,7 +6,7 @@ const log = cds.test.log() cds.env.requires['audit-log'].credentials = process.env.ALS_CREDS_OAUTH2 && JSON.parse(process.env.ALS_CREDS_OAUTH2) // stay in provider account (i.e., use "$PROVIDER" and avoid x-zid header when fetching oauth2 token) -cds.env.requires.auth.users.alice.tenant = cds.env.requires['audit-log'].credentials.uaa.tenantid +cds.env.requires.auth.users.alice.tenant = cds.env.requires['audit-log'].credentials?.uaa.tenantid cds.env.log.levels['audit-log'] = 'debug' From 2ed287dbd6673fdf8cee5bf40260deeac1ea5766 Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 5 Dec 2025 11:53:48 +0100 Subject: [PATCH 05/10] chore: add entry to changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e24ecc3..28f0ba6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog](http://keepachangelog.com/). +## Version 1.1.2 - tbd + +### Changed + +- Removed alpha auto-detect kind `audit-log-to-als` to prevent failing lookup from now optional `VCAP_SERVICES` + ## Version 1.1.1 - 2025-10-27 ### Fixed From a591c397ed8bad9ce8059b42dff1d92e96568508 Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 5 Dec 2025 13:42:05 +0100 Subject: [PATCH 06/10] test: fix test setup to proide environment before cds.test --- test/integration/ng.test.js | 6 ++---- test/integration/package.json | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/test/integration/ng.test.js b/test/integration/ng.test.js index 4f7429b..dd49c9f 100644 --- a/test/integration/ng.test.js +++ b/test/integration/ng.test.js @@ -1,9 +1,5 @@ const cds = require('@sap/cds') -const { POST } = cds.test().in(__dirname) - -cds.env.requires['audit-log'].kind = 'audit-log-to-alsng' -cds.env.requires['audit-log'].impl = '@cap-js/audit-logging/srv/log2alsng' const VCAP_SERVICES = { 'user-provided': [ { @@ -14,6 +10,8 @@ const VCAP_SERVICES = { } process.env.VCAP_SERVICES = JSON.stringify(VCAP_SERVICES) +const { POST } = cds.test(__dirname, '--with-mocks', '--profile', 'audit-log-to-alsng') + describe('Log to Audit Log Service NG ', () => { if (!VCAP_SERVICES['user-provided'][0].credentials) return test.skip('Skipping tests due to missing credentials', () => {}) diff --git a/test/integration/package.json b/test/integration/package.json index ff5a877..35bf7fb 100644 --- a/test/integration/package.json +++ b/test/integration/package.json @@ -4,7 +4,12 @@ }, "cds": { "requires": { - "audit-log": "audit-log-to-restv2" + "audit-log": "audit-log-to-restv2", + "[audit-log-to-alsng]": { + "audit-log": { + "kind": "audit-log-to-alsng" + } + } } } } From a5c3d1bc7d3ad49ebd238319ac370ff1b0a6ccb2 Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 5 Dec 2025 13:51:45 +0100 Subject: [PATCH 07/10] fix: account for constructor --- srv/log2alsng.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srv/log2alsng.js b/srv/log2alsng.js index 775469d..9f3f0c6 100644 --- a/srv/log2alsng.js +++ b/srv/log2alsng.js @@ -8,7 +8,7 @@ const AuditLogService = require('./service') module.exports = class AuditLog2ALSNG extends AuditLogService { constructor() { super() - if (!this.options.credentials) throw new Error('No credentials found for SAP Audit Log Service NG') + if (!cds.requires['audit-log']?.credentials) throw new Error('No credentials found for SAP Audit Log Service NG') this._vcapApplication = JSON.parse(process.env.VCAP_APPLICATION || '{}') } From 92664cc446d2a0087989a6cca92c2808ecd09a52 Mon Sep 17 00:00:00 2001 From: I548646 Date: Fri, 5 Dec 2025 13:59:28 +0100 Subject: [PATCH 08/10] fix: revert to using a local object --- srv/log2alsng.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/srv/log2alsng.js b/srv/log2alsng.js index 9f3f0c6..83405ac 100644 --- a/srv/log2alsng.js +++ b/srv/log2alsng.js @@ -8,7 +8,8 @@ const AuditLogService = require('./service') module.exports = class AuditLog2ALSNG extends AuditLogService { constructor() { super() - if (!cds.requires['audit-log']?.credentials) throw new Error('No credentials found for SAP Audit Log Service NG') + this._userProvided = cds.requires['audit-log'] + if (!this._userProvided?.credentials) throw new Error('No credentials found for SAP Audit Log Service NG') this._vcapApplication = JSON.parse(process.env.VCAP_APPLICATION || '{}') } @@ -113,7 +114,7 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { const eventData = { id: cds.utils.uuid(), specversion: 1, - source: `/${this.options.credentials?.region}/${this.options.credentials?.namespace}/${tenant}`, + source: `/${this._userProvided.credentials?.region}/${this._userProvided.credentials?.namespace}/${tenant}`, type: event, time: timestamp, data: { @@ -160,8 +161,8 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { } logEvent(event, data) { - const passphrase = this.options.credentials?.keyPassphrase - const url = new URL(`${this.options.credentials?.url}/ingestion/v1/events`) + const passphrase = this._userProvided.credentials?.keyPassphrase + const url = new URL(`${this._userProvided.credentials?.url}/ingestion/v1/events`) const eventData = this.formatEventData(event, data) const options = { @@ -170,8 +171,8 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(eventData) }, - key: this.options.credentials?.key, - cert: this.options.credentials?.cert, + key: this._userProvided.credentials?.key, + cert: this._userProvided.credentials?.cert, ...(passphrase !== undefined && { passphrase }) } From 7e631d02c87c711d32aec5b4adbe3cfc7dd02edf Mon Sep 17 00:00:00 2001 From: I548646 Date: Mon, 19 Jan 2026 12:50:27 +0100 Subject: [PATCH 09/10] fix: adjust vcap handling --- lib/utils.js | 35 +++++++++++++++++++++++++++++++++-- srv/log2als.js | 7 ++++--- srv/log2alsng.js | 24 ++++++++++++++---------- srv/log2restv2.js | 13 ++++++++----- 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/lib/utils.js b/lib/utils.js index 102878e..29d82a0 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -286,6 +286,36 @@ const resolveDataSubjects = (logs, req) => { }) } +const getAppMetadata = () => { + const appMetadata = cds.env.app; + + if (appMetadata) { + return { + appID: appMetadata.id, + appName: appMetadata.name, + appURL: appMetadata.url, + organization_name: appMetadata.organization_name, + space_name: appMetadata.space_name, + }; + } + + // fallback: if the app metadata is undefined, then extract the metadata from the underlying environment (CF/Kyma/...) + const vcapApplication = + process.env.VCAP_APPLICATION && JSON.parse(process.env.VCAP_APPLICATION); + + return { + appID: vcapApplication && vcapApplication.application_id, + appName: vcapApplication && vcapApplication.application_name, + organization_name: vcapApplication && vcapApplication.organization_name, + space_name: vcapApplication && vcapApplication.space_name, + appURL: + vcapApplication && + vcapApplication.application_uris && + vcapApplication.application_uris[0] && + `https://${vcapApplication.application_uris[0].replace(/^https?:\/\//, "")}`, + }; +}; + module.exports = { hasPersonalData, getMapKeyForCurrentRequest, @@ -295,5 +325,6 @@ module.exports = { addObjectID, addDataSubject, addDataSubjectForDetailsEntity, - resolveDataSubjects -} + resolveDataSubjects, + appMetadata: getAppMetadata(), +}; diff --git a/srv/log2als.js b/srv/log2als.js index 5ecb159..ef8ea95 100644 --- a/srv/log2als.js +++ b/srv/log2als.js @@ -1,4 +1,5 @@ -const credentials = JSON.parse(process.env.VCAP_SERVICES) || {} -const isV3 = credentials['user-provided']?.some(obj => obj.tags.includes('auditlog-ng')) +const cds = require("@sap/cds"); -module.exports = isV3 ? require('./log2alsng') : require('./log2restv2') +module.exports = cds.env.requires["audit-log"].vcap.tags.includes("auditlog-ng") + ? require("./log2alsng") + : require("./log2restv2"); diff --git a/srv/log2alsng.js b/srv/log2alsng.js index 83405ac..f168b81 100644 --- a/srv/log2alsng.js +++ b/srv/log2alsng.js @@ -1,6 +1,6 @@ const cds = require('@sap/cds') const LOG = cds.log('audit-log') - +const { appMetadata } = require("../lib/utils"); const https = require('https') const AuditLogService = require('./service') @@ -8,9 +8,8 @@ const AuditLogService = require('./service') module.exports = class AuditLog2ALSNG extends AuditLogService { constructor() { super() - this._userProvided = cds.requires['audit-log'] - if (!this._userProvided?.credentials) throw new Error('No credentials found for SAP Audit Log Service NG') - this._vcapApplication = JSON.parse(process.env.VCAP_APPLICATION || '{}') + if (!cds.env.requires['audit-log']?.credentials) + throw new Error('No credentials found for SAP Audit Log Service NG') } async init() { @@ -114,13 +113,13 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { const eventData = { id: cds.utils.uuid(), specversion: 1, - source: `/${this._userProvided.credentials?.region}/${this._userProvided.credentials?.namespace}/${tenant}`, + source: `/${cds.env.requires["audit-log"].credentials?.region}/${cds.env.requires["audit-log"].credentials?.namespace}/${tenant}`, type: event, time: timestamp, data: { metadata: { ts: timestamp, - appId: this._vcapApplication.application_id || 'default app', + appId: appMetadata.appID || "default app", infrastructure: { other: { runtimeType: 'Node.js' @@ -161,8 +160,13 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { } logEvent(event, data) { - const passphrase = this._userProvided.credentials?.keyPassphrase - const url = new URL(`${this._userProvided.credentials?.url}/ingestion/v1/events`) + const credentials = cds.env.requires["audit-log"].credentials; + if (!credentials) { + throw new Error("No credentials found for SAP Audit Log Service NG"); + } + + const passphrase = credentials.keyPassphrase + const url = new URL(`${credentials.url}/ingestion/v1/events`) const eventData = this.formatEventData(event, data) const options = { @@ -171,8 +175,8 @@ module.exports = class AuditLog2ALSNG extends AuditLogService { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(eventData) }, - key: this._userProvided.credentials?.key, - cert: this._userProvided.credentials?.cert, + key: credentials.key, + cert: credentials.cert, ...(passphrase !== undefined && { passphrase }) } diff --git a/srv/log2restv2.js b/srv/log2restv2.js index 6fe69a0..161d69b 100644 --- a/srv/log2restv2.js +++ b/srv/log2restv2.js @@ -1,4 +1,5 @@ const cds = require('@sap/cds') +const { appMetadata } = require("../lib/utils"); const LOG = cds.log('audit-log') @@ -17,7 +18,6 @@ module.exports = class AuditLog2RESTv2 extends AuditLogService { this._tokens = new Map() this._provider = credentials.uaa.tenantid } - this._vcap = process.env.VCAP_APPLICATION ? JSON.parse(process.env.VCAP_APPLICATION) : null this.on('*', function (req) { const { event, data } = req @@ -55,6 +55,7 @@ module.exports = class AuditLog2RESTv2 extends AuditLogService { const data = { grant_type: 'client_credentials', response_type: 'token', client_id: uaa.clientid } const options = { headers: { 'content-type': 'application/x-www-form-urlencoded' } } if (tenant !== this._provider) options.headers['x-zid'] = tenant + // certificate or secret? if (uaa['credential-type'] === 'x509') { options.agent = new https.Agent({ cert: uaa.certificate, key: uaa.key }) @@ -81,11 +82,13 @@ module.exports = class AuditLog2RESTv2 extends AuditLogService { async _send(data, path) { const headers = { 'content-type': 'application/json;charset=utf-8' } - if (this._vcap) { - headers.XS_AUDIT_ORG = this._vcap.organization_name - headers.XS_AUDIT_SPACE = this._vcap.space_name - headers.XS_AUDIT_APP = this._vcap.application_name + + if (appMetadata.appName) { + headers.XS_AUDIT_ORG = appMetadata.organization_name; + headers.XS_AUDIT_SPACE = appMetadata.space_name; + headers.XS_AUDIT_APP = appMetadata.appName; } + let url if (this._plan === 'standard') { url = this.options.credentials.url + PATHS.STANDARD[path] From 105b713906b34102232e76134810960582c9384f Mon Sep 17 00:00:00 2001 From: I548646 Date: Mon, 19 Jan 2026 14:06:56 +0100 Subject: [PATCH 10/10] test: add missing tag to test config --- test/integration/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/package.json b/test/integration/package.json index 35bf7fb..fb4fb54 100644 --- a/test/integration/package.json +++ b/test/integration/package.json @@ -7,7 +7,8 @@ "audit-log": "audit-log-to-restv2", "[audit-log-to-alsng]": { "audit-log": { - "kind": "audit-log-to-alsng" + "kind": "audit-log-to-alsng", + "vcap": { "tag": "auditlog-ng"} } } }