@sap/cds 7.1.2 → 7.2.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 (83) hide show
  1. package/CHANGELOG.md +68 -4
  2. package/apis/cds.d.ts +10 -6
  3. package/apis/connect.d.ts +1 -2
  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 +18 -15
  9. package/apis/services.d.ts +67 -56
  10. package/apis/test.d.ts +1 -2
  11. package/bin/serve.js +4 -4
  12. package/common.cds +4 -4
  13. package/lib/auth/basic-auth.js +1 -1
  14. package/lib/auth/dummy-auth.js +2 -1
  15. package/lib/auth/ias-auth.js +68 -2
  16. package/lib/auth/index.js +5 -5
  17. package/lib/auth/jwt-auth.js +40 -24
  18. package/lib/auth/mocked-users.js +0 -13
  19. package/lib/auth/passport-basic.js +2 -0
  20. package/lib/auth/passport-digest.js +2 -0
  21. package/lib/compile/etc/_localized.js +0 -1
  22. package/lib/compile/extend.js +16 -0
  23. package/lib/compile/for/lean_drafts.js +38 -6
  24. package/lib/compile/resolve.js +7 -5
  25. package/lib/compile/to/json.js +6 -2
  26. package/lib/dbs/cds-deploy.js +3 -3
  27. package/lib/env/cds-env.js +3 -3
  28. package/lib/env/cds-requires.js +1 -0
  29. package/lib/env/defaults.js +8 -1
  30. package/lib/env/schemas/cds-rc.json +27 -3
  31. package/lib/i18n/localize.js +3 -3
  32. package/lib/index.js +4 -0
  33. package/lib/log/cds-log.js +10 -1
  34. package/lib/ql/Whereable.js +7 -3
  35. package/lib/req/user.js +18 -16
  36. package/lib/srv/middlewares/sap-statistics.js +3 -3
  37. package/lib/srv/middlewares/trace.js +5 -4
  38. package/lib/srv/srv-dispatch.js +10 -9
  39. package/lib/utils/axios.js +3 -0
  40. package/lib/utils/cds-test.js +3 -0
  41. package/lib/utils/cds-utils.js +2 -0
  42. package/libx/_runtime/auth/index.js +8 -32
  43. package/libx/_runtime/auth/strategies/ias-auth.js +1 -77
  44. package/libx/_runtime/auth/strategies/mock.js +1 -12
  45. package/libx/_runtime/auth/strategies/xssecUtils.js +2 -2
  46. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -9
  47. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +5 -0
  48. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +5 -2
  49. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/uri/UriParser.js +4 -0
  50. package/libx/_runtime/common/composition/data.js +5 -3
  51. package/libx/_runtime/common/composition/insert.js +6 -3
  52. package/libx/_runtime/common/composition/update.js +12 -8
  53. package/libx/_runtime/common/error/constants.js +6 -1
  54. package/libx/_runtime/common/generic/auth/requires.js +11 -3
  55. package/libx/_runtime/common/generic/auth/restrict.js +21 -15
  56. package/libx/_runtime/common/generic/auth/restrictions.js +5 -2
  57. package/libx/_runtime/common/generic/crud.js +6 -0
  58. package/libx/_runtime/common/generic/paging.js +3 -1
  59. package/libx/_runtime/common/i18n/messages.properties +1 -0
  60. package/libx/_runtime/common/utils/cqn2cqn4sql.js +3 -5
  61. package/libx/_runtime/common/utils/resolveView.js +3 -1
  62. package/libx/_runtime/common/utils/restrictions.js +47 -0
  63. package/libx/_runtime/db/data-conversion/post-processing.js +3 -3
  64. package/libx/_runtime/db/generic/input.js +1 -1
  65. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +0 -17
  66. package/libx/_runtime/fiori/lean-draft.js +27 -24
  67. package/libx/_runtime/hana/driver.js +2 -4
  68. package/libx/_runtime/hana/pool.js +1 -1
  69. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +2 -2
  70. package/libx/_runtime/messaging/outbox/utils.js +1 -2
  71. package/libx/_runtime/remote/Service.js +10 -9
  72. package/libx/_runtime/remote/utils/client.js +4 -3
  73. package/libx/_runtime/sqlite/Service.js +0 -4
  74. package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +2 -1
  75. package/libx/odata/afterburner.js +5 -3
  76. package/libx/odata/cqn2odata.js +7 -7
  77. package/libx/odata/utils.js +4 -1
  78. package/libx/rest/RestAdapter.js +15 -16
  79. package/package.json +1 -1
  80. package/lib/auth/xsuaa-auth.js +0 -2
  81. package/libx/_runtime/auth/utils.js +0 -32
  82. package/libx/audit-log/client.cds +0 -0
  83. package/libx/audit-log/client.js +0 -0
@@ -9,8 +9,11 @@ import { ref } from './cqn'
9
9
  // import { Service } from './cds'
10
10
 
11
11
  export class QueryAPI {
12
+
13
+ entities : LinkedModel['entities']
14
+
12
15
  /**
13
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
16
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
14
17
  */
15
18
  read: {
16
19
  <T extends ArrayConstructable<any>>(entity: T, key?: any): Awaitable<SELECT<T>, InstanceType<T>>
@@ -18,7 +21,7 @@ export class QueryAPI {
18
21
  }
19
22
 
20
23
  /**
21
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
24
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
22
25
  */
23
26
  create: {
24
27
  <T extends ArrayConstructable<any>>(entity: T, key?: any): INSERT<T>
@@ -26,7 +29,7 @@ export class QueryAPI {
26
29
  }
27
30
 
28
31
  /**
29
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
32
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
30
33
  */
31
34
  insert: {
32
35
  <T extends ArrayConstructable<any>>(data: T): INSERT<T>
@@ -34,7 +37,7 @@ export class QueryAPI {
34
37
  }
35
38
 
36
39
  /**
37
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
40
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
38
41
  */
39
42
  upsert: {
40
43
  <T extends ArrayConstructable<any>>(data: T): UPSERT<T>
@@ -42,7 +45,7 @@ export class QueryAPI {
42
45
  }
43
46
 
44
47
  /**
45
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
48
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
46
49
  */
47
50
  update: {
48
51
  <T extends ArrayConstructable<any>>(entity: T, key?: any): UPDATE<T>
@@ -50,26 +53,26 @@ export class QueryAPI {
50
53
  }
51
54
 
52
55
  /**
53
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
56
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
54
57
  */
55
58
  run: {
56
59
  (query: ConstructedQuery | ConstructedQuery[]): Promise<ResultSet | any>
57
60
  (query: Query): Promise<ResultSet | any>
58
61
  (query: string, args?: any[] | object): Promise<ResultSet | any>
59
62
  }
60
-
63
+
61
64
  /**
62
- * @see [docs](https://cap.cloud.sap/docs/node.js/cds-facade?q=cds.delete)
65
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
63
66
  */
64
67
  delete<T>(entity: Definition | string, key?: any): DELETE<T>
65
68
 
66
69
  /**
67
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-run)
70
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#srv-foreach-entity)
68
71
  */
69
72
  foreach(query: Query, callback: (row: object) => void): this
70
73
 
71
74
  /**
72
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-stream)
75
+ * @see [docs](https://cap.cloud.sap/docs/node.js/core-services#srv-stream-column)
73
76
  */
74
77
  stream: {
75
78
  (column: string): {
@@ -82,18 +85,18 @@ export class QueryAPI {
82
85
 
83
86
  /**
84
87
  * Starts or joins a transaction
85
- * @see [docs](https://cap.cloud.sap/docs/node.js/services#srv-tx)
88
+ * @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx)
86
89
  */
87
90
  tx(context?: object): Transaction
88
91
  transaction(context?: object): Transaction
89
-
92
+
90
93
  /**
91
- * @see [docs](https://pages.github.tools.sap/cap/docs/node.js/cds-context-tx?q=spawn#cds-spawn)
94
+ * @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#cds-spawn)
92
95
  */
93
96
  spawn(options: Options, fn: (tx: Transaction) => {}): SpawnEventEmitter
94
97
 
95
98
  /**
96
- * @see [docs](https://pages.github.tools.sap/cap/docs/node.js/cds-context-tx?q=cds.context#cds-context
99
+ * @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#event-contexts
97
100
  */
98
101
  context: ContextProperties
99
102
  }
@@ -107,7 +110,7 @@ type ContextProperties = {
107
110
 
108
111
  /**
109
112
  * Class cds.Service
110
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services)
113
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
111
114
  */
112
115
  export class Service extends QueryAPI {
113
116
  constructor(
@@ -126,81 +129,81 @@ export class Service extends QueryAPI {
126
129
 
127
130
  /**
128
131
  * The model from which the service's definition was loaded
129
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-reflect)
132
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
130
133
  */
131
134
  model: LinkedModel
132
135
 
133
136
  /**
134
137
  * Provides access to the entities exposed by a service
135
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-reflect)
138
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
136
139
  */
137
140
  entities: Definitions & ((namespace: string) => Definitions)
138
141
 
139
142
  /**
140
143
  * Provides access to the events declared by a service
141
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-reflect)
144
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
142
145
  */
143
146
  events: Definitions & ((namespace: string) => Definitions)
144
147
 
145
148
  /**
146
149
  * Provides access to the types exposed by a service
147
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-reflect)
150
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
148
151
  */
149
152
  types: Definitions & ((namespace: string) => Definitions)
150
153
 
151
154
  /**
152
155
  * Provides access to the operations, i.e. actions and functions, exposed by a service
153
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-reflect)
156
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
154
157
  */
155
158
  operations: Definitions & ((namespace: string) => Definitions)
156
159
 
157
160
  /**
158
161
  * Acts like a parameter-less constructor. Ensure to call `await super.init()` to have the base class’s handlers added.
159
162
  * You may register own handlers before the base class’s ones, to intercept requests before the default handlers snap in.
160
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#cds-service-subclasses)
163
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#srv-init)
161
164
  */
162
165
  init(): Promise<void>
163
166
 
164
167
  /**
165
168
  * Constructs and emits an asynchronous event.
166
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-emit)
169
+ * @see [capire docs](https://cap.cloud.sap/docs/core-services#srv-emit-event)
167
170
  */
168
171
  emit: {
169
- <T = any>(details: { event: Events; data?: object; headers?: object }): Promise<T>
170
- <T = any>(event: Events, data?: object, headers?: object): Promise<T>
172
+ <T = any>(details: { event: EventArg; data?: object; headers?: object }): Promise<T>
173
+ <T = any>(event: EventArg, data?: object, headers?: object): Promise<T>
171
174
  }
172
175
 
173
176
  /**
174
177
  * Constructs and sends a synchronous request.
175
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srvsend--method-path-data-headers--results-)
178
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#srv-send-request)
176
179
  */
177
180
  send: {
178
- <T = any>(event: Events, path: string, data?: object, headers?: object): Promise<T>
179
- <T = any>(event: Events, data?: object, headers?: object): Promise<T>
180
- <T = any>(details: { event: Events; data?: object; headers?: object }): Promise<T>
181
+ <T = any>(event: EventArg, path: string, data?: object, headers?: object): Promise<T>
182
+ <T = any>(event: EventArg, data?: object, headers?: object): Promise<T>
183
+ <T = any>(details: { event: EventArg; data?: object; headers?: object }): Promise<T>
181
184
  <T = any>(details: { query: ConstructedQuery; data?: object; headers?: object }): Promise<T>
182
- <T = any>(details: { method: Event; path: string; data?: object; headers?: object }): Promise<T>
183
- <T = any>(details: { event: Event; entity: Definition | string; data?: object; params?: object }): Promise<T>
185
+ <T = any>(details: { method: EventName; path: string; data?: object; headers?: object }): Promise<T>
186
+ <T = any>(details: { event: EventName; entity: Definition | string; data?: object; params?: object }): Promise<T>
184
187
  }
185
188
 
186
189
  /**
187
190
  * Constructs and sends a GET request.
188
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-send)
191
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
189
192
  */
190
193
  get<T = any>(entityOrPath: Target, data?: object): Promise<T>
191
194
  /**
192
195
  * Constructs and sends a POST request.
193
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-send)
196
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
194
197
  */
195
198
  post<T = any>(entityOrPath: Target, data?: object): Promise<T>
196
199
  /**
197
200
  * Constructs and sends a PUT request.
198
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-send)
201
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
199
202
  */
200
203
  put<T = any>(entityOrPath: Target, data?: object): Promise<T>
201
204
  /**
202
205
  * Constructs and sends a PATCH request.
203
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services#srv-send)
206
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
204
207
  */
205
208
  patch<T = any>(entityOrPath: Target, data?: object): Promise<T>
206
209
  /**
@@ -213,28 +216,28 @@ export class Service extends QueryAPI {
213
216
  }
214
217
 
215
218
  // The central method to dispatch events
216
- dispatch(msg: EventMessage): Promise<any>
219
+ dispatch(msg: Event): Promise<any>
217
220
 
218
221
  // Provider API
219
222
  prepend(fn: ServiceImpl): Promise<this>
220
- on<T extends Constructable>(eve: Events, entity: T, handler: CRUDEventHandler.On<InstanceType<T>, InstanceType<T> | void | Error>): this
223
+ on<T extends Constructable>(eve: EventArg, entity: T, handler: CRUDEventHandler.On<InstanceType<T>, InstanceType<T> | void | Error>): this
221
224
  on<P,R>(boundAction: (args: P) => R, service: string, handler: ActionEventHandler<P, void | Error | R>): this
222
225
  on<P,R>(action: (args: P) => R, handler: ActionEventHandler<P, void | Error | R>): this
223
- on(eve: Events, entity: Target, handler: OnEventHandler): this
224
- on(eve: Events, handler: OnEventHandler): this
226
+ on(eve: EventArg, entity: Target, handler: OnEventHandler): this
227
+ on(eve: EventArg, handler: OnEventHandler): this
225
228
 
226
229
 
227
230
  // onSucceeded (eve: Events, entity: Target, handler: EventHandler): this
228
231
  // onSucceeded (eve: Events, handler: EventHandler): this
229
232
  // onFailed (eve: Events, entity: Target, handler: EventHandler): this
230
233
  // onFailed (eve: Events, handler: EventHandler): this
231
- before<T extends Constructable>(eve: Events, entity: T, handler: CRUDEventHandler.Before<InstanceType<T>, InstanceType<T> | void | Error>): this
232
- before(eve: Events, entity: Target, handler: EventHandler): this
233
- before(eve: Events, handler: EventHandler): this
234
- after<T extends Constructable>(eve: Events, entity: T, handler: CRUDEventHandler.After<InstanceType<T>, InstanceType<T> | void | Error>): this
235
- after(eve: Events, entity: Target, handler: ResultsHandler): this
236
- after(eve: Events, handler: ResultsHandler): this
237
- reject(eves: Events, ...entity: Target[]): this
234
+ before<T extends Constructable>(eve: EventArg, entity: T, handler: CRUDEventHandler.Before<InstanceType<T>, InstanceType<T> | void | Error>): this
235
+ before(eve: EventArg, entity: Target, handler: EventHandler): this
236
+ before(eve: EventArg, handler: EventHandler): this
237
+ after<T extends Constructable>(eve: EventArg, entity: T, handler: CRUDEventHandler.After<InstanceType<T>, InstanceType<T> | void | Error>): this
238
+ after(eve: EventArg, entity: Target, handler: ResultsHandler): this
239
+ after(eve: EventArg, handler: ResultsHandler): this
240
+ reject(eves: EventArg, ...entity: Target[]): this
238
241
  }
239
242
 
240
243
  export interface Transaction extends Service {
@@ -242,6 +245,9 @@ export interface Transaction extends Service {
242
245
  rollback(): Promise<void>
243
246
  }
244
247
 
248
+ export class ApplicationService extends Service {}
249
+ export class MessagingService extends Service {}
250
+ export class RemoteService extends Service {}
245
251
  export class DatabaseService extends Service {
246
252
  deploy(model?: csn | string): Promise<csn>
247
253
  begin(): Promise<void>
@@ -251,26 +257,29 @@ export class DatabaseService extends Service {
251
257
 
252
258
  export interface ResultSet extends Array<{}> {}
253
259
 
254
- export class cds {
260
+ declare class cds {
255
261
  /**
256
- * @see [capire docs](https://cap.cloud.sap/docs/node.js/services)
262
+ * @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
257
263
  */
258
264
  Service: typeof Service
265
+ Request: typeof Request
266
+ Event: typeof Event
267
+ EventContext: typeof EventContext
259
268
 
260
269
  /**
261
270
  * @see [capire docs](https://cap.cloud.sap/docs/node.js/app-services)
262
271
  */
263
- ApplicationService: typeof Service
272
+ ApplicationService: typeof ApplicationService
264
273
 
265
274
  /**
266
275
  * @see [capire docs](https://cap.cloud.sap/docs/node.js/remote-services)
267
276
  */
268
- RemoteService: typeof Service
277
+ RemoteService: typeof RemoteService
269
278
 
270
279
  /**
271
280
  * @see [capire docs](https://cap.cloud.sap/docs/node.js/messaging)
272
281
  */
273
- MessagingService: typeof Service
282
+ MessagingService: typeof MessagingService
274
283
 
275
284
  /**
276
285
  * @see [capire docs](https://cap.cloud.sap/docs/node.js/databases)
@@ -305,7 +314,7 @@ type OneOrMany<T> = T | T[];
305
314
 
306
315
  type TypedRequest<T> = Omit<Request, 'data'> & { data: T }
307
316
 
308
- // https://cap.cloud.sap/docs/node.js/services#event-handlers
317
+ // https://cap.cloud.sap/docs/node.js/core-services#srv-on-before-after
309
318
  export namespace CRUDEventHandler {
310
319
  type Before<P,R> = (req: TypedRequest<P>) => Promise<R> | R
311
320
  type On<P,R> = (req: TypedRequest<P>, next: (...args: any) => Promise<R> | R) => Promise<R> | R
@@ -336,7 +345,7 @@ interface ResultsHandler {
336
345
  * Represents the invocation context of incoming request and event messages.
337
346
  * @see [capire docs](https://cap.cloud.sap/docs/node.js/requests)
338
347
  */
339
- interface EventContext {
348
+ export class EventContext {
340
349
  timestamp: Date
341
350
  locale: string
342
351
  id: string
@@ -347,7 +356,7 @@ interface EventContext {
347
356
  /**
348
357
  * @see [capire docs](https://cap.cloud.sap/docs/node.js/requests)
349
358
  */
350
- interface EventMessage extends EventContext {
359
+ export class Event extends EventContext {
351
360
  event: string
352
361
  data: any
353
362
  headers: {}
@@ -379,7 +388,7 @@ interface Options {
379
388
  /**
380
389
  * @see [capire docs](https://cap.cloud.sap/docs/node.js/requests)
381
390
  */
382
- interface Request extends EventMessage {
391
+ export class Request extends Event {
383
392
  params: (string | {})[]
384
393
  method: string
385
394
  path: string
@@ -414,8 +423,10 @@ interface Request extends EventMessage {
414
423
  reject(message: { code?: number | string; message: string; target?: string; args?: {}, status?: number }): Error
415
424
  }
416
425
 
417
- type Events = Event | Event[]
418
- type Event = (CRUD | TX | HTTP | DRAFT) | (CustomOp & {})
426
+ export default cds
427
+
428
+ type EventArg = EventName | EventName[]
429
+ type EventName = (CRUD | TX | HTTP | DRAFT) | (CustomOp & {})
419
430
  type CRUD = 'CREATE' | 'READ' | 'UPDATE' | 'DELETE'
420
431
  type DRAFT = 'NEW' | 'EDIT' | 'PATCH' | 'SAVE'
421
432
  type HTTP = 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE'
package/apis/test.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { AxiosInstance } from 'axios';
2
2
  import chai from 'chai';
3
- import * as main_cds from './cds';
4
3
  import * as http from 'http';
5
4
  import { Service } from './services';
6
5
 
@@ -37,7 +36,7 @@ declare class Test extends Axios {
37
36
  get expect(): typeof chai.expect;
38
37
  get assert(): typeof chai.assert;
39
38
  get data(): DataUtil;
40
- get cds(): typeof main_cds;
39
+ get cds(): typeof import('./cds').default;
41
40
 
42
41
  then(r: (args: { server: http.Server, url: string }) => void): void;
43
42
 
package/bin/serve.js CHANGED
@@ -111,7 +111,7 @@ module.exports = Object.assign ( serve, {
111
111
  database, if any, and only adds an in-memory database if no
112
112
  persistent one is configured.
113
113
 
114
- Requires an sqlite driver to be installed. For example: _npm i sqlite3_.
114
+ Requires an SQLite driver to be installed. For example: _npm i @cap-js/sqlite_.
115
115
 
116
116
  # EXAMPLES
117
117
 
@@ -168,7 +168,7 @@ async function serve (all=[], o={}) {
168
168
  const cds_server = await _local_server_js() || cds.server
169
169
  if (!o.silent) _prepare_logging ()
170
170
 
171
- // The following things are meant for dev mode, which can be overruled by feature flagse...
171
+ // The following things are meant for dev mode, which can be overruled by feature flags...
172
172
  const {features} = cds.env
173
173
  {
174
174
  // handle --with-mocks resp. --mocked
@@ -206,8 +206,8 @@ async function serve (all=[], o={}) {
206
206
 
207
207
  const LOG = cds.log('cli|server')
208
208
  cds.shutdown = _shutdown //> for programmatic invocation
209
- process.on('unhandledRejection', e => _shutdown (e, cds.log('cds').error('❗️Uncaught',e)))
210
- process.on('uncaughtException', e => _shutdown (e, cds.log('cds').error('❗️Uncaught',e)))
209
+ process.on('unhandledRejection', e => _shutdown (e, cds.log().error('❗️Uncaught',e))) //> using std logger to have it labelled with [cds] - instead of [cli] -
210
+ process.on('uncaughtException', e => _shutdown (e, cds.log().error('❗️Uncaught',e))) //> using std logger to have it labelled with [cds] - instead of [cli] -
211
211
  process.on('SIGINT', cds.watched ? _shutdown : (s,n)=>_shutdown(s,n,console.log())) //> newline after ^C
212
212
  process.on('SIGHUP', _shutdown)
213
213
  process.on('SIGHUP2', _shutdown)
package/common.cds CHANGED
@@ -35,7 +35,7 @@ context sap.common {
35
35
  /**
36
36
  * Code list for languages
37
37
  *
38
- * See https://cap.cloud.sap/docs/cds/common#entity-sapcommonlanguages
38
+ * See https://cap.cloud.sap/docs/cds/common#entity-languages
39
39
  */
40
40
  entity Languages : CodeList {
41
41
  key code : Locale;
@@ -44,7 +44,7 @@ context sap.common {
44
44
  /**
45
45
  * Code list for countries
46
46
  *
47
- * See https://cap.cloud.sap/docs/cds/common#entity-sapcommoncountries
47
+ * See https://cap.cloud.sap/docs/cds/common#entity-countries
48
48
  */
49
49
  entity Countries : CodeList {
50
50
  key code : String(3) @(title : '{i18n>CountryCode}');
@@ -53,7 +53,7 @@ context sap.common {
53
53
  /**
54
54
  * Code list for currencies
55
55
  *
56
- * See https://cap.cloud.sap/docs/cds/common#entity-sapcommoncurrencies
56
+ * See https://cap.cloud.sap/docs/cds/common#entity-currencies
57
57
  */
58
58
  entity Currencies : CodeList {
59
59
  key code : String(3) @(title : '{i18n>CurrencyCode}');
@@ -64,7 +64,7 @@ context sap.common {
64
64
  /**
65
65
  * Aspect for a code list with name and description
66
66
  *
67
- * See https://cap.cloud.sap/docs/cds/common#aspect-sapcommoncodelist
67
+ * See https://cap.cloud.sap/docs/cds/common#aspect-codelist
68
68
  */
69
69
  aspect CodeList @(
70
70
  cds.autoexpose,
@@ -12,7 +12,7 @@ module.exports = function basic_auth (options) {
12
12
  // get basic authorization header
13
13
  let auth = req.headers.authorization
14
14
  // enforce login if requested
15
- if (!auth || !auth.match(/^basic/i)) return login_required ? req._login('Logged in user required!') : next()
15
+ if (!auth || !auth.match(/^basic/i)) return login_required ? req._login('Logged in user required!') : req.user = new cds.User.default, next()
16
16
  // decode user credentials from autorization header
17
17
  let [id,pwd] = Buffer.from(auth.slice(6),'base64').toString().split(':')
18
18
  // verify user credentials and set req.user
@@ -1,4 +1,5 @@
1
- const { User: { privileged }} = require ('../index')
1
+ const { User: { privileged } } = require ('../index')
2
+
2
3
  module.exports = function dummy_auth() {
3
4
  return function dummy_auth (req, res, next) {
4
5
  req.user = privileged
@@ -1,2 +1,68 @@
1
- module.exports = require('../../libx/_runtime/auth/strategies/ias-auth')
2
- // TODO: could move that implementation over here and link from libx/_runtime
1
+ const cds = require('../')
2
+ const LOG = cds.log('auth')
3
+
4
+ // _require for better error message
5
+ const _require = require('../../libx/_runtime/common/utils/require')
6
+ const express = _require('express')
7
+ const passport = _require('passport')
8
+ const { JWTStrategy } = _require('@sap/xssec')
9
+
10
+ const RESERVED_ATTRIBUTES = new Set(['aud', 'azp', 'exp', 'ext_attr', 'iat', 'ias_iss', 'iss', 'jti', 'sub', 'user_uuid', 'zone_uuid', 'zid'])
11
+
12
+ module.exports = function ias_auth(config) {
13
+ if (!config.credentials) {
14
+ let msg = `Authentication kind "${config.kind}" configured, but no IAS instance bound to application.`
15
+ msg += ' Either bind an IAS instance, or switch to an authentication kind that does not require a binding.'
16
+ throw new Error(msg)
17
+ }
18
+
19
+ passport.use('IAS', new JWTStrategy(config.credentials))
20
+
21
+ return express
22
+ .Router()
23
+ .use((req, res, next) => {
24
+ // callback needed in order to suppress 401 and continue with anonymous to allow @requires: 'any'/ restrict_all_services=false
25
+ const callback = (err, user, info, _status) => {
26
+ if (err) return next(err)
27
+
28
+ if (user) {
29
+ req.user = user
30
+ req.authInfo = info
31
+ } else {
32
+ req.user = new cds.User.default
33
+ if (LOG._debug && req.tokenInfo)
34
+ LOG.debug('Error during token validation:', req.tokenInfo.getErrorObject().message)
35
+ }
36
+
37
+ next()
38
+ }
39
+ passport.authenticate('IAS', { session: false }, callback)(req, res, next)
40
+ })
41
+ .use((req, res, next) => {
42
+ if (!('authInfo' in req)) return next()
43
+
44
+ if (req.tokenInfo.getClientId() === req.tokenInfo.getSubject()) //> grant_type === client_credentials or x509
45
+ req.user = new cds.User({
46
+ id: 'system',
47
+ roles: ['authenticated-user', 'system-user'],
48
+ attr: {}
49
+ })
50
+ else {
51
+ // add all unknown attributes to req.user.attr in order to keep public API small
52
+ const payload = req.tokenInfo.getPayload()
53
+ const attributes = Object.keys(payload)
54
+ .filter(k => !RESERVED_ATTRIBUTES.has(k))
55
+ .reduce((attrs, k) => { attrs[k] = payload[k]; return attrs }, {})
56
+
57
+ req.user = new cds.User({
58
+ id: req.user.id,
59
+ roles: ['authenticated-user'],
60
+ attr: attributes
61
+ })
62
+ }
63
+
64
+ req.tenant = req.tokenInfo.getZoneId()
65
+
66
+ next()
67
+ })
68
+ }
package/lib/auth/index.js CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  const cds = require ('../index'), { path, local } = cds.utils
3
2
 
4
3
  const _require = require; require = cds.lazified (module) // eslint-disable-line no-global-assign
@@ -12,23 +11,24 @@ module.exports = Object.assign (auth_factory, {
12
11
  })
13
12
  require = _require // eslint-disable-line no-global-assign
14
13
 
15
-
16
14
  /**
17
15
  * Constructs one of the above middlewares as configured
18
16
  */
19
17
  function auth_factory (options) {
20
18
  const o = { ...options, ...cds.requires.auth }
21
19
  let kind = o.impl ? 'custom' : o.kind || o.strategy
22
- let middleware = cds.auth[kind]
20
+ let middleware = cds.auth[kind] || cds.auth[kind?.replace(/-auth$/, '')]
23
21
  if (middleware) {
24
- cds.log().info ('using auth strategy:', { kind }, '\n')
22
+ // REVISIT: here, kind may be misleading, e.g., "basic-auth" instead of "mocked" -> _kind workaround
23
+ const _kind = (o._kind || kind).replace(/-auth$/, '') //> official auth kinds are NOT postfixed with "-auth"
24
+ cds.log().info ('using authentication:', { kind: _kind }, '\n')
25
25
  } else {
26
26
  let impl = kind === 'custom' ? cds.resolve (o.impl)?.[0] : path.resolve (__dirname, kind)
27
27
  try { impl = require.resolve (impl) } catch {
28
28
  const e = o.impl ? `Cannot find custom impl at: ${o.impl}` : `Cannot find unknown auth kind: ${o.kind}`
29
29
  throw cds.error(e)
30
30
  }
31
- cds.log().info ('using auth strategy:', { kind, impl: local(impl) }, '\n')
31
+ cds.log().info ('using authentication:', { kind, impl: local(impl) }, '\n')
32
32
  middleware = require(impl)
33
33
  }
34
34
  if ((typeof middleware === 'function' && middleware.length === 3) || Array.isArray(middleware)) {
@@ -1,41 +1,64 @@
1
1
  const cds = require('../')
2
- const _require = require('../../libx/_runtime/common/utils/require')
2
+ const LOG = cds.log('auth')
3
+
3
4
  // _require for better error message
5
+ const _require = require('../../libx/_runtime/common/utils/require')
4
6
  const express = _require('express')
5
7
  const passport = _require('passport')
6
8
  const { JWTStrategy } = _require('@sap/xssec')
7
- const LOG = cds.log('auth')
8
9
 
9
10
  module.exports = function jwt_auth(config) {
10
- // warn if no credentials
11
11
  if (!config.credentials) {
12
- LOG._warn &&
13
- LOG.warn(`Authentication kind "${config.kind}" configured, but no XSUAA instance bound to application.
14
- This is NOT recommended in production!`)
15
- return (req,res,next) => next()
12
+ let msg = `Authentication kind "${config.kind}" configured, but no XSUAA instance bound to application.`
13
+ msg += ' Either bind an IAS instance, or switch to an authentication kind that does not require a binding.'
14
+ throw new Error(msg)
16
15
  }
17
16
 
18
- passport.use(config.kind, new JWTStrategy(config.credentials))
17
+ passport.use('JWT', new JWTStrategy(config.credentials))
18
+
19
19
  return express
20
20
  .Router()
21
- .use(passport.authenticate(config.kind, { session: false }))
22
21
  .use((req, res, next) => {
22
+ // callback needed in order to suppress 401 and continue with anonymous to allow @requires: 'any'/ restrict_all_services=false
23
+ const callback = (err, user, info, _status) => {
24
+ if (err) return next(err)
25
+
26
+ if (user) {
27
+ req.user = user
28
+ req.authInfo = info
29
+ } else {
30
+ req.user = new cds.User.default
31
+ if (LOG._debug && req.tokenInfo)
32
+ LOG.debug('Error during token validation:', req.tokenInfo.getErrorObject().message)
33
+ }
34
+
35
+ next()
36
+ }
37
+ passport.authenticate('JWT', { session: false }, callback)(req, res, next)
38
+ })
39
+ .use((req, res, next) => {
40
+ if (!('authInfo' in req)) return next()
41
+
23
42
  const payload = req.tokenInfo.getPayload()
24
43
 
25
44
  let id = req.user.id
26
- let _is_system, _is_internal
27
45
 
28
- let roles = payload.scope.map(s => s.replace(new RegExp(`^(${config.credentials.xsappname + '.'})`), ''))
46
+ // Roles = scope names w/o xsappname
47
+ let xsappname = new RegExp(`^${config.credentials.xsappname}\\.`)
48
+ let roles = payload.scope.map(s => s.replace(xsappname, ''))
49
+
50
+ // Disallow setting system roles from external
51
+ roles = roles.filter(r => !(r in { 'internal-user': 1, 'system-user': 1 }))
52
+
29
53
  roles.push('identified-user')
30
54
  if (payload.grant_type) {
31
55
  // > not "weak"
32
56
  roles.push('authenticated-user')
33
57
 
34
- const CLIENT = { client_credentials: 1, client_x509: 1 }
35
- if (payload.grant_type in CLIENT) {
58
+ if (payload.grant_type in { client_credentials: 1, client_x509: 1 }) {
36
59
  id = 'system'
37
- _is_system = true
38
- if (req.tokenInfo.getClientId() === config.credentials.clientid) _is_internal = true
60
+ roles.push('system-user')
61
+ if (req.tokenInfo.getClientId() === config.credentials.clientid) roles.push('internal-user')
39
62
  }
40
63
  }
41
64
 
@@ -47,16 +70,9 @@ module.exports = function jwt_auth(config) {
47
70
  attr.email = req.authInfo.getEmail()
48
71
  }
49
72
 
50
- req.user = new cds.User({ id, roles, attr, _is_system, _is_internal })
73
+ req.user = new cds.User({ id, roles, attr })
51
74
  req.tenant = req.tokenInfo.getZoneId?.()
75
+
52
76
  next()
53
77
  })
54
- .use((err, req, res, _next) => {
55
- if (req.tokenInfo) {
56
- LOG?.debug('error during token validation', req.tokenInfo.getErrorObject())
57
- }
58
- // REVISIT: reject request immediately as our other auth strategies do
59
- // should we call next(err)? -> I don't think so; it's not an error, is it?
60
- res.status(401).json({ code: '401', message: 'Unauthorized' }) // REVISIT: this is OData style?
61
- })
62
78
  }