@sap/cds 6.8.4 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/CHANGELOG.md +66 -4
  2. package/README.md +0 -1
  3. package/bin/cds-serve.js +50 -3
  4. package/bin/deploy/to-hana.js +1 -0
  5. package/bin/serve.js +16 -20
  6. package/lib/auth/basic-auth.js +6 -4
  7. package/lib/auth/index.js +4 -3
  8. package/lib/auth/jwt-auth.js +2 -5
  9. package/lib/compile/cds-compile.js +34 -89
  10. package/lib/compile/cdsc.js +11 -0
  11. package/lib/compile/etc/properties.js +2 -2
  12. package/lib/compile/for/lean_drafts.js +36 -69
  13. package/lib/compile/for/nodejs.js +2 -1
  14. package/lib/compile/load.js +3 -3
  15. package/lib/compile/minify.js +2 -0
  16. package/lib/compile/to/csn.js +74 -0
  17. package/{bin/build/provider/hana/2tabledata.js → lib/compile/to/hdbtabledata.js} +4 -6
  18. package/lib/compile/to/json.js +1 -1
  19. package/lib/compile/to/sql.js +8 -6
  20. package/lib/dbs/cds-deploy.js +174 -114
  21. package/lib/env/cds-env.js +64 -79
  22. package/lib/env/cds-requires.js +11 -28
  23. package/lib/env/defaults.js +13 -3
  24. package/lib/env/plugins.js +1 -12
  25. package/lib/env/presets.js +25 -21
  26. package/lib/index.js +121 -147
  27. package/lib/{core/reflect.js → linked/models.js} +2 -2
  28. package/lib/{core/infer.js → linked/queries.js} +2 -0
  29. package/lib/{core/index.js → linked/types.js} +2 -1
  30. package/lib/log/cds-error.js +13 -7
  31. package/lib/log/format/cf.js +1 -1
  32. package/lib/plugins.js +49 -0
  33. package/lib/ql/Query.js +0 -9
  34. package/lib/ql/STREAM.js +0 -1
  35. package/lib/req/context.js +2 -7
  36. package/lib/req/request.js +6 -2
  37. package/lib/req/response.js +23 -10
  38. package/lib/srv/middlewares/ctx-model.js +2 -2
  39. package/lib/srv/middlewares/errors.js +1 -1
  40. package/lib/srv/protocols/_legacy.js +1 -0
  41. package/lib/srv/protocols/graphql.js +7 -16
  42. package/lib/srv/protocols/index.js +59 -45
  43. package/lib/srv/protocols/odata-v2-proxy.js +2 -70
  44. package/lib/srv/protocols/odata-v4.js +9 -4
  45. package/lib/srv/srv-api.js +9 -3
  46. package/lib/srv/srv-dispatch.js +12 -9
  47. package/lib/srv/srv-models.js +4 -21
  48. package/lib/srv/srv-tx.js +15 -12
  49. package/lib/utils/cds-test.js +14 -9
  50. package/lib/utils/cds-utils.js +2 -12
  51. package/lib/utils/check-version.js +17 -0
  52. package/{bin/build → lib/utils}/csv-reader.js +23 -24
  53. package/libx/_runtime/auth/index.js +27 -23
  54. package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +15 -72
  55. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +1 -1
  56. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +0 -2
  57. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +33 -63
  58. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +14 -18
  59. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +15 -5
  60. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -4
  61. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +37 -40
  62. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +7 -1
  63. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +101 -38
  64. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/errors/AbstractError.js +5 -1
  65. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriTokenizer.js +5 -8
  66. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/ValueConverter.js +2 -1
  67. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/ResourceJsonDeserializer.js +9 -8
  68. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  69. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +15 -11
  70. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +4 -0
  71. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +5 -2
  72. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +1 -123
  73. package/libx/_runtime/cds-services/services/Service.js +79 -107
  74. package/libx/_runtime/cds-services/services/utils/columns.js +23 -19
  75. package/libx/_runtime/cds-services/services/utils/compareJson.js +11 -1
  76. package/libx/_runtime/cds-services/services/utils/differ.js +7 -2
  77. package/libx/_runtime/cds-services/util/assert.js +65 -2
  78. package/libx/_runtime/common/composition/data.js +1 -0
  79. package/libx/_runtime/common/generic/auth/expand.js +1 -1
  80. package/libx/_runtime/common/generic/auth/restrict.js +5 -10
  81. package/libx/_runtime/common/generic/auth/restrictions.js +40 -0
  82. package/libx/_runtime/common/generic/auth/utils.js +1 -2
  83. package/libx/_runtime/common/generic/crud.js +32 -16
  84. package/libx/_runtime/common/generic/etag.js +133 -104
  85. package/libx/_runtime/common/generic/input.js +6 -21
  86. package/libx/_runtime/common/generic/put.js +1 -1
  87. package/libx/_runtime/common/generic/stream.js +52 -0
  88. package/libx/_runtime/common/generic/temporal.js +25 -8
  89. package/libx/_runtime/common/i18n/messages.properties +0 -2
  90. package/libx/_runtime/common/utils/cqn.js +1 -1
  91. package/libx/_runtime/common/utils/cqn2cqn4sql.js +5 -2
  92. package/libx/_runtime/common/utils/csn.js +0 -51
  93. package/libx/_runtime/common/utils/etag.js +30 -0
  94. package/libx/_runtime/common/utils/keys.js +1 -1
  95. package/libx/_runtime/common/utils/normalizeTimestamp.js +25 -0
  96. package/libx/_runtime/common/utils/path.js +1 -1
  97. package/libx/_runtime/common/utils/resolveView.js +2 -1
  98. package/libx/_runtime/common/utils/rewriteAsterisks.js +6 -4
  99. package/libx/_runtime/common/utils/search2cqn4sql.js +12 -16
  100. package/libx/_runtime/common/utils/stream.js +140 -0
  101. package/libx/_runtime/common/utils/streamProp.js +29 -12
  102. package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +0 -2
  103. package/libx/_runtime/db/generic/index.js +0 -2
  104. package/libx/_runtime/db/query/delete.js +2 -2
  105. package/libx/_runtime/db/query/insert.js +2 -2
  106. package/libx/_runtime/db/query/read.js +2 -2
  107. package/libx/_runtime/db/query/run.js +2 -2
  108. package/libx/_runtime/db/query/update.js +2 -2
  109. package/libx/_runtime/db/sql-builder/BaseBuilder.js +0 -6
  110. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +23 -12
  111. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +18 -6
  112. package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -0
  113. package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -7
  114. package/libx/_runtime/db/sql-builder/UpsertBuilder.js +1 -0
  115. package/libx/_runtime/db/utils/normalizeTimeData.js +7 -3
  116. package/libx/_runtime/fiori/draft.js +2 -0
  117. package/libx/_runtime/fiori/generic/activate.js +8 -9
  118. package/libx/_runtime/fiori/generic/before.js +30 -20
  119. package/libx/_runtime/fiori/generic/cancel.js +5 -3
  120. package/libx/_runtime/fiori/generic/delete.js +5 -3
  121. package/libx/_runtime/fiori/generic/edit.js +7 -7
  122. package/libx/_runtime/fiori/generic/index.js +10 -16
  123. package/libx/_runtime/fiori/generic/new.js +5 -3
  124. package/libx/_runtime/fiori/generic/patch.js +11 -8
  125. package/libx/_runtime/fiori/generic/prepare.js +13 -6
  126. package/libx/_runtime/fiori/generic/read.js +12 -6
  127. package/libx/_runtime/fiori/lean-draft.js +207 -152
  128. package/libx/_runtime/fiori/utils/delete.js +10 -5
  129. package/libx/_runtime/fiori/utils/req.js +17 -5
  130. package/libx/_runtime/fiori/utils/stream.js +36 -0
  131. package/libx/_runtime/hana/Service.js +12 -9
  132. package/libx/_runtime/hana/conversion.js +10 -15
  133. package/libx/_runtime/hana/driver.js +2 -0
  134. package/libx/_runtime/hana/execute.js +28 -6
  135. package/libx/_runtime/hana/pool.js +36 -122
  136. package/libx/_runtime/hana/search2cqn4sql.js +34 -36
  137. package/libx/_runtime/messaging/enterprise-messaging-utils/getTenantInfo.js +2 -6
  138. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +3 -1
  139. package/libx/_runtime/messaging/enterprise-messaging.js +10 -58
  140. package/libx/_runtime/messaging/outbox/utils.js +1 -1
  141. package/libx/_runtime/remote/Service.js +20 -1
  142. package/libx/_runtime/remote/utils/client.js +3 -5
  143. package/libx/_runtime/sqlite/Service.js +4 -6
  144. package/libx/_runtime/sqlite/conversion.js +3 -13
  145. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +9 -6
  146. package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +6 -1
  147. package/libx/_runtime/sqlite/execute.js +5 -16
  148. package/libx/odata/afterburner.js +22 -6
  149. package/libx/odata/grammar.pegjs +6 -1
  150. package/libx/odata/parser.js +1 -1
  151. package/libx/rest/RestAdapter.js +16 -9
  152. package/libx/rest/RestRequest.js +1 -1
  153. package/libx/rest/middleware/input.js +2 -1
  154. package/libx/rest/middleware/operation.js +1 -0
  155. package/libx/rest/middleware/parse.js +3 -2
  156. package/libx/rest/middleware/payload.js +9 -8
  157. package/libx/rest/middleware/read.js +1 -0
  158. package/package.json +9 -16
  159. package/server.js +1 -1
  160. package/app/fiori/preview.js +0 -270
  161. package/app/fiori/routes.js +0 -59
  162. package/bin/build/buildTaskEngine.js +0 -360
  163. package/bin/build/buildTaskFactory.js +0 -283
  164. package/bin/build/buildTaskHandler.js +0 -241
  165. package/bin/build/buildTaskProvider.js +0 -22
  166. package/bin/build/buildTaskProviderFactory.js +0 -175
  167. package/bin/build/cds.js +0 -5
  168. package/bin/build/constants.js +0 -66
  169. package/bin/build/index.js +0 -58
  170. package/bin/build/provider/buildTaskHandlerEdmx.js +0 -82
  171. package/bin/build/provider/buildTaskHandlerFeatureToggles.js +0 -131
  172. package/bin/build/provider/buildTaskHandlerInternal.js +0 -254
  173. package/bin/build/provider/buildTaskProviderInternal.js +0 -383
  174. package/bin/build/provider/fiori/index.js +0 -171
  175. package/bin/build/provider/hana/2migration.js +0 -179
  176. package/bin/build/provider/hana/index.js +0 -505
  177. package/bin/build/provider/hana/migrationtable.js +0 -472
  178. package/bin/build/provider/hana/template/.hdiconfig-haas +0 -163
  179. package/bin/build/provider/hana/template/.hdiconfig-hanacloud +0 -137
  180. package/bin/build/provider/hana/template/.hdinamespace +0 -4
  181. package/bin/build/provider/hana/template/package.json +0 -12
  182. package/bin/build/provider/hana/template/undeploy.json +0 -5
  183. package/bin/build/provider/java/index.js +0 -111
  184. package/bin/build/provider/java-cf/index.js +0 -1
  185. package/bin/build/provider/mtx/index.js +0 -268
  186. package/bin/build/provider/mtx/resourcesTarBuilder.js +0 -95
  187. package/bin/build/provider/mtx-extension/index.js +0 -131
  188. package/bin/build/provider/mtx-sidecar/index.js +0 -137
  189. package/bin/build/provider/node-cf/index.js +0 -1
  190. package/bin/build/provider/nodejs/index.js +0 -192
  191. package/bin/build/util.js +0 -299
  192. package/bin/cds.js +0 -125
  193. package/bin/deploy/to-hana/cfUtil.js +0 -355
  194. package/bin/deploy/to-hana/gitUtil.js +0 -57
  195. package/bin/deploy/to-hana/hana.js +0 -306
  196. package/bin/deploy/to-hana/hdiDeployUtil.js +0 -153
  197. package/bin/deploy/to-hana/index.js +0 -16
  198. package/bin/deploy/to-hana/mtaUtil.js +0 -170
  199. package/bin/mtx/in-cds.js +0 -17
  200. package/bin/plugins.js +0 -32
  201. package/bin/run.js +0 -24
  202. package/bin/utils/log.js +0 -24
  203. package/bin/version.js +0 -178
  204. package/libx/_runtime/audit/Service.js +0 -222
  205. package/libx/_runtime/audit/generic/personal/access.js +0 -61
  206. package/libx/_runtime/audit/generic/personal/index.js +0 -56
  207. package/libx/_runtime/audit/generic/personal/modification.js +0 -132
  208. package/libx/_runtime/audit/generic/personal/utils.js +0 -186
  209. package/libx/_runtime/audit/utils/log.js +0 -23
  210. package/libx/_runtime/audit/utils/v2.js +0 -176
  211. package/libx/_runtime/db/data-conversion/timestamp.js +0 -9
  212. package/libx/_runtime/db/generic/integrity.js +0 -455
  213. package/srv/audit-log.cds +0 -87
  214. package/srv/mtx.cds +0 -2
  215. package/srv/mtx.js +0 -8
  216. /package/lib/{core → linked}/classes.js +0 -0
  217. /package/lib/{core → linked}/entities.js +0 -0
@@ -1,132 +0,0 @@
1
- const cds = require('../../../cds')
2
-
3
- const getTemplate = require('../../../common/utils/template')
4
- const templateProcessor = require('../../../common/utils/templateProcessor')
5
-
6
- const {
7
- getMapKeyForCurrentRequest,
8
- getRootEntity,
9
- getPick,
10
- createLogEntry,
11
- addObjectID,
12
- addDataSubject,
13
- addDataSubjectForDetailsEntity,
14
- resolveDataSubjectPromises
15
- } = require('./utils')
16
-
17
- let auditLogService
18
-
19
- const attachDiffToContextHandler = async function (req) {
20
- // store diff in audit data structure at context
21
- const _audit = req.context._audit || (req.context._audit = {})
22
- if (!_audit.diffs) _audit.diffs = new Map()
23
- _audit.diffs.set(req._.query, await req.diff())
24
- }
25
-
26
- const _getOldAndNew = (action, row, key) => {
27
- let oldValue = action === 'Create' ? null : row._old && row._old[key]
28
- if (oldValue === undefined) oldValue = null
29
- let newValue = action === 'Delete' ? null : row[key]
30
- if (newValue === undefined) newValue = null
31
- return { oldValue, newValue }
32
- }
33
-
34
- const _addAttribute = (log, action, row, key) => {
35
- if (!log.attributes.find(ele => ele.name === key)) {
36
- const { oldValue, newValue } = _getOldAndNew(action, row, key)
37
- if (oldValue !== newValue)
38
- log.attributes.push({ name: key, oldValue: String(oldValue), newValue: String(newValue) })
39
- }
40
- }
41
-
42
- const _processorFnModification = (modificationLogs, model, req, beforeWrite) => elementInfo => {
43
- if (!elementInfo.row._op) return
44
-
45
- const { row, key, element, plain } = elementInfo
46
-
47
- // delete in before phase, create and update in after phase
48
- if ((row._op === 'delete') !== !!beforeWrite) return
49
-
50
- const entity = getRootEntity(element)
51
- const action = row._op[0].toUpperCase() + row._op.slice(1)
52
-
53
- // create or augment log entry
54
- const modificationLog = createLogEntry(modificationLogs, entity, row)
55
-
56
- // process categories
57
- for (const category of plain.categories) {
58
- if (category === 'ObjectID') {
59
- addObjectID(modificationLog, row, key)
60
- } else if (category === 'DataSubjectID') {
61
- addDataSubject(modificationLog, row, key, entity)
62
- } else if (category === 'IsPotentiallyPersonal' || category === 'IsPotentiallySensitive') {
63
- _addAttribute(modificationLog, action, row, key)
64
- }
65
- }
66
-
67
- // add promise to determine data subject if a DataSubjectDetails entity
68
- if (
69
- (entity['@PersonalData.EntitySemantics'] === 'DataSubjectDetails' ||
70
- entity['@PersonalData.EntitySemantics'] === 'Other') &&
71
- modificationLog.dataSubject.id.length === 0 // > id still an array -> promise not yet set
72
- ) {
73
- addDataSubjectForDetailsEntity(row, modificationLog, req, entity, model)
74
- }
75
- }
76
-
77
- const _getDataModificationLogs = (req, tx, diff, beforeWrite) => {
78
- const template = getTemplate(
79
- `personal_${req.event}`.toLowerCase(),
80
- Object.assign({ name: req.target._service.name, model: tx.model }),
81
- req.target,
82
- { pick: getPick(req.event) }
83
- )
84
-
85
- const modificationLogs = {}
86
- const processFn = _processorFnModification(modificationLogs, tx.model, req, beforeWrite)
87
- templateProcessor({ processFn, row: diff, template })
88
-
89
- return modificationLogs
90
- }
91
-
92
- const _calcModificationLogsHandler = async function (req, beforeWrite, that) {
93
- const mapKey = getMapKeyForCurrentRequest(req)
94
-
95
- const _audit = req.context._audit || (req.context._audit = {})
96
- const modificationLogs = _getDataModificationLogs(req, that, _audit.diffs.get(mapKey), beforeWrite)
97
-
98
- // store modificationLogs in audit data structure at context
99
- if (!_audit.modificationLogs) _audit.modificationLogs = new Map()
100
- const existingLogs = _audit.modificationLogs.get(mapKey) || {}
101
- _audit.modificationLogs.set(mapKey, Object.assign(existingLogs, modificationLogs))
102
-
103
- // execute the data subject promises before going along to on phase
104
- // guarantees that the reads are executed before the data is modified
105
- await resolveDataSubjectPromises(modificationLogs)
106
- }
107
-
108
- const calcModificationLogsHandler4Before = function (req) {
109
- return _calcModificationLogsHandler(req, true, this)
110
- }
111
-
112
- const calcModificationLogsHandler4After = function (_, req) {
113
- return _calcModificationLogsHandler(req, false, this)
114
- }
115
-
116
- const emitModificationHandler = async function (_, req) {
117
- auditLogService = auditLogService || (await cds.connect.to('audit-log'))
118
-
119
- const modificationLogs = req.context._audit.modificationLogs.get(req.query)
120
- const modifications = Object.keys(modificationLogs)
121
- .map(k => modificationLogs[k])
122
- .filter(log => log.attributes.length)
123
-
124
- await auditLogService.emit('dataModificationLog', { modifications })
125
- }
126
-
127
- module.exports = {
128
- attachDiffToContextHandler,
129
- calcModificationLogsHandler4Before,
130
- calcModificationLogsHandler4After,
131
- emitModificationHandler
132
- }
@@ -1,186 +0,0 @@
1
- const cds = require('../../../cds')
2
-
3
- const { getDataSubject } = require('../../../common/utils/csn')
4
-
5
- const WRITE = { CREATE: 1, UPDATE: 1, DELETE: 1 }
6
-
7
- const getMapKeyForCurrentRequest = req => {
8
- // running in srv or db layer? -> srv's req.query used as key of diff and logs maps at req.context
9
- // REVISIT: req._tx should not be used like that!
10
- return req.tx.isDatabaseService ? req._.query : req.query
11
- }
12
-
13
- const getRootEntity = element => {
14
- let entity = element.parent
15
- while (entity.kind !== 'entity') entity = entity.parent
16
- return entity
17
- }
18
-
19
- const _hasPersonalData = e => {
20
- if (!e['@PersonalData.DataSubjectRole']) return
21
- if (!e['@PersonalData.EntitySemantics']) return
22
- return !!Object.values(e.elements).some(
23
- e => e['@PersonalData.IsPotentiallyPersonal'] || e['@PersonalData.IsPotentiallySensitive']
24
- )
25
- }
26
-
27
- const auditAnnotations = {
28
- CREATE: '@AuditLog.Operation.Insert',
29
- UPDATE: '@AuditLog.Operation.Update',
30
- DELETE: '@AuditLog.Operation.Delete',
31
- READ: '@AuditLog.Operation.Read'
32
- }
33
-
34
- const getPick = event => {
35
- return (element, target) => {
36
- if (!_hasPersonalData(target)) return
37
- if (!auditAnnotations[event] || !target[auditAnnotations[event]]) return
38
-
39
- const categories = []
40
- if (!element.isAssociation && element.key) categories.push('ObjectID')
41
- if (
42
- !element.isAssociation &&
43
- element['@PersonalData.FieldSemantics'] === 'DataSubjectID' &&
44
- target['@PersonalData.EntitySemantics'] === 'DataSubject'
45
- )
46
- categories.push('DataSubjectID')
47
- if (event in WRITE && element['@PersonalData.IsPotentiallyPersonal']) categories.push('IsPotentiallyPersonal')
48
- if (element['@PersonalData.IsPotentiallySensitive']) categories.push('IsPotentiallySensitive')
49
- if (categories.length) return { categories }
50
- }
51
- }
52
-
53
- const _getHash = (entity, row) => {
54
- return `${entity.name}(${Object.keys(entity.keys)
55
- .map(k => `${k}=${row[k]}`)
56
- .join(',')})`
57
- }
58
-
59
- const createLogEntry = (logs, entity, row) => {
60
- const hash = _getHash(entity, row)
61
- let log = logs[hash]
62
- if (!log) {
63
- logs[hash] = {
64
- dataObject: { type: entity.name, id: [] },
65
- dataSubject: { id: [], role: entity['@PersonalData.DataSubjectRole'] },
66
- attributes: [],
67
- attachments: []
68
- }
69
- log = logs[hash]
70
- }
71
- return log
72
- }
73
-
74
- const addObjectID = (log, row, key) => {
75
- if (!log.dataObject.id.find(ele => ele.keyName === key) && key !== 'IsActiveEntity')
76
- log.dataObject.id.push({ keyName: key, value: String(row[key]) })
77
- }
78
-
79
- const addDataSubject = (log, row, key, entity) => {
80
- if (!log.dataSubject.type) log.dataSubject.type = entity.name
81
- if (!log.dataSubject.id.find(ele => ele.key === key)) {
82
- const value = row[key] || (row._old && row._old[key])
83
- log.dataSubject.id.push({ keyName: key, value: String(value) })
84
- }
85
- }
86
-
87
- const _addKeysToWhere = (keys, row, alias) =>
88
- keys
89
- .filter(key => !key.isAssociation && key.name !== 'IsActiveEntity')
90
- .reduce((keys, key) => {
91
- if (keys.length) keys.push('and')
92
- keys.push({ ref: [alias, key.name] }, '=', { val: row[key.name] })
93
- return keys
94
- }, [])
95
-
96
- const _keyColumns = (keys, alias) =>
97
- keys.filter(key => !key.isAssociation && key.name !== 'IsActiveEntity').map(key => ({ ref: [alias, key.name] }))
98
-
99
- const _alias = entity => entity.name.replace(`${entity._service.name}.`, '').replace('.', '_')
100
-
101
- const _buildSubSelect = (model, { entity, relative, element, next }, row, previousCqn) => {
102
- // relative is a parent or an entity itself
103
-
104
- const keys = Object.values(entity.keys)
105
-
106
- const entityName = entity.name
107
- const as = _alias(entity)
108
-
109
- const childCqn = SELECT.from({ ref: [entityName], as }).columns(_keyColumns(keys, as))
110
-
111
- const targetAlias = _alias(element._target)
112
- const relativeAlias = _alias(relative)
113
-
114
- childCqn.where(relative._relations[element.name].join(targetAlias, relativeAlias))
115
-
116
- if (previousCqn) {
117
- childCqn.where('exists', previousCqn)
118
- } else {
119
- childCqn.where(_addKeysToWhere(keys, row, as))
120
- }
121
- if (next) return _buildSubSelect(model, next, {}, childCqn)
122
- return childCqn
123
- }
124
-
125
- const _getDataSubjectIdPromise = ({ dataSubjectEntity, subs }, row, req, model) => {
126
- const keys = Object.values(dataSubjectEntity.keys)
127
- const as = _alias(dataSubjectEntity)
128
-
129
- const cqn = SELECT.from({ ref: [dataSubjectEntity.name], as })
130
- .columns(_keyColumns(keys, as))
131
- .where(['exists', _buildSubSelect(model, subs[0], row)])
132
- // entity reused in different branches => must check all
133
- for (let i = 1; i < subs.length; i++) {
134
- cqn.or(['exists', _buildSubSelect(model, subs[i], row)])
135
- }
136
- return cds
137
- .tx(req)
138
- .run(cqn)
139
- .then(res => {
140
- const id = []
141
- for (const k in res[0]) id.push({ keyName: k, value: String(res[0][k]) })
142
- return id
143
- })
144
- }
145
-
146
- const addDataSubjectForDetailsEntity = (row, log, req, entity, model) => {
147
- const role = entity['@PersonalData.DataSubjectRole']
148
-
149
- const dataSubjectInfo = getDataSubject(entity, model, role)
150
-
151
- log.dataSubject.type = dataSubjectInfo.dataSubjectEntity.name
152
-
153
- /*
154
- * for each req (cf. $batch with atomicity) and data subject role (e.g., customer vs supplier),
155
- * store (in audit data structure at context) and reuse a single promise to look up the respective data subject
156
- */
157
- const mapKey = getMapKeyForCurrentRequest(req)
158
- const _audit = req.context._audit || (req.context._audit = {})
159
- if (!_audit.dataSubjects) _audit.dataSubjects = new Map()
160
- if (!_audit.dataSubjects.has(mapKey)) _audit.dataSubjects.set(mapKey, new Map())
161
- const map = _audit.dataSubjects.get(mapKey)
162
- if (map.has(role)) log.dataSubject.id = map.get(role)
163
- // REVISIT by downward lookups row might already contain ID - some potential to optimize
164
- else map.set(role, _getDataSubjectIdPromise(dataSubjectInfo, row, req, model))
165
- }
166
-
167
- const resolveDataSubjectPromises = log => {
168
- const logs = Object.values(log)
169
- return Promise.all(logs.map(log => log.dataSubject.id)).then(IDs =>
170
- logs.map((log, i) => {
171
- log.dataSubject.id = IDs[i]
172
- return log
173
- })
174
- )
175
- }
176
-
177
- module.exports = {
178
- getMapKeyForCurrentRequest,
179
- getRootEntity,
180
- getPick,
181
- createLogEntry,
182
- addObjectID,
183
- addDataSubject,
184
- addDataSubjectForDetailsEntity,
185
- resolveDataSubjectPromises
186
- }
@@ -1,23 +0,0 @@
1
- function getObjectAndDataSubject(entry) {
2
- const { dataObject, dataSubject } = entry
3
- dataObject.id = dataObject.id.reduce((acc, cur) => {
4
- acc[cur.keyName] = cur.value
5
- return acc
6
- }, {})
7
- if (dataSubject) {
8
- dataSubject.id = dataSubject.id.reduce((acc, cur) => {
9
- acc[cur.keyName] = cur.value
10
- return acc
11
- }, {})
12
- }
13
- return { dataObject, dataSubject }
14
- }
15
-
16
- function getAttributeToLog(ele) {
17
- return { name: ele.name, old: ele.oldValue || 'null', new: ele.newValue || 'null' }
18
- }
19
-
20
- module.exports = {
21
- getObjectAndDataSubject,
22
- getAttributeToLog
23
- }
@@ -1,176 +0,0 @@
1
- const cds = require('../../cds')
2
- const LOG = cds.log('audit-log')
3
- const { getObjectAndDataSubject, getAttributeToLog } = require('./log')
4
-
5
- async function connect(credentials, securityContext) {
6
- let auditLogging
7
-
8
- try {
9
- // eslint-disable-next-line cds/no-missing-dependencies -- needs to be added by app dev
10
- auditLogging = require('@sap/audit-logging')
11
- } catch (error) {
12
- // not able to require lib -> no audit logging ootb
13
- return Promise.resolve()
14
- }
15
-
16
- try {
17
- return await auditLogging.v2(credentials, securityContext)
18
- } catch (error) {
19
- LOG._warn && LOG.warn('Unable to initialize audit-logging client with error:', error)
20
- return Promise.resolve()
21
- }
22
- }
23
-
24
- function sendDataAccessLog(entry) {
25
- return new Promise((resolve, reject) => {
26
- entry.log(function (err) {
27
- if (err && LOG._warn) {
28
- err.message = `Writing data access log failed with error: ${err.message}`
29
- return reject(err)
30
- }
31
-
32
- resolve()
33
- })
34
- })
35
- }
36
-
37
- function sendDataModificationLog(entry) {
38
- return new Promise((resolve, reject) => {
39
- entry.logPrepare(function (err) {
40
- if (err) {
41
- err.message = `Preparing data modification log failed with error: ${err.message}`
42
- return reject(err)
43
- }
44
-
45
- entry.logSuccess(function (err) {
46
- if (err) {
47
- err.message = `Writing data modification log failed with error: ${err.message}`
48
- return reject(err)
49
- }
50
-
51
- resolve()
52
- })
53
- })
54
- })
55
- }
56
-
57
- function sendSecurityLog(entry) {
58
- return new Promise((resolve, reject) => {
59
- entry.log(function (err) {
60
- if (err) {
61
- err.message = `Writing security log failed with error: ${err.message}`
62
- return reject(err)
63
- }
64
-
65
- resolve()
66
- })
67
- })
68
- }
69
-
70
- function sendConfigChangeLog(entry) {
71
- return new Promise((resolve, reject) => {
72
- entry.logPrepare(function (err) {
73
- if (err) {
74
- err.message = `Preparing configuration change log failed with error: ${err.message}`
75
- return reject(err)
76
- }
77
-
78
- entry.logSuccess(function (err) {
79
- if (err) {
80
- err.message = `Writing configuration change log failed with error: ${err.message}`
81
- return reject(err)
82
- }
83
-
84
- resolve()
85
- })
86
- })
87
- })
88
- }
89
-
90
- function buildDataAccessLogs(auditLogClient, accesses, tenant, user) {
91
- const entries = []
92
- const errors = []
93
-
94
- for (const access of accesses) {
95
- try {
96
- const { dataObject, dataSubject } = getObjectAndDataSubject(access)
97
- const entry = auditLogClient.read(dataObject).dataSubject(dataSubject).by(user)
98
- if (tenant) entry.tenant(tenant)
99
- for (const each of access.attributes) entry.attribute(each)
100
- if (access.attachments) for (const each of access.attachments) entry.attachment(each)
101
- entries.push(entry)
102
- } catch (err) {
103
- err.message = `Building data access log failed with error: ${err.message}`
104
- errors.push(err)
105
- }
106
- }
107
-
108
- return { entries, errors }
109
- }
110
-
111
- function buildDataModificationLogs(auditLogClient, modifications, tenant, user) {
112
- const entries = []
113
- const errors = []
114
-
115
- for (const modification of modifications) {
116
- try {
117
- const { dataObject, dataSubject } = getObjectAndDataSubject(modification)
118
- const entry = auditLogClient.update(dataObject).dataSubject(dataSubject).by(user)
119
- if (tenant) entry.tenant(tenant)
120
- for (const each of modification.attributes) entry.attribute(getAttributeToLog(each))
121
- entries.push(entry)
122
- } catch (err) {
123
- err.message = `Building data modification log failed with error: ${err.message}`
124
- errors.push(err)
125
- }
126
- }
127
-
128
- return { entries, errors }
129
- }
130
-
131
- function buildSecurityLog(auditLogClient, action, data, tenant, user) {
132
- let entry
133
-
134
- try {
135
- entry = auditLogClient.securityMessage('action: %s, data: %s', action, data)
136
- if (tenant) entry.tenant(tenant)
137
- if (user) entry.by(user)
138
- } catch (err) {
139
- err.message = `Building security log failed with error: ${err.message}`
140
- throw err
141
- }
142
-
143
- return entry
144
- }
145
-
146
- function buildConfigChangeLogs(auditLogClient, configurations, tenant, user) {
147
- const entries = []
148
- const errors = []
149
-
150
- for (const configuration of configurations) {
151
- try {
152
- const { dataObject } = getObjectAndDataSubject(configuration)
153
- const entry = auditLogClient.configurationChange(dataObject).by(user)
154
- if (tenant) entry.tenant(tenant)
155
- for (const each of configuration.attributes) entry.attribute(getAttributeToLog(each))
156
- entries.push(entry)
157
- } catch (err) {
158
- err.message = `Building configuration change log failed with error: ${err.message}`
159
- errors.push(err)
160
- }
161
- }
162
-
163
- return { entries, errors }
164
- }
165
-
166
- module.exports = {
167
- connect,
168
- buildDataAccessLogs,
169
- buildDataModificationLogs,
170
- buildSecurityLog,
171
- buildConfigChangeLogs,
172
- sendDataAccessLog,
173
- sendDataModificationLog,
174
- sendSecurityLog,
175
- sendConfigChangeLog
176
- }
@@ -1,9 +0,0 @@
1
- function timestampToISO(ts) {
2
- if (typeof ts === 'number') return new Date(ts).toISOString()
3
- if (ts instanceof Date) return ts.toISOString()
4
- return ts
5
- }
6
-
7
- module.exports = {
8
- timestampToISO
9
- }