@sap/cds 7.4.2 → 7.5.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.
- package/CHANGELOG.md +94 -0
- package/apis/cds.d.ts +1 -38
- package/apis/core.d.ts +21 -101
- package/apis/cqn.d.ts +18 -76
- package/apis/csn.d.ts +18 -114
- package/apis/events.d.ts +16 -123
- package/apis/internal/inference.d.ts +18 -32
- package/apis/linked.d.ts +18 -97
- package/apis/log.d.ts +19 -164
- package/apis/models.d.ts +18 -180
- package/apis/ql.d.ts +16 -323
- package/apis/reflect.d.ts +32 -0
- package/apis/server.d.ts +18 -135
- package/apis/services.d.ts +18 -380
- package/bin/cds-serve.js +5 -2
- package/bin/serve.js +7 -16
- package/lib/auth/basic-auth.js +3 -1
- package/lib/auth/ias-auth.js +62 -48
- package/lib/auth/ias-claims.js +34 -0
- package/lib/auth/index.js +54 -33
- package/lib/auth/jwt-auth.js +55 -52
- package/lib/compile/cdsc.js +2 -2
- package/lib/compile/to/edm.js +4 -4
- package/lib/compile/to/hdbtabledata.js +5 -8
- package/lib/compile/to/srvinfo.js +2 -2
- package/lib/env/cds-env.js +3 -9
- package/lib/env/cds-requires.js +16 -17
- package/lib/env/compat.js +0 -9
- package/lib/env/defaults.js +17 -6
- package/lib/i18n/localize.js +46 -42
- package/lib/index.js +6 -8
- package/lib/linked/classes.js +7 -118
- package/lib/linked/entities.js +1 -1
- package/lib/log/cds-log.js +15 -10
- package/lib/log/format/aspects/als.js +41 -0
- package/lib/log/format/aspects/cf.js +36 -0
- package/lib/log/format/json.js +96 -0
- package/lib/plugins.js +7 -3
- package/lib/req/context.js +4 -2
- package/lib/srv/cds-connect.js +3 -5
- package/lib/srv/cds-serve.js +13 -26
- package/lib/srv/factory.js +3 -3
- package/lib/srv/middlewares/index.js +0 -2
- package/lib/srv/middlewares/trace.js +2 -3
- package/lib/srv/protocols/_legacy.js +27 -30
- package/lib/srv/protocols/index.js +173 -58
- package/lib/srv/protocols/odata-v4.js +29 -16
- package/lib/srv/srv-api.js +8 -13
- package/lib/srv/srv-handlers.js +14 -14
- package/lib/utils/cds-utils.js +15 -0
- package/libx/_runtime/auth/index.js +4 -5
- package/libx/_runtime/auth/strategies/basic.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +23 -13
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +6 -15
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +10 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +5 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +2 -1
- package/libx/_runtime/cds-services/services/utils/columns.js +3 -9
- package/libx/_runtime/cds.js +13 -0
- package/libx/_runtime/common/composition/data.js +3 -0
- package/libx/_runtime/common/composition/delete.js +1 -1
- package/libx/_runtime/common/error/frontend.js +2 -2
- package/libx/_runtime/common/generic/auth/readOnly.js +1 -1
- package/libx/_runtime/common/generic/auth/restrictions.js +1 -1
- package/libx/_runtime/common/generic/sorting.js +4 -5
- package/libx/_runtime/common/utils/csn.js +23 -18
- package/libx/_runtime/common/utils/restrictions.js +6 -15
- package/libx/_runtime/db/generic/input.js +3 -2
- package/libx/_runtime/fiori/generic/readOverDraft.js +2 -5
- package/libx/_runtime/fiori/lean-draft.js +69 -5
- package/libx/_runtime/hana/Service.js +1 -1
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +1 -1
- package/libx/_runtime/messaging/Outbox.js +3 -8
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -1
- package/libx/_runtime/messaging/file-based.js +1 -1
- package/libx/_runtime/messaging/service.js +7 -10
- package/libx/_runtime/remote/Service.js +15 -45
- package/libx/_runtime/remote/utils/client.js +20 -33
- package/libx/_runtime/remote/utils/cloudSdkProvider.js +30 -0
- package/libx/_runtime/sqlite/Service.js +2 -2
- package/libx/odata/afterburner.js +29 -21
- package/libx/odata/cqn2odata.js +1 -1
- package/libx/odata/error.js +7 -0
- package/libx/odata/grammar.peggy +16 -20
- package/libx/odata/metadata.js +73 -78
- package/libx/odata/parser.js +1 -1
- package/libx/odata/read.js +94 -0
- package/libx/odata/result.js +91 -0
- package/libx/odata/service-document.js +31 -37
- package/libx/odata/utils.js +2 -1
- package/libx/outbox/index.js +9 -4
- package/libx/rest/RestAdapter.js +68 -67
- package/libx/rest/middleware/create.js +20 -26
- package/libx/rest/middleware/delete.js +5 -3
- package/libx/rest/middleware/error.js +2 -3
- package/libx/rest/middleware/input.js +5 -5
- package/libx/rest/middleware/operation.js +96 -41
- package/libx/rest/middleware/parse.js +4 -6
- package/libx/rest/middleware/payload.js +5 -5
- package/libx/rest/middleware/read.js +11 -17
- package/libx/rest/middleware/update.js +20 -25
- package/package.json +2 -1
- package/server.js +7 -4
- package/srv/outbox.cds +9 -10
- package/apis/env.d.ts +0 -25
- package/apis/test.d.ts +0 -81
- package/apis/utils.d.ts +0 -15
- package/lib/auth/passport-basic.js +0 -14
- package/lib/auth/passport-digest.js +0 -16
- package/lib/env/presets.js +0 -35
- package/lib/log/format/cf.js +0 -16
- package/lib/log/format/kibana.js +0 -92
- package/lib/srv/middlewares/ctx-auth.js +0 -11
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +0 -119
package/apis/services.d.ts
CHANGED
|
@@ -1,384 +1,22 @@
|
|
|
1
|
-
import { SELECT, INSERT, UPDATE, DELETE, Query, ConstructedQuery, UPSERT } from './ql'
|
|
2
|
-
import { Awaitable } from './ql'
|
|
3
|
-
import { ArrayConstructable, Constructable } from './internal/inference'
|
|
4
|
-
import { LinkedCSN, LinkedDefinition, Definitions, LinkedEntity } from './linked'
|
|
5
|
-
import { CSN } from './csn'
|
|
6
|
-
import { EventContext } from './events'
|
|
7
|
-
import { Request } from './events'
|
|
8
|
-
|
|
9
|
-
export class QueryAPI {
|
|
10
|
-
|
|
11
|
-
entities : LinkedCSN['entities']
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
15
|
-
*/
|
|
16
|
-
read: {
|
|
17
|
-
<T extends ArrayConstructable<any>>(entity: T, key?: any): Awaitable<SELECT<T>, InstanceType<T>>
|
|
18
|
-
<T>(entity: LinkedDefinition | string, key?: any): SELECT<T>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
23
|
-
*/
|
|
24
|
-
create: {
|
|
25
|
-
<T extends ArrayConstructable<any>>(entity: T, key?: any): INSERT<T>
|
|
26
|
-
<T>(entity: LinkedDefinition | string, key?: any): INSERT<T>
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
31
|
-
*/
|
|
32
|
-
insert: {
|
|
33
|
-
<T extends ArrayConstructable<any>>(data: T): INSERT<T>
|
|
34
|
-
<T>(data: object | object[]): INSERT<T>
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
39
|
-
*/
|
|
40
|
-
upsert: {
|
|
41
|
-
<T extends ArrayConstructable<any>>(data: T): UPSERT<T>
|
|
42
|
-
<T>(data: object | object[]): UPSERT<T>
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
47
|
-
*/
|
|
48
|
-
update: {
|
|
49
|
-
<T extends ArrayConstructable<any>>(entity: T, key?: any): UPDATE<T>
|
|
50
|
-
<T>(entity: LinkedDefinition | string, key?: any): UPDATE<T>
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
55
|
-
*/
|
|
56
|
-
run: {
|
|
57
|
-
(query: ConstructedQuery | ConstructedQuery[]): Promise<ResultSet | any>
|
|
58
|
-
(query: Query): Promise<ResultSet | any>
|
|
59
|
-
(query: string, args?: any[] | object): Promise<ResultSet | any>
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#crud-style-api)
|
|
64
|
-
*/
|
|
65
|
-
delete<T>(entity: LinkedDefinition | string, key?: any): DELETE<T>
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#srv-foreach-entity)
|
|
69
|
-
*/
|
|
70
|
-
foreach(query: Query, callback: (row: object) => void): this
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/core-services#srv-stream-column)
|
|
74
|
-
*/
|
|
75
|
-
stream: {
|
|
76
|
-
(column: string): {
|
|
77
|
-
from(entity: LinkedDefinition | string): {
|
|
78
|
-
where(filter: any): ReadableStream
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
(query: Query): Promise<ReadableStream>
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Starts or joins a transaction
|
|
86
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx)
|
|
87
|
-
*/
|
|
88
|
-
|
|
89
|
-
tx: {
|
|
90
|
-
(fn: (tx: Transaction) => {}): Promise<unknown>
|
|
91
|
-
(context?: object): Transaction
|
|
92
|
-
(context: object, fn: (tx: Transaction) => {}): Promise<unknown>
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
transaction: {
|
|
96
|
-
(fn: (tx: Transaction) => {}): Promise<unknown>
|
|
97
|
-
(context?: object): Transaction
|
|
98
|
-
(context: object, fn: (tx: Transaction) => {}): Promise<unknown>
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#cds-spawn)
|
|
103
|
-
*/
|
|
104
|
-
spawn(options: {
|
|
105
|
-
[key: string]: any
|
|
106
|
-
every?: number
|
|
107
|
-
after?: number
|
|
108
|
-
}, fn: (tx: Transaction) => {}): SpawnEventEmitter
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* @see [docs](https://cap.cloud.sap/docs/node.js/cds-tx#event-contexts
|
|
112
|
-
*/
|
|
113
|
-
context?: EventContext
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
1
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
2
|
+
* DO NO LONGER IMPORT THIS FILE
|
|
3
|
+
*
|
|
4
|
+
* Instead use:
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```
|
|
8
|
+
* import * as cds from '@sap/cds'
|
|
9
|
+
* cds.Request
|
|
10
|
+
* or
|
|
11
|
+
* import { Request } from '@sap/cds'
|
|
12
|
+
* or
|
|
13
|
+
* @type { import('@sap/cds').Request }
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @deprecated
|
|
120
17
|
*/
|
|
121
|
-
export
|
|
122
|
-
constructor(
|
|
123
|
-
name?: string,
|
|
124
|
-
model?: CSN,
|
|
125
|
-
options?: {
|
|
126
|
-
kind: string
|
|
127
|
-
impl: string | ServiceImpl
|
|
128
|
-
}
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* The name of the service
|
|
133
|
-
*/
|
|
134
|
-
name: string
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* The model from which the service's definition was loaded
|
|
138
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
139
|
-
*/
|
|
140
|
-
model: LinkedCSN
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Provides access to the entities exposed by a service
|
|
144
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
145
|
-
*/
|
|
146
|
-
entities: Definitions & ((namespace: string) => Definitions)
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Provides access to the events declared by a service
|
|
150
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
151
|
-
*/
|
|
152
|
-
events: Definitions & ((namespace: string) => Definitions)
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Provides access to the types exposed by a service
|
|
156
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
157
|
-
*/
|
|
158
|
-
types: Definitions & ((namespace: string) => Definitions)
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Provides access to the operations, i.e. actions and functions, exposed by a service
|
|
162
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
163
|
-
*/
|
|
164
|
-
operations: Definitions & ((namespace: string) => Definitions)
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Acts like a parameter-less constructor. Ensure to call `await super.init()` to have the base class’s handlers added.
|
|
168
|
-
* You may register own handlers before the base class’s ones, to intercept requests before the default handlers snap in.
|
|
169
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#srv-init)
|
|
170
|
-
*/
|
|
171
|
-
init(): Promise<void>
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Constructs and emits an asynchronous event.
|
|
175
|
-
* @see [capire docs](https://cap.cloud.sap/docs/core-services#srv-emit-event)
|
|
176
|
-
*/
|
|
177
|
-
emit: {
|
|
178
|
-
<T = any>(details: { event: types.event; data?: object; headers?: object }): Promise<T>
|
|
179
|
-
<T = any>(event: types.event, data?: object, headers?: object): Promise<T>
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Constructs and sends a synchronous request.
|
|
184
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#srv-send-request)
|
|
185
|
-
*/
|
|
186
|
-
send: {
|
|
187
|
-
<T = any>(event: types.event, path: string, data?: object, headers?: object): Promise<T>
|
|
188
|
-
<T = any>(event: types.event, data?: object, headers?: object): Promise<T>
|
|
189
|
-
<T = any>(details: { event: types.event; data?: object; headers?: object }): Promise<T>
|
|
190
|
-
<T = any>(details: { query: ConstructedQuery; data?: object; headers?: object }): Promise<T>
|
|
191
|
-
<T = any>(details: { method: types.eventName; path: string; data?: object; headers?: object }): Promise<T>
|
|
192
|
-
<T = any>(details: { event: types.eventName; entity: LinkedDefinition | string; data?: object; params?: object }): Promise<T>
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Constructs and sends a GET request.
|
|
197
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
|
|
198
|
-
*/
|
|
199
|
-
get<T = any>(entityOrPath: types.target, data?: object): Promise<T>
|
|
200
|
-
/**
|
|
201
|
-
* Constructs and sends a POST request.
|
|
202
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
|
|
203
|
-
*/
|
|
204
|
-
post<T = any>(entityOrPath: types.target, data?: object): Promise<T>
|
|
205
|
-
/**
|
|
206
|
-
* Constructs and sends a PUT request.
|
|
207
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
|
|
208
|
-
*/
|
|
209
|
-
put<T = any>(entityOrPath: types.target, data?: object): Promise<T>
|
|
210
|
-
/**
|
|
211
|
-
* Constructs and sends a PATCH request.
|
|
212
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services#rest-style-api)
|
|
213
|
-
*/
|
|
214
|
-
patch<T = any>(entityOrPath: types.target, data?: object): Promise<T>
|
|
215
|
-
/**
|
|
216
|
-
* Constructs and sends a DELETE request.
|
|
217
|
-
*/
|
|
218
|
-
delete: {
|
|
219
|
-
<T = any>(entityOrPath: types.target, data?: object): DELETE<T>
|
|
220
|
-
<T extends ArrayConstructable<any>>(entity: T, key?: any): DELETE<T>
|
|
221
|
-
<T>(entity: LinkedDefinition | string, key?: any): DELETE<T>
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// The central method to dispatch events
|
|
225
|
-
dispatch(msg: types.event): Promise<any>
|
|
226
|
-
|
|
227
|
-
// Provider API
|
|
228
|
-
prepend(fn: ServiceImpl): Promise<this>
|
|
229
|
-
on<T extends Constructable>(eve: types.event, entity: T, handler: CRUDEventHandler.On<InstanceType<T>, InstanceType<T> | void | Error>): this
|
|
230
|
-
on<F extends CdsFunction>(boundAction: F, service: string, handler: ActionEventHandler<F['__parameters'], void | Error | F['__returns']>): this
|
|
231
|
-
on<F extends CdsFunction>(unboundAction: F, handler: ActionEventHandler<F['__parameters'], void | Error | F['__returns']>): this
|
|
232
|
-
on(eve: types.event, entity: types.target, handler: OnEventHandler): this
|
|
233
|
-
on(eve: types.event, handler: OnEventHandler): this
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// onSucceeded (eve: types.Events, entity: types.Target, handler: types.EventHandler): this
|
|
237
|
-
// onSucceeded (eve: types.Events, handler: types.EventHandler): this
|
|
238
|
-
// onFailed (eve: types.Events, entity: types.Target, handler: types.EventHandler): this
|
|
239
|
-
// onFailed (eve: types.Events, handler: types.EventHandler): this
|
|
240
|
-
before<T extends Constructable>(eve: types.event, entity: T, handler: CRUDEventHandler.Before<InstanceType<T>, InstanceType<T> | void | Error>): this
|
|
241
|
-
before(eve: types.event, entity: types.target, handler: EventHandler): this
|
|
242
|
-
before(eve: types.event, handler: EventHandler): this
|
|
243
|
-
after<T extends Constructable>(eve: types.event, entity: T, handler: CRUDEventHandler.After<InstanceType<T>, InstanceType<T> | void | Error>): this
|
|
244
|
-
after(eve: types.event, entity: types.target, handler: ResultsHandler): this
|
|
245
|
-
after(eve: types.event, handler: ResultsHandler): this
|
|
246
|
-
reject(eves: types.event, ...entity: types.target[]): this
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
export class ApplicationService extends Service {}
|
|
250
|
-
export class MessagingService extends Service {}
|
|
251
|
-
export class RemoteService extends Service {}
|
|
252
|
-
export class DatabaseService extends Service {
|
|
253
|
-
deploy(model?: CSN | string): Promise<CSN>
|
|
254
|
-
begin(): Promise<void>
|
|
255
|
-
commit(): Promise<void>
|
|
256
|
-
rollback(): Promise<void>
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
export default class cds {
|
|
261
|
-
/**
|
|
262
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/core-services)
|
|
263
|
-
*/
|
|
264
|
-
Service: typeof Service
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/app-services)
|
|
268
|
-
*/
|
|
269
|
-
ApplicationService: typeof ApplicationService
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/remote-services)
|
|
273
|
-
*/
|
|
274
|
-
RemoteService: typeof RemoteService
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/messaging)
|
|
278
|
-
*/
|
|
279
|
-
MessagingService: typeof MessagingService
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* @see [capire docs](https://cap.cloud.sap/docs/node.js/databases)
|
|
283
|
-
*/
|
|
284
|
-
DatabaseService: typeof DatabaseService
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
interface Transaction extends Service {
|
|
289
|
-
commit(): Promise<void>
|
|
290
|
-
rollback(): Promise<void>
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
interface ResultSet extends Array<{}> {}
|
|
294
|
-
|
|
295
|
-
interface ServiceImpl {
|
|
296
|
-
(this: Service, srv: Service): any
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
interface EventHandler {
|
|
300
|
-
// (msg : types.EventMessage) : Promise<any> | any | void
|
|
301
|
-
(req: Request): Promise<any> | any | void
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
interface OnEventHandler {
|
|
305
|
-
(req: Request, next: Function): Promise<any> | any | void
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// `Partial` wraps any type and allows all properties to be undefined
|
|
309
|
-
// in addition to their actual type.
|
|
310
|
-
// This is important in the context of CRUD events, where
|
|
311
|
-
// entities could be lacking any properties, like a non-existing ID
|
|
312
|
-
// or when DB fields are corrupted, etc.
|
|
313
|
-
type Partial<T> = { [Key in keyof T]: undefined | T[Key] }
|
|
314
|
-
|
|
315
|
-
// Naming the first parameter in a handler `each` has special voodoo
|
|
316
|
-
// semantic which makes it impossible for us to infer the exact behaviour on a type level.
|
|
317
|
-
// So we always have to expect scalars as well as arrays in some callbacks.
|
|
318
|
-
type OneOrMany<T> = T | T[];
|
|
319
|
-
|
|
320
|
-
// functions generated by cds-typer explicitly carry types for
|
|
321
|
-
// parameters and return type, as their names are not accessible from
|
|
322
|
-
// function signatures to the type system.
|
|
323
|
-
// This meta information is required in .on action handlers.
|
|
324
|
-
type CdsFunction = {
|
|
325
|
-
(...args: any[]): any,
|
|
326
|
-
__parameters: object,
|
|
327
|
-
__returns: unknown
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
type TypedRequest<T> = Omit<Request, 'data'> & { data: T }
|
|
331
|
-
|
|
332
|
-
// https://cap.cloud.sap/docs/node.js/core-services#srv-on-before-after
|
|
333
|
-
declare namespace CRUDEventHandler {
|
|
334
|
-
type Before<P,R> = (req: TypedRequest<P>) => Promise<R> | R
|
|
335
|
-
type On<P,R> = (req: TypedRequest<P>, next: (...args: any) => Promise<R> | R) => Promise<R> | R
|
|
336
|
-
type After<P,R> = (data: undefined | P, req: TypedRequest<P>) => Promise<R> | R
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Handlers for actions try to infer the passed .data property
|
|
340
|
-
// as strictly as possible and therefore have to remove
|
|
341
|
-
// { data: any } (inherited EventMessage} with a more restricted
|
|
342
|
-
// type, based on the parameters of the action.
|
|
343
|
-
interface ActionEventHandler<P,R> {
|
|
344
|
-
(req: Omit<Request, 'data'> & { data: P }, next: Function): Promise<R> | R
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Note: the behaviour of ResultsHandler changes based on the name of the parameter.
|
|
348
|
-
// If the parameter in the hook is called "each", it is called once for each row in the result,
|
|
349
|
-
// otherwise it gets called exactly one time with the entire result.
|
|
350
|
-
// This runtime behaviour can not be described on type level
|
|
351
|
-
// (in a way that would benefit the user).
|
|
352
|
-
// The user will therefore receive "any" as their result/ each. If we could some day differentiate,
|
|
353
|
-
// we may want to add a generic to ResultsHandler which is passed from the EventHandlers down below.
|
|
354
|
-
interface ResultsHandler {
|
|
355
|
-
(results: any[], req: Request): void
|
|
356
|
-
(each: any, req: Request): void
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
interface SpawnEvents {
|
|
360
|
-
'succeeded': (res: any) => void
|
|
361
|
-
'failed': (error: any) => void
|
|
362
|
-
'done': () => void
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
declare class SpawnEventEmitter {
|
|
366
|
-
on<U extends keyof SpawnEvents>(
|
|
367
|
-
event: U, listener: SpawnEvents[U]
|
|
368
|
-
): this;
|
|
18
|
+
export * from '@cap-js/cds-types/apis/services'
|
|
369
19
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
): boolean;
|
|
373
|
-
timer: any
|
|
374
|
-
}
|
|
20
|
+
// this got moved to 'events' in 7.4
|
|
21
|
+
export { Request } from '@cap-js/cds-types/apis/events'
|
|
375
22
|
|
|
376
|
-
declare namespace types {
|
|
377
|
-
type event = eventName | eventName[]
|
|
378
|
-
type eventName = (string & {})
|
|
379
|
-
| 'CREATE' | 'READ' | 'UPDATE' | 'DELETE'
|
|
380
|
-
| 'NEW' | 'EDIT' | 'PATCH' | 'SAVE'
|
|
381
|
-
| 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE'
|
|
382
|
-
| 'COMMIT' | 'ROLLBACK'
|
|
383
|
-
type target = string | LinkedDefinition | LinkedEntity | (string | LinkedDefinition | LinkedEntity)[] | ArrayConstructable<any>
|
|
384
|
-
}
|
package/bin/cds-serve.js
CHANGED
|
@@ -31,11 +31,14 @@ const cli = {
|
|
|
31
31
|
else if (_global.test(a)) add_global(a, _flags[a] || argv[++i])
|
|
32
32
|
else throw 'Invalid option: '+ a
|
|
33
33
|
}
|
|
34
|
+
// consistent production setting for NODE_ENV and CDS_ENV
|
|
35
|
+
if (process.env.NODE_ENV !== 'production') process.env.NODE_ENV = process.env.CDS_ENV?.split(',').find(p => p === 'production') || process.env.NODE_ENV
|
|
36
|
+
else process.env.CDS_ENV = Array.from(new Set([...process.env.CDS_ENV?.split(',') ?? [], 'production']))
|
|
34
37
|
|
|
35
38
|
function add (k,v) { options[k.slice(2)] = v || true }
|
|
36
39
|
function add_global (k,v='') {
|
|
37
|
-
if (k === '--production') return process.env.
|
|
38
|
-
if (k === '--profile')
|
|
40
|
+
if (k === '--production') return process.env.CDS_ENV = Array.from(new Set([...process.env.CDS_ENV?.split(',') ?? [], 'production']))
|
|
41
|
+
if (k === '--profile') return process.env.CDS_ENV = Array.from(new Set([...process.env.CDS_ENV?.split(',') ?? [], ...v.split(',')]))
|
|
39
42
|
if (k === '--odata') v = { flavor:v }
|
|
40
43
|
let e=env || (env={}), path = k.slice(2).split('-')
|
|
41
44
|
while (path.length > 1) { let p = path.shift(); e = e[p]||(e[p]={}) }
|
package/bin/serve.js
CHANGED
|
@@ -127,7 +127,7 @@ module.exports = Object.assign ( serve, {
|
|
|
127
127
|
`})
|
|
128
128
|
|
|
129
129
|
|
|
130
|
-
const cds = require('../lib'), { exists, isfile, local, path } = cds.utils
|
|
130
|
+
const cds = require('../lib'), { exists, isfile, local, path, _redacted } = cds.utils
|
|
131
131
|
const COLORS = !!process.stdout.isTTY && !!process.stderr.isTTY && !process.env.NO_COLOR
|
|
132
132
|
|
|
133
133
|
// provisional loggers, see _prepare_logging
|
|
@@ -261,7 +261,12 @@ function _prepare_logging () { // NOSONAR
|
|
|
261
261
|
// print information when model is loaded
|
|
262
262
|
cds.on ('loaded', (model)=>{
|
|
263
263
|
LOG.info (`loaded model from ${model.$sources.length} file(s):\n${COLORS ? '\x1b[2m' : ''}`)
|
|
264
|
-
|
|
264
|
+
const limit = 30, shown = model.$sources.length === limit + 1 ? limit + 1 : limit // REVISIT: configurable limit?
|
|
265
|
+
for (let each of model.$sources.slice(0, shown)) console.log (' ', local(each))
|
|
266
|
+
if (model.$sources.length > shown) {
|
|
267
|
+
if (LOG._debug) for (let each of model.$sources.slice(shown)) console.log (' ', local(each))
|
|
268
|
+
else console.log (` ...${model.$sources.length-shown} more. Run with DEBUG=serve to show all files.`)
|
|
269
|
+
}
|
|
265
270
|
COLORS && console.log ('\x1b[0m')
|
|
266
271
|
})
|
|
267
272
|
|
|
@@ -337,20 +342,6 @@ function _with_mocks (o) {
|
|
|
337
342
|
}
|
|
338
343
|
}
|
|
339
344
|
|
|
340
|
-
const SECRETS = /(password)|(certificate)|(ca)|(secret)|(key)/i // 'certificate' and 'ca' on HANA
|
|
341
|
-
/** mascades password-like strings, also reducing clutter in output */
|
|
342
|
-
function _redacted(cred) {
|
|
343
|
-
if (!cred) return cred
|
|
344
|
-
if (Array.isArray(cred)) return cred.map(_redacted)
|
|
345
|
-
if (typeof cred === 'object') {
|
|
346
|
-
const newCred = Object.assign({}, cred)
|
|
347
|
-
Object.keys(newCred).forEach(k => (typeof newCred[k] === 'string' && SECRETS.test(k)) ? (newCred[k] = '...') : (newCred[k] = _redacted(newCred[k])))
|
|
348
|
-
return newCred
|
|
349
|
-
}
|
|
350
|
-
return cred
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
|
|
354
345
|
const _warn_if_cds_was_loaded_from_different_locations = ()=> global.__cds_loaded_from?.size > 1 && console.warn(`
|
|
355
346
|
-----------------------------------------------------------------------
|
|
356
347
|
WARNING: Package '@sap/cds' was loaded from different installations:`,
|
package/lib/auth/basic-auth.js
CHANGED
|
@@ -12,13 +12,15 @@ 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
|
|
15
|
+
if (!auth?.match(/^basic/i)) return login_required ? req._login('Logged-in user required!') : 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
|
|
19
19
|
let u = req.user = await users.verify (id, pwd)
|
|
20
20
|
// re-request login in case of wrong credentials
|
|
21
21
|
if (u.failed) return req._login (u)
|
|
22
|
+
// set req.tenant
|
|
23
|
+
if (u.tenant) req.tenant = u.tenant
|
|
22
24
|
// support for feature toggles via req.headers.features
|
|
23
25
|
if (req.headers.features) u = req.user = { ...u, features: req.headers.features } // NOTE: need to clone u
|
|
24
26
|
// done...
|
package/lib/auth/ias-auth.js
CHANGED
|
@@ -3,65 +3,79 @@ const LOG = cds.log('auth')
|
|
|
3
3
|
|
|
4
4
|
// _require for better error message
|
|
5
5
|
const _require = require('../../libx/_runtime/common/utils/require')
|
|
6
|
-
const
|
|
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'])
|
|
6
|
+
const xssec = _require('@sap/xssec')
|
|
11
7
|
|
|
12
8
|
module.exports = function ias_auth(config) {
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const { kind, credentials, known_claims } = config
|
|
10
|
+
|
|
11
|
+
if (!credentials) {
|
|
12
|
+
let msg = `Authentication kind "${kind}" configured, but no IAS instance bound to application.`
|
|
15
13
|
msg += ' Either bind an IAS instance, or switch to an authentication kind that does not require a binding.'
|
|
16
14
|
throw new Error(msg)
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
// cds.env.requires.auth.known_claims is not an official config!
|
|
18
|
+
const KNOWN_CLAIMS = new Set(known_claims || require('./ias-claims'))
|
|
19
|
+
|
|
20
|
+
function getUser(tokenInfo) {
|
|
21
|
+
const payload = tokenInfo.getPayload()
|
|
22
|
+
|
|
23
|
+
const clientid = tokenInfo.getClientId()
|
|
24
|
+
if (clientid === payload.sub) {
|
|
25
|
+
//> grant_type === client_credentials or x509
|
|
26
|
+
const roles = ['system-user']
|
|
27
|
+
if (clientid === credentials.clientid) roles.push('internal-user')
|
|
28
|
+
return new cds.User({ id: 'system', roles })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// add all unknown attributes to req.user.attr in order to keep public API small
|
|
32
|
+
const attr = Object.keys(payload)
|
|
33
|
+
.filter(k => !KNOWN_CLAIMS.has(k))
|
|
34
|
+
.reduce((attr, k) => { attr[k] = payload[k]; return attr }, {})
|
|
35
|
+
|
|
36
|
+
// same api as xsuaa-auth for easier migration
|
|
37
|
+
if (attr.user_name) attr.logonName = attr.user_name
|
|
38
|
+
if (attr.given_name) attr.givenName = attr.given_name
|
|
39
|
+
if (attr.family_name) attr.familyName = attr.family_name
|
|
40
|
+
|
|
41
|
+
return new cds.User({ id: payload.sub, attr })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return (req, _, next) => {
|
|
45
|
+
const token = req.headers.authorization?.split(/^bearer /i)[1]
|
|
46
|
+
xssec.createSecurityContext(token, credentials, 'IAS', function (err, securityContext, tokenInfo) {
|
|
47
|
+
// REVISIT: ias impl not as sophisticated as xsuaa impl, so we need to be more tolerant here -> xssec issue 221
|
|
48
|
+
// if (err && !tokenInfo) {
|
|
49
|
+
// // here, there is a general problem, .e.g., bad credentials -> throw the error
|
|
50
|
+
// return next(err)
|
|
51
|
+
// }
|
|
52
|
+
|
|
53
|
+
if (err && LOG._debug) LOG.debug('User could not be authenticated due to error:', err)
|
|
54
|
+
|
|
55
|
+
// if no general problem, tokenInfo object is always available -> add to req via getter for compat reasons
|
|
56
|
+
// -> the "always available" part is not true for ias (see REVISIT above)
|
|
57
|
+
tokenInfo && Object.defineProperty(req, 'tokenInfo', {
|
|
58
|
+
get() {
|
|
59
|
+
cds._logDeprecation('req.tokenInfo was added for compatibility purposes but will be removed in the next major version.')
|
|
60
|
+
return tokenInfo
|
|
35
61
|
}
|
|
62
|
+
})
|
|
36
63
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const payload = req.tokenInfo.getPayload()
|
|
45
|
-
if (req.tokenInfo.getClientId() === req.tokenInfo.getSubject()) {
|
|
46
|
-
//> grant_type === client_credentials or x509
|
|
47
|
-
const roles = ['authenticated-user', 'system-user']
|
|
48
|
-
if (payload.azp === config.credentials.clientid) roles.push('internal-user')
|
|
49
|
-
req.user = new cds.User({ id: 'system', roles, attr: {} })
|
|
50
|
-
} else {
|
|
51
|
-
// add all unknown attributes to req.user.attr in order to keep public API small
|
|
52
|
-
const attributes = Object.keys(payload)
|
|
53
|
-
.filter(k => !RESERVED_ATTRIBUTES.has(k))
|
|
54
|
-
.reduce((attrs, k) => { attrs[k] = payload[k]; return attrs }, {})
|
|
55
|
-
|
|
56
|
-
req.user = new cds.User({
|
|
57
|
-
id: req.user.id,
|
|
58
|
-
roles: ['authenticated-user'],
|
|
59
|
-
attr: attributes
|
|
60
|
-
})
|
|
64
|
+
if (!securityContext) {
|
|
65
|
+
if (!req.headers.authorization) {
|
|
66
|
+
LOG._debug && LOG.debug('No authorization header provided, continuing with default user.')
|
|
67
|
+
req.user = new cds.User.default()
|
|
68
|
+
return next()
|
|
69
|
+
}
|
|
70
|
+
return next(new cds.error('Unauthorized', { statusCode: 401 }))
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
req.
|
|
73
|
+
req.user = getUser(tokenInfo)
|
|
74
|
+
req.tenant = tokenInfo.getZoneId()
|
|
75
|
+
|
|
76
|
+
req.authInfo = securityContext //> compat req.authInfo
|
|
64
77
|
|
|
65
78
|
next()
|
|
66
79
|
})
|
|
80
|
+
}
|
|
67
81
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module.exports = Object.values({
|
|
2
|
+
/*
|
|
3
|
+
* JWT claims (https://datatracker.ietf.org/doc/html/rfc7519#section-4)
|
|
4
|
+
*/
|
|
5
|
+
ISSUER: 'iss',
|
|
6
|
+
SUBJECT: 'sub',
|
|
7
|
+
AUDIENCE: 'aud',
|
|
8
|
+
EXPIRATION_TIME: 'exp',
|
|
9
|
+
NOT_BEFORE: 'nbf',
|
|
10
|
+
ISSUED_AT: 'iat',
|
|
11
|
+
JWT_ID: 'jti',
|
|
12
|
+
/*
|
|
13
|
+
* TokenClaims (com.sap.cloud.security.token.TokenClaims)
|
|
14
|
+
*/
|
|
15
|
+
// ISSUER: "iss", //> already in JWT claims
|
|
16
|
+
IAS_ISSUER: 'ias_iss',
|
|
17
|
+
// EXPIRATION: "exp", //> already in JWT claims
|
|
18
|
+
// AUDIENCE: "aud", //> already in JWT claims
|
|
19
|
+
// NOT_BEFORE: "nbf", //> already in JWT claims
|
|
20
|
+
// SUBJECT: "sub", //> already in JWT claims
|
|
21
|
+
// USER_NAME: 'user_name', //> do not exclude
|
|
22
|
+
// GIVEN_NAME: 'given_name', //> do not exclude
|
|
23
|
+
// FAMILY_NAME: 'family_name', //> do not exclude
|
|
24
|
+
// EMAIL: 'email', //> do not exclude
|
|
25
|
+
SAP_GLOBAL_SCIM_ID: 'scim_id',
|
|
26
|
+
SAP_GLOBAL_USER_ID: 'user_uuid', //> exclude for now
|
|
27
|
+
SAP_GLOBAL_ZONE_ID: 'zone_uuid',
|
|
28
|
+
// GROUPS: 'groups', //> do not exclude
|
|
29
|
+
AUTHORIZATION_PARTY: 'azp',
|
|
30
|
+
CNF: 'cnf',
|
|
31
|
+
CNF_X5T: 'x5t#S256',
|
|
32
|
+
// own
|
|
33
|
+
APP_TENANT_ID: 'app_tid'
|
|
34
|
+
})
|