Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CI #1

Merged
merged 13 commits into from
Jun 23, 2023
Merged

CI #1

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18.x, 16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- run: npm i -g @sap/cds-dk
- run: npm i
- run: cds v
- run: npm run lint
- run: npm run test
5 changes: 0 additions & 5 deletions cds-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@ const { auditAccess } = require('./lib/access')
const { augmentContext, calcMods4Before, calcMods4After, emitMods } = require('./lib/modification')
const { hasPersonalData } = require('./lib/utils')

// // UNCOMMENT THIS IF YOU CAN'T USE CDS 7
// if (cds.version.match(/^6/)) {
// cds.env.requires['audit-log'] = cds.env.requires['audit-log'] || { impl: '@cap-js/audit-logging/srv/log2console' }
// }

// TODO: why does cds.requires.audit-log: false in sample package.json not work ootb?!

/*
Expand Down
7 changes: 7 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const config = {
testTimeout: 42222,
testMatch: ['**/*.test.js'],
setupFilesAfterEnv: ['./test/jest.setup.js']
}

module.exports = config
2 changes: 1 addition & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,4 @@ module.exports = {
addDataSubject,
addDataSubjectForDetailsEntity,
resolveDataSubjectPromises
}
}
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,20 @@
"lib",
"srv"
],
"scripts": {
"lint": "npx eslint .",
"test": "npx jest --silent"
},
"peerDependencies": {
"@sap/cds": "*"
},
"devDependencies": {
"@sap/audit-logging": "^5.7.0",
"axios": "^1.4.0",
"eslint": "^8",
"@sap/audit-logging": "^5.7.0"
"express": "^4.18.2",
"jest": "^29.5.0",
"sqlite3": "^5.1.6"
},
"cds": {
"requires": {
Expand Down
14 changes: 11 additions & 3 deletions srv/log2console.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ module.exports = class AuditLog2Console extends AuditLogService {
await super.init()

this.on('*', function(req) {
const { event, data } = req.data
const { event, data } = req.data.event && req.data.data ? req.data : req

console.log(`[audit-log] - ${event}:\n${JSON.stringify(data, null, 2).split('\n').map(l => ` ${l}`).join('\n')}`)
console.log(`[audit-log] - ${event}:\n${_format(data)}`)
})
}
}
}

/*
* utils
*/

function _format(data) {
return JSON.stringify(data, null, 2).split('\n').map(l => ` ${l}`).join('\n')
}
186 changes: 109 additions & 77 deletions srv/log2library.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ module.exports = class AuditLog2Library extends AuditLogService {
await super.init()

this.on('*', function(req) {
const { event, data } = req.data
const { event, data } = req.data.event && req.data.data ? req.data : req

if (event.match(/^dataAccess/)) return this._dataAccess(data)
if (event.match(/^dataModification/)) return this._dataModification(data)
if (event.match(/^configChange/)) return this._configChange(data)
if (event.match(/^security/)) return this._securityEvent(data)

LOG._info && LOG.info(`event ${event} not implemented`)
})
Expand Down Expand Up @@ -49,15 +51,14 @@ module.exports = class AuditLog2Library extends AuditLogService {
return client
}

async _dataAccess(access) {
// REVISIT: previous model/impl supported bulk
const accesses = [access]
async _dataAccess(accesses) {
if (!Array.isArray(accesses)) accesses = [accesses]

const client = this._client || await this._getClient()
if (!client) return

// build the logs
const { tenant, user } = this._oauth2 ? { tenant: '$PROVIDER', user: '$USER' } : { tenant: access.tenant, user: access.user }
const { tenant, user } = this._oauth2 ? { tenant: '$PROVIDER', user: '$USER' } : { tenant: accesses[0].tenant, user: accesses[0].user }
const { entries, errors } = _buildDataAccessLogs(client, accesses, tenant, user)
if (errors.length) throw errors.length === 1 ? errors[0] : Object.assign(new Error('MULTIPLE_ERRORS'), { details: errors })

Expand All @@ -66,22 +67,53 @@ module.exports = class AuditLog2Library extends AuditLogService {
if (errors.length) throw errors.length === 1 ? errors[0] : Object.assign(new Error('MULTIPLE_ERRORS'), { details: errors })
}

async _dataModification(modification) {
// REVISIT: previous model/impl supported bulk
const modifications = [modification]
async _dataModification(modifications) {
if (!Array.isArray(modifications)) modifications = [modifications]

const client = this._client || await this._getClient()
if (!client) return

// build the logs
const { tenant, user } = this._oauth2 ? { tenant: '$PROVIDER', user: '$USER' } : { tenant: modification.tenant, user: modification.user }
const { tenant, user } = this._oauth2 ? { tenant: '$PROVIDER', user: '$USER' } : { tenant: modifications[0].tenant, user: modifications[0].user }
const { entries, errors } = _buildDataModificationLogs(client, modifications, tenant, user)
if (errors.length) throw errors.length === 1 ? errors[0] : Object.assign(new Error('MULTIPLE_ERRORS'), { details: errors })

// write the logs
await Promise.all(entries.map(entry => _sendDataModificationLog(entry).catch(err => errors.push(err))))
if (errors.length) throw errors.length === 1 ? errors[0] : Object.assign(new Error('MULTIPLE_ERRORS'), { details: errors })
}

async _configChange(configurations) {
if (!Array.isArray(configurations)) configurations = [configurations]

const client = this._client || await this._getClient()
if (!client) return

// build the logs
const { tenant, user } = this._oauth2 ? { tenant: '$PROVIDER', user: '$USER' } : { tenant: configurations[0].tenant, user: configurations[0].user }
const { entries, errors } = _buildConfigChangeLogs(client, configurations, tenant, user)
if (errors.length) throw errors.length === 1 ? errors[0] : Object.assign(new Error('MULTIPLE_ERRORS'), { details: errors })

// write the logs
await Promise.all(entries.map(entry => _sendConfigChangeLog(entry).catch(err => errors.push(err))))
if (errors.length) throw errors.length === 1 ? errors[0] : Object.assign(new Error('MULTIPLE_ERRORS'), { details: errors })
}

async _securityEvent(arg) {
const { action, data } = arg

const client = this._client || await this._getClient()
if (!client) return

// build the logs
const { tenant, user } = this._oauth2 ? { tenant: '$PROVIDER', user: '$USER' } : { tenant: arg.tenant, user: arg.user }
const { entries, errors } = _buildSecurityLog(client, action, data, tenant, user)
if (errors.length) throw errors.length === 1 ? errors[0] : Object.assign(new Error('MULTIPLE_ERRORS'), { details: errors })

// write the logs
await Promise.all(entries.map(entry => _sendSecurityLog(entry).catch(err => errors.push(err))))
if (errors.length) throw errors.length === 1 ? errors[0] : Object.assign(new Error('MULTIPLE_ERRORS'), { details: errors })
}
}

/*
Expand Down Expand Up @@ -190,77 +222,77 @@ function _sendDataModificationLog(entry) {
}

/*
* security
* config
*/

// function _buildSecurityLog(client, action, data, tenant, user) {
// let entry

// try {
// entry = client.securityMessage('action: %s, data: %s', action, data)
// if (tenant) entry.tenant(tenant)
// if (user) entry.by(user)
// } catch (err) {
// err.message = `Building security log failed with error: ${err.message}`
// throw err
// }

// return entry
// }

// function _sendSecurityLog(entry) {
// return new Promise((resolve, reject) => {
// entry.log(function (err) {
// if (err) {
// err.message = `Writing security log failed with error: ${err.message}`
// return reject(err)
// }

// resolve()
// })
// })
// }
function _buildConfigChangeLogs(client, configurations, tenant, user) {
const entries = []
const errors = []

for (const configuration of configurations) {
try {
const { dataObject } = _getObjectAndDataSubject(configuration)
const entry = client.configurationChange(dataObject).by(user)
if (tenant) entry.tenant(tenant)
for (const each of configuration.attributes) entry.attribute(_getAttributeToLog(each))
entries.push(entry)
} catch (err) {
err.message = `Building configuration change log failed with error: ${err.message}`
errors.push(err)
}
}

return { entries, errors }
}

function _sendConfigChangeLog(entry) {
return new Promise((resolve, reject) => {
entry.logPrepare(function (err) {
if (err) {
err.message = `Preparing configuration change log failed with error: ${err.message}`
return reject(err)
}

entry.logSuccess(function (err) {
if (err) {
err.message = `Writing configuration change log failed with error: ${err.message}`
return reject(err)
}

resolve()
})
})
})
}

/*
* config
* security
*/

// function _buildConfigChangeLogs(client, configurations, tenant, user) {
// const entries = []
// const errors = []

// for (const configuration of configurations) {
// try {
// const { dataObject } = getObjectAndDataSubject(configuration)
// const entry = client.configurationChange(dataObject).by(user)
// if (tenant) entry.tenant(tenant)
// for (const each of configuration.attributes) entry.attribute(getAttributeToLog(each))
// entries.push(entry)
// } catch (err) {
// err.message = `Building configuration change log failed with error: ${err.message}`
// errors.push(err)
// }
// }

// return { entries, errors }
// }

// function _sendConfigChangeLog(entry) {
// return new Promise((resolve, reject) => {
// entry.logPrepare(function (err) {
// if (err) {
// err.message = `Preparing configuration change log failed with error: ${err.message}`
// return reject(err)
// }

// entry.logSuccess(function (err) {
// if (err) {
// err.message = `Writing configuration change log failed with error: ${err.message}`
// return reject(err)
// }

// resolve()
// })
// })
// })
// }
function _buildSecurityLog(client, action, data, tenant, user) {
let entry

try {
entry = client.securityMessage('action: %s, data: %s', action, data)
if (tenant) entry.tenant(tenant)
if (user) entry.by(user)
} catch (err) {
err.message = `Building security log failed with error: ${err.message}`
throw err
}

return entry
}

function _sendSecurityLog(entry) {
return new Promise((resolve, reject) => {
entry.log(function (err) {
if (err) {
err.message = `Writing security log failed with error: ${err.message}`
return reject(err)
}

resolve()
})
})
}
Loading