@sap/cds 7.1.2 → 7.2.0

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 (80) hide show
  1. package/CHANGELOG.md +55 -4
  2. package/apis/cds.d.ts +10 -6
  3. package/apis/connect.d.ts +0 -1
  4. package/apis/core.d.ts +54 -5
  5. package/apis/log.d.ts +19 -6
  6. package/apis/models.d.ts +0 -18
  7. package/apis/ql.d.ts +23 -23
  8. package/apis/serve.d.ts +17 -14
  9. package/apis/services.d.ts +40 -29
  10. package/apis/test.d.ts +1 -2
  11. package/bin/serve.js +4 -4
  12. package/lib/auth/basic-auth.js +1 -1
  13. package/lib/auth/dummy-auth.js +2 -1
  14. package/lib/auth/ias-auth.js +68 -2
  15. package/lib/auth/index.js +5 -5
  16. package/lib/auth/jwt-auth.js +40 -24
  17. package/lib/auth/mocked-users.js +0 -13
  18. package/lib/auth/passport-basic.js +2 -0
  19. package/lib/auth/passport-digest.js +2 -0
  20. package/lib/compile/etc/_localized.js +0 -1
  21. package/lib/compile/extend.js +16 -0
  22. package/lib/compile/for/lean_drafts.js +38 -6
  23. package/lib/compile/resolve.js +7 -5
  24. package/lib/compile/to/json.js +6 -2
  25. package/lib/dbs/cds-deploy.js +3 -3
  26. package/lib/env/cds-env.js +3 -3
  27. package/lib/env/cds-requires.js +1 -0
  28. package/lib/env/defaults.js +8 -1
  29. package/lib/env/schemas/cds-rc.json +27 -3
  30. package/lib/i18n/localize.js +3 -3
  31. package/lib/index.js +4 -0
  32. package/lib/log/cds-log.js +10 -1
  33. package/lib/ql/Whereable.js +7 -3
  34. package/lib/req/user.js +18 -16
  35. package/lib/srv/middlewares/sap-statistics.js +3 -3
  36. package/lib/srv/middlewares/trace.js +5 -4
  37. package/lib/srv/srv-dispatch.js +10 -9
  38. package/lib/utils/axios.js +3 -0
  39. package/lib/utils/cds-test.js +3 -0
  40. package/lib/utils/cds-utils.js +2 -0
  41. package/libx/_runtime/auth/index.js +8 -32
  42. package/libx/_runtime/auth/strategies/ias-auth.js +1 -77
  43. package/libx/_runtime/auth/strategies/mock.js +1 -12
  44. package/libx/_runtime/auth/strategies/xssecUtils.js +2 -2
  45. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -9
  46. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -0
  47. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +5 -2
  48. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +4 -0
  49. package/libx/_runtime/common/composition/data.js +5 -3
  50. package/libx/_runtime/common/composition/insert.js +6 -3
  51. package/libx/_runtime/common/composition/update.js +12 -8
  52. package/libx/_runtime/common/error/constants.js +6 -1
  53. package/libx/_runtime/common/generic/auth/requires.js +11 -3
  54. package/libx/_runtime/common/generic/auth/restrict.js +21 -15
  55. package/libx/_runtime/common/generic/auth/restrictions.js +5 -2
  56. package/libx/_runtime/common/generic/crud.js +6 -0
  57. package/libx/_runtime/common/generic/paging.js +2 -1
  58. package/libx/_runtime/common/utils/cqn2cqn4sql.js +3 -5
  59. package/libx/_runtime/common/utils/resolveView.js +3 -1
  60. package/libx/_runtime/common/utils/restrictions.js +47 -0
  61. package/libx/_runtime/db/data-conversion/post-processing.js +3 -3
  62. package/libx/_runtime/db/generic/input.js +1 -1
  63. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -17
  64. package/libx/_runtime/fiori/lean-draft.js +6 -3
  65. package/libx/_runtime/hana/driver.js +2 -4
  66. package/libx/_runtime/hana/pool.js +1 -1
  67. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
  68. package/libx/_runtime/messaging/outbox/utils.js +1 -2
  69. package/libx/_runtime/remote/utils/client.js +1 -1
  70. package/libx/_runtime/sqlite/Service.js +0 -4
  71. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +2 -1
  72. package/libx/odata/afterburner.js +5 -3
  73. package/libx/odata/cqn2odata.js +7 -7
  74. package/libx/odata/utils.js +4 -1
  75. package/libx/rest/RestAdapter.js +15 -16
  76. package/package.json +1 -1
  77. package/lib/auth/xsuaa-auth.js +0 -2
  78. package/libx/_runtime/auth/utils.js +0 -32
  79. package/libx/audit-log/client.cds +0 -0
  80. package/libx/audit-log/client.js +0 -0
@@ -30,6 +30,7 @@ const _addKeysDeep = (keys, keysCollector, ignoreManagedBacklinks) => {
30
30
  function _keysOf(entity, ignoreManagedBacklinks) {
31
31
  const keysCollector = []
32
32
  if (!entity || !entity.keys) return keysCollector
33
+
33
34
  _addKeysDeep(entity.keys, keysCollector, ignoreManagedBacklinks)
34
35
  return keysCollector
35
36
  }
@@ -174,15 +175,16 @@ function _convertVal(element, value) {
174
175
  throw new Error('Not a valid integer') // TODO
175
176
 
176
177
  case 'cds.String':
177
- case 'cds.LargeString':
178
+ case 'cds.LargeString':
179
+ return String(value)
180
+ case 'cds.Double':
181
+ return parseFloat(value)
178
182
  case 'cds.Decimal':
179
183
  case 'cds.DecimalFloat':
180
- case 'cds.Double':
181
184
  case 'cds.Int64':
182
185
  case 'cds.Integer64':
183
186
  if (typeof value === 'string') return value
184
187
  return String(value)
185
-
186
188
  case 'cds.Boolean':
187
189
  return typeof value === 'string' ? value === 'true' : value
188
190
 
@@ -73,7 +73,7 @@ function hasValidProps(obj, ...names) {
73
73
  return true
74
74
  }
75
75
 
76
- function _args(args) {
76
+ function _args(args, func) {
77
77
  const res = []
78
78
 
79
79
  for (const cur of args) {
@@ -83,11 +83,11 @@ function _args(args) {
83
83
  }
84
84
 
85
85
  if (hasValidProps(cur, 'func', 'args')) {
86
- res.push(`${cur.func}(${_args(cur.args)})`)
86
+ res.push(`${cur.func}(${_args(cur.args, cur.func)})`)
87
87
  } else if (hasValidProps(cur, 'ref')) {
88
88
  res.push(_format(cur))
89
89
  } else if (hasValidProps(cur, 'val')) {
90
- res.push(_format(cur))
90
+ res.push(_format(cur, null, null, null, null, func))
91
91
  }
92
92
  }
93
93
 
@@ -111,20 +111,20 @@ const _odataV2Func = (func, args) => {
111
111
  // this doesn't support the contains signature with two collections as args, introduced in odata v4.01
112
112
  return `substringof(${_args([args[1], args[0]])})`
113
113
  default:
114
- return `${func}(${_args(args)})`
114
+ return `${func}(${_args(args, func)})`
115
115
  }
116
116
  }
117
117
 
118
- const _format = (cur, elementName, target, kind, isLambda) => {
118
+ const _format = (cur, elementName, target, kind, isLambda, func) => {
119
119
  if (typeof cur !== 'object') return encodeURIComponent(formatVal(cur, elementName, target, kind))
120
120
  if (hasValidProps(cur, 'ref'))
121
121
  return encodeURIComponent(isLambda ? [LAMBDA_VARIABLE, ...cur.ref].join('/') : cur.ref[0].id || cur.ref.join('/'))
122
- if (hasValidProps(cur, 'val')) return encodeURIComponent(formatVal(cur.val, elementName, target, kind))
122
+ if (hasValidProps(cur, 'val')) return encodeURIComponent(formatVal(cur.val, elementName, target, kind, func))
123
123
  if (hasValidProps(cur, 'xpr')) return `(${_xpr(cur.xpr, target, kind, isLambda)})`
124
124
  // REVISIT: How to detect the types for all functions?
125
125
  if (hasValidProps(cur, 'func')) {
126
126
  if (cur.args?.length) {
127
- return kind === 'odata-v2' ? _odataV2Func(cur.func, cur.args) : `${cur.func}(${_args(cur.args)})`
127
+ return kind === 'odata-v2' ? _odataV2Func(cur.func, cur.args) : `${cur.func}(${_args(cur.args, cur.func)})`
128
128
  }
129
129
  return `${cur.func}()`
130
130
  }
@@ -1,6 +1,8 @@
1
1
  const { toBase64url } = require('../_runtime/common/utils/binary')
2
2
  const cds = require('../_runtime/cds')
3
3
 
4
+ const MATH_FUNC = {'round': 1, 'floor': 1, 'ceiling': 1}
5
+
4
6
  const getSafeNumber = str => {
5
7
  const n = Number(str)
6
8
  return Number.isSafeInteger(n) || String(n) === str ? n : str
@@ -48,11 +50,12 @@ const _getElement = (csnTarget, key) => {
48
50
  }
49
51
  }
50
52
 
51
- const formatVal = (val, elementName, csnTarget, kind) => {
53
+ const formatVal = (val, elementName, csnTarget, kind, func) => {
52
54
  if (val === null || val === 'null') return 'null'
53
55
  if (typeof val === 'boolean') return val
54
56
  if (typeof val === 'number') return getSafeNumber(val)
55
57
  if (!csnTarget && typeof val === 'string' && UUID.test(val)) return kind === 'odata-v2' ? `guid'${val}'` : val
58
+ if (typeof val === 'string' && func in MATH_FUNC) return val
56
59
  const element = _getElement(csnTarget, elementName)
57
60
  if (kind === 'odata-v2') {
58
61
  switch (element.type) {
@@ -17,9 +17,13 @@ const error = require('./middleware/error')
17
17
  const { alias2ref } = require('../_runtime/common/utils/csn')
18
18
  const { bufferToBase64 } = require('../_runtime/common/utils/binary')
19
19
 
20
+ const { getAccessRestrictions } = require('../_runtime/common/utils/restrictions')
21
+
20
22
  const RestAdapter = function (srv) {
21
23
  alias2ref(srv) // REVISIT: that's an anti pattern in new prototocol adapter setups
22
24
 
25
+ const accessRestrictions = getAccessRestrictions(srv)
26
+
23
27
  const router = express.Router()
24
28
 
25
29
  // pass srv-related stuff to middlewares via req
@@ -64,24 +68,19 @@ const RestAdapter = function (srv) {
64
68
  // REVISIT: ensure there always is a user (should be the case with new middlewares -> remove with old middlewares)
65
69
  if (!req.user) req.user = new cds.User.default
66
70
 
67
- // REVISIT: This is authorization enforcement which is protocol-independent -> should move to service layer
68
- const requires = srv.definition?.['@requires']
69
- if (requires) {
70
- const ok = typeof requires === 'string' ? req.user.is(requires) : requires.some(r => req.user.is(r))
71
- if (ok) return next()
72
- } else {
73
- return next() // neither of the above
74
- }
75
-
76
- // > unauthorized or forbidden?
77
- if (req.user._is_anonymous) {
78
- // NOTE: "return req._login()" would not invoke custom error handlers
79
- if (req._login) res.set('WWW-Authenticate', `Basic realm="Users"`)
80
- else if (req.user._challenges) res.set('WWW-Authenticate', req.user._challenges.join(';'))
81
- throw cds.error('Unauthorized', { statusCode: 401, code: '401' })
82
- } else {
71
+ // check @restrict and @requires as soon as possible (DoS)
72
+ if (!accessRestrictions.some(r => req.user.is(r))) {
73
+ // > unauthorized or forbidden?
74
+ if (req.user._is_anonymous) {
75
+ // NOTE: "return req._login()" would not invoke custom error handlers
76
+ if (req._login) res.set('WWW-Authenticate', `Basic realm="Users"`)
77
+ else if (req.user._challenges) res.set('WWW-Authenticate', req.user._challenges.join(';'))
78
+ throw cds.error('Unauthorized', { statusCode: 401, code: '401' })
79
+ }
83
80
  throw cds.error('Forbidden', { statusCode: 403, code: '403' })
84
81
  }
82
+
83
+ next()
85
84
  })
86
85
 
87
86
  // -----------------------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds",
3
- "version": "7.1.2",
3
+ "version": "7.2.0",
4
4
  "description": "SAP Cloud Application Programming Model - CDS for Node.js",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
@@ -1,2 +0,0 @@
1
- // REVISIT: "kind": "xsuaa-auth" does not work because cds.requires logic does not resolve it. Intentional?
2
- module.exports = require('./jwt-auth')
@@ -1,32 +0,0 @@
1
- const UNAUTHORIZED = { statusCode: 401, code: '401', message: 'Unauthorized' }
2
- const FORBIDDEN = { statusCode: 403, code: '403', message: 'Forbidden' }
3
-
4
- const getRequiresAsArray = definition => {
5
- const requires = definition['@requires']
6
- if (requires) return Array.isArray(requires) ? requires : [requires]
7
- }
8
-
9
- const isRestricted = srv => {
10
- if (srv.definition['@requires']) return true
11
-
12
- const entities = srv.entities
13
- const entitiesKeys = Object.keys(entities)
14
-
15
- return !!(
16
- entitiesKeys.some(entity => entities[entity]['@requires'] || entities[entity]['@restrict']) ||
17
- entitiesKeys.some(entity => {
18
- const actions = entities[entity].actions
19
- actions && Object.keys(actions).some(action => actions[action]['@requires'] || actions[action]['@restrict'])
20
- }) ||
21
- Object.keys(srv.operations).some(
22
- operation => srv.operations[operation]['@requires'] || srv.operations[operation]['@restrict']
23
- )
24
- )
25
- }
26
-
27
- module.exports = {
28
- UNAUTHORIZED,
29
- FORBIDDEN,
30
- getRequiresAsArray,
31
- isRestricted
32
- }
File without changes
File without changes