@sap/cds 5.8.3 → 5.9.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.
- package/CHANGELOG.md +193 -77
- package/app/fiori/preview.js +16 -11
- package/app/fiori/routes.js +15 -8
- package/app/index.js +1 -1
- package/bin/build/buildTaskFactory.js +3 -3
- package/bin/build/buildTaskProviderFactory.js +1 -1
- package/bin/build/constants.js +1 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +12 -7
- package/bin/build/provider/buildTaskHandlerInternal.js +1 -1
- package/bin/build/provider/buildTaskProviderInternal.js +8 -2
- package/bin/build/provider/hana/2migration.js +27 -24
- package/bin/build/provider/hana/index.js +17 -18
- package/bin/build/provider/hana/migrationtable.js +9 -10
- package/bin/build/provider/java-cf/index.js +4 -5
- package/bin/build/provider/node-cf/index.js +99 -6
- package/bin/cds.js +17 -18
- package/bin/deploy/to-hana/cfUtil.js +16 -19
- package/bin/deploy/to-hana/hana.js +7 -24
- package/bin/deploy/to-hana/hdiDeployUtil.js +8 -4
- package/bin/mtx/in-cds.js +2 -2
- package/bin/serve.js +10 -3
- package/bin/utils/modules.js +7 -0
- package/bin/version.js +56 -3
- package/lib/compile/cdsc.js +7 -2
- package/lib/compile/etc/_localized.js +36 -25
- package/lib/compile/etc/csv.js +8 -8
- package/lib/compile/for/drafts.js +9 -0
- package/lib/compile/for/java.js +16 -0
- package/lib/compile/for/nodejs.js +12 -0
- package/lib/compile/index.js +3 -0
- package/lib/compile/minify.js +16 -2
- package/lib/compile/parse.js +2 -2
- package/lib/compile/resolve.js +35 -18
- package/lib/compile/to/json.js +3 -1
- package/lib/compile/to/sql.js +2 -2
- package/lib/compile/to/srvinfo.js +4 -2
- package/lib/connect/index.js +1 -1
- package/lib/core/entities.js +15 -14
- package/lib/core/index.js +39 -36
- package/lib/core/reflect.js +4 -2
- package/lib/deploy.js +114 -127
- package/lib/env/defaults.js +1 -0
- package/lib/env/index.js +165 -165
- package/lib/env/presets.js +1 -0
- package/lib/env/requires.js +120 -49
- package/lib/index.js +1 -0
- package/lib/log/format/kibana.js +2 -2
- package/lib/ql/SELECT.js +10 -0
- package/lib/ql/parse.js +1 -0
- package/lib/req/cds-context.js +4 -1
- package/lib/req/context.js +50 -56
- package/lib/req/event.js +1 -6
- package/lib/req/locale.js +6 -5
- package/lib/req/request.js +2 -0
- package/lib/req/user.js +7 -5
- package/lib/serve/Service-api.js +10 -7
- package/lib/serve/Service-dispatch.js +9 -11
- package/lib/serve/Service-methods.js +30 -41
- package/lib/serve/Transaction.js +10 -7
- package/lib/serve/adapters.js +7 -5
- package/lib/serve/index.js +24 -12
- package/lib/utils/data.js +1 -1
- package/lib/utils/index.js +27 -30
- package/lib/utils/resources/index.js +101 -0
- package/lib/utils/resources/tar.js +71 -0
- package/lib/utils/resources/utils.js +11 -0
- package/libx/_runtime/audit/Service.js +36 -39
- package/libx/_runtime/audit/generic/personal/access.js +3 -4
- package/libx/_runtime/audit/generic/personal/modification.js +3 -4
- package/libx/_runtime/audit/utils/v2.js +1 -2
- package/libx/_runtime/auth/index.js +126 -84
- package/libx/_runtime/auth/strategies/JWT.js +12 -19
- package/libx/_runtime/auth/strategies/dummy.js +1 -5
- package/libx/_runtime/auth/strategies/dwc.js +11 -9
- package/libx/_runtime/auth/strategies/mock.js +0 -4
- package/libx/_runtime/auth/strategies/{utils/xssec.js → xssecUtils.js} +7 -4
- package/libx/_runtime/auth/strategies/xsuaa.js +12 -19
- package/libx/_runtime/auth/utils.js +22 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +104 -98
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/language.js +2 -8
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +4 -29
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +3 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +4 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +24 -21
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +8 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/invocation/DispatcherCommand.js +2 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -12
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +33 -9
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/dispatcherUtils.js +51 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +10 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +9 -11
- package/libx/_runtime/cds-services/adapter/rest/RestRequest.js +6 -3
- package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +4 -2
- package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/utils.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/binary.js +1 -1
- package/libx/_runtime/cds-services/adapter/rest/utils/key-value-utils.js +2 -3
- package/libx/_runtime/cds-services/adapter/rest/utils/parse-url.js +6 -4
- package/libx/_runtime/cds-services/adapter/rest/utils/result.js +1 -0
- package/libx/_runtime/cds-services/adapter/rest/utils/validation-checks.js +8 -5
- package/libx/_runtime/cds-services/services/Service.js +40 -0
- package/libx/_runtime/cds-services/services/utils/columns.js +4 -3
- package/libx/_runtime/cds-services/services/utils/compareJson.js +4 -4
- package/libx/_runtime/cds-services/services/utils/differ.js +3 -3
- package/libx/_runtime/cds-services/services/utils/handlerUtils.js +4 -4
- package/libx/_runtime/cds-services/services/utils/restrictions.js +78 -0
- package/libx/_runtime/cds-services/util/assert.js +20 -14
- package/libx/_runtime/cds.js +9 -1
- package/libx/_runtime/common/aspects/any.js +5 -0
- package/libx/_runtime/common/aspects/entity.js +25 -7
- package/libx/_runtime/common/aspects/utils.js +2 -2
- package/libx/_runtime/common/composition/data.js +6 -0
- package/libx/_runtime/common/composition/insert.js +3 -2
- package/libx/_runtime/common/composition/tree.js +4 -10
- package/libx/_runtime/common/composition/update.js +4 -4
- package/libx/_runtime/common/constants/draft.js +29 -26
- package/libx/_runtime/common/error/constants.js +2 -2
- package/libx/_runtime/common/error/frontend.js +7 -15
- package/libx/_runtime/common/generic/auth/capabilities.js +59 -0
- package/libx/_runtime/common/generic/auth/constants.js +20 -0
- package/libx/_runtime/common/generic/auth/expand.js +54 -0
- package/libx/_runtime/common/generic/auth/index.js +32 -0
- package/libx/_runtime/common/generic/auth/insertOnly.js +15 -0
- package/libx/_runtime/common/generic/auth/readOnly.js +26 -0
- package/libx/_runtime/common/generic/auth/requires.js +34 -0
- package/libx/_runtime/common/generic/auth/restrict.js +296 -0
- package/libx/_runtime/common/generic/auth/utils.js +213 -0
- package/libx/_runtime/common/generic/crud.js +14 -10
- package/libx/_runtime/common/generic/etag.js +1 -1
- package/libx/_runtime/common/generic/input.js +35 -35
- package/libx/_runtime/common/generic/sorting.js +2 -3
- package/libx/_runtime/common/generic/temporal.js +2 -2
- package/libx/_runtime/common/i18n/messages.properties +1 -1
- package/libx/_runtime/common/toggles/handler.js +21 -0
- package/libx/_runtime/common/utils/copy.js +10 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +100 -29
- package/libx/_runtime/common/utils/csn.js +63 -1
- package/libx/_runtime/common/utils/dollar.js +10 -1
- package/libx/_runtime/common/utils/draft.js +46 -7
- package/libx/_runtime/common/utils/entityFromCqn.js +13 -9
- package/libx/_runtime/common/utils/extensibilityUtils.js +18 -0
- package/libx/_runtime/common/utils/foreignKeyPropagations.js +88 -104
- package/libx/_runtime/common/utils/generateOnCond.js +5 -2
- package/libx/_runtime/common/utils/quotingStyles.js +2 -0
- package/libx/_runtime/common/utils/resolveStructured.js +25 -9
- package/libx/_runtime/common/utils/resolveView.js +4 -1
- package/libx/_runtime/common/utils/rewriteAsterisks.js +3 -16
- package/libx/_runtime/common/utils/structured.js +33 -37
- package/libx/_runtime/common/utils/template.js +17 -8
- package/libx/_runtime/common/utils/templateProcessor.js +28 -28
- package/libx/_runtime/db/data-conversion/post-processing.js +118 -412
- package/libx/_runtime/db/expand/expandCQNToJoin.js +45 -41
- package/libx/_runtime/db/expand/rawToExpanded.js +29 -8
- package/libx/_runtime/db/generic/index.js +1 -3
- package/libx/_runtime/db/generic/input.js +5 -10
- package/libx/_runtime/db/generic/rewrite.js +5 -2
- package/libx/_runtime/db/generic/structured.js +2 -2
- package/libx/_runtime/db/query/delete.js +2 -2
- package/libx/_runtime/db/query/insert.js +1 -1
- package/libx/_runtime/db/query/update.js +9 -14
- package/libx/_runtime/db/sql-builder/CreateBuilder.js +4 -3
- package/libx/_runtime/db/sql-builder/FunctionBuilder.js +8 -8
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +14 -1
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +3 -2
- package/libx/_runtime/db/sql-builder/dataTypes.js +3 -3
- package/libx/_runtime/db/utils/columns.js +3 -3
- package/libx/_runtime/db/utils/normalizeTimeData.js +2 -2
- package/libx/_runtime/db/utils/propagateForeignKeys.js +6 -2
- package/libx/_runtime/extensibility/mps/index.js +5 -0
- package/libx/_runtime/extensibility/mps/service.js +111 -0
- package/libx/_runtime/extensibility/mps/tar.js +42 -0
- package/libx/_runtime/extensibility/mps/utils.js +11 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformREAD.js +0 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformRESULT.js +17 -5
- package/libx/_runtime/{fiori → extensibility}/uiflex/handler/transformWRITE.js +1 -0
- package/libx/_runtime/extensibility/uiflex/index.js +54 -0
- package/libx/_runtime/extensibility/uiflex/service.js +276 -0
- package/libx/_runtime/{fiori → extensibility}/uiflex/utils.js +22 -7
- package/libx/_runtime/fiori/generic/activate.js +2 -2
- package/libx/_runtime/fiori/generic/before.js +4 -4
- package/libx/_runtime/fiori/generic/new.js +3 -3
- package/libx/_runtime/fiori/generic/patch.js +1 -1
- package/libx/_runtime/fiori/generic/read.js +58 -66
- package/libx/_runtime/fiori/generic/readOverDraft.js +71 -16
- package/libx/_runtime/fiori/utils/handler.js +6 -13
- package/libx/_runtime/fiori/utils/where.js +6 -5
- package/libx/_runtime/hana/Service.js +4 -10
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +1 -1
- package/libx/_runtime/hana/driver.js +2 -2
- package/libx/_runtime/hana/execute.js +27 -74
- package/libx/_runtime/hana/pool.js +1 -1
- package/libx/_runtime/hana/streaming.js +2 -1
- package/libx/_runtime/index.js +6 -6
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +5 -21
- package/libx/_runtime/messaging/Outbox.js +2 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +4 -14
- package/libx/_runtime/messaging/common-utils/connections.js +5 -7
- package/libx/_runtime/messaging/common-utils/normalizeIncomingMessage.js +30 -0
- package/libx/_runtime/messaging/enterprise-messaging-shared.js +2 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +36 -30
- package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +19 -12
- package/libx/_runtime/messaging/enterprise-messaging.js +8 -8
- package/libx/_runtime/messaging/file-based.js +5 -5
- package/libx/_runtime/messaging/message-queuing.js +14 -12
- package/libx/_runtime/messaging/outbox/utils.js +18 -19
- package/libx/_runtime/messaging/redis-messaging.js +91 -0
- package/libx/_runtime/messaging/service.js +8 -6
- package/libx/_runtime/remote/Service.js +44 -8
- package/libx/_runtime/remote/utils/client.js +24 -19
- package/libx/_runtime/remote/utils/data.js +11 -11
- package/libx/_runtime/sqlite/Service.js +6 -9
- package/libx/_runtime/sqlite/customBuilder/CustomFunctionBuilder.js +5 -2
- package/libx/_runtime/types/api.js +10 -2
- package/libx/common/utils/ucsn.js +109 -0
- package/libx/gql/resolvers/crud/update.js +5 -0
- package/libx/gql/resolvers/parse/ast2cqn/columns.js +3 -1
- package/libx/gql/schema/typeDefMap.js +2 -2
- package/libx/odata/afterburner.js +110 -16
- package/libx/odata/cqn2odata.js +24 -27
- package/libx/odata/grammar.pegjs +9 -1
- package/libx/odata/parseToCqn.js +39 -0
- package/libx/odata/parser.js +1 -1
- package/libx/rest/RestAdapter.js +9 -1
- package/libx/rest/middleware/input.js +54 -0
- package/libx/rest/middleware/operation.js +14 -1
- package/libx/rest/middleware/parse.js +11 -7
- package/package.json +2 -2
- package/server.js +34 -19
- package/srv/audit-log.cds +2 -2
- package/srv/flex.cds +8 -2
- package/srv/flex.js +1 -1
- package/srv/mps.cds +23 -0
- package/srv/mps.js +1 -0
- package/libx/_runtime/auth/strategies/utils/uaa.js +0 -21
- package/libx/_runtime/common/generic/auth.js +0 -874
- package/libx/_runtime/common/toggles/alpha.js +0 -43
- package/libx/_runtime/db/generic/arrayed.js +0 -33
- package/libx/_runtime/fiori/uiflex/index.js +0 -35
- package/libx/_runtime/fiori/uiflex/service.js +0 -150
- package/libx/rest/utils/data.js +0 -60
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const cds = require('../cds')
|
|
2
|
-
const LOG = cds.log(
|
|
2
|
+
const LOG = cds.log()
|
|
3
3
|
|
|
4
4
|
const _require = require('../common/utils/require')
|
|
5
|
-
const { UNAUTHORIZED } = require('./utils')
|
|
5
|
+
const { UNAUTHORIZED, isRestricted } = require('./utils')
|
|
6
6
|
|
|
7
|
-
let passport
|
|
7
|
+
let passport, logged
|
|
8
8
|
|
|
9
9
|
// strategy initializers for lazy loading of dependencies
|
|
10
10
|
const _initializers = {
|
|
@@ -21,125 +21,167 @@ const _initializers = {
|
|
|
21
21
|
const DwcStrategy = require('./strategies/dwc')
|
|
22
22
|
passport.use(new DwcStrategy())
|
|
23
23
|
},
|
|
24
|
-
|
|
24
|
+
jwt: ({ credentials, uaa }) => {
|
|
25
25
|
const JWTStrategy = require('./strategies/JWT')
|
|
26
|
-
|
|
26
|
+
if (credentials) {
|
|
27
|
+
passport.use(new JWTStrategy(credentials))
|
|
28
|
+
} else if (uaa) {
|
|
29
|
+
// REVISIT: compat, remove with cds^6
|
|
30
|
+
passport.use(new JWTStrategy(uaa.credentials))
|
|
31
|
+
} else {
|
|
32
|
+
throw Object.assign(new Error('No or malformed credentials for auth kind "jwt-auth"'), { credentials })
|
|
33
|
+
}
|
|
27
34
|
},
|
|
28
35
|
mock: ({ users }, srvName) => {
|
|
29
36
|
const MockStrategy = require('./strategies/mock')
|
|
30
37
|
passport.use(new MockStrategy(users, `mock_${srvName}`))
|
|
31
38
|
},
|
|
32
|
-
xsuaa: ({ uaa }) => {
|
|
39
|
+
xsuaa: ({ credentials, uaa }) => {
|
|
33
40
|
const XSUAAStrategy = require('./strategies/xsuaa')
|
|
34
|
-
|
|
41
|
+
if (credentials) {
|
|
42
|
+
passport.use(new XSUAAStrategy(credentials))
|
|
43
|
+
} else if (uaa) {
|
|
44
|
+
// REVISIT: compat, remove with cds^6
|
|
45
|
+
passport.use(new XSUAAStrategy(uaa.credentials))
|
|
46
|
+
} else {
|
|
47
|
+
throw Object.assign(new Error('No or malformed credentials for auth kind "xsuaa"'), { credentials })
|
|
48
|
+
}
|
|
35
49
|
}
|
|
36
50
|
}
|
|
37
51
|
|
|
38
52
|
// map for initialized authenticators
|
|
39
53
|
const _authenticators = {}
|
|
40
54
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Object.keys(srv.entities).some(
|
|
46
|
-
k =>
|
|
47
|
-
srv.entities[k].actions &&
|
|
48
|
-
Object.keys(srv.entities[k].actions).some(
|
|
49
|
-
l => srv.entities[k].actions[l]['@requires'] || srv.entities[k].actions[l]['@restrict']
|
|
50
|
-
)
|
|
51
|
-
) ||
|
|
52
|
-
Object.keys(srv.operations).some(k => srv.operations[k]['@requires'] || srv.operations[k]['@restrict'])
|
|
53
|
-
)
|
|
55
|
+
const _log = (req, challenges) => {
|
|
56
|
+
if (!LOG._debug) return
|
|
57
|
+
const challengesLog = challenges ? ['User challenges:', challenges] : []
|
|
58
|
+
LOG.debug(`User "${req.user.id}" request URL`, req.url, '\n', ...challengesLog)
|
|
54
59
|
}
|
|
55
60
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
const _authCallback = (req, res, next, internalError, user, challenges) => {
|
|
62
|
+
// An internal error occurs during the authentication process
|
|
63
|
+
if (internalError) {
|
|
64
|
+
// REVISIT: What to do? Security log?
|
|
65
|
+
return res.status(401).json(UNAUTHORIZED) // no details to client
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
let infoChallenges
|
|
69
|
+
|
|
70
|
+
if (challenges) {
|
|
71
|
+
if (Array.isArray(challenges)) {
|
|
72
|
+
infoChallenges = challenges.filter(ele => ele)
|
|
73
|
+
infoChallenges = infoChallenges.length ? infoChallenges : undefined
|
|
74
|
+
} else {
|
|
75
|
+
req.authInfo = challenges
|
|
76
|
+
}
|
|
66
77
|
}
|
|
78
|
+
|
|
79
|
+
req.user = user || Object.defineProperty(new cds.User(), '_challenges', { enumerable: false, value: infoChallenges })
|
|
80
|
+
Object.defineProperty(req.user, '_req', { enumerable: false, value: req })
|
|
81
|
+
_log(req, infoChallenges)
|
|
82
|
+
next()
|
|
67
83
|
}
|
|
68
84
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
85
|
+
const _mountMockAuth = (srv, app, strategy, config) => {
|
|
86
|
+
const impl =
|
|
87
|
+
strategy === 'dummy'
|
|
88
|
+
? new (require('./strategies/dummy'))()
|
|
89
|
+
: new (require('./strategies/mock'))(config.users, `mock_${srv.name}`)
|
|
75
90
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
91
|
+
app.use(srv.path, (req, res, next) => {
|
|
92
|
+
let user, challenge
|
|
93
|
+
impl.success = arg => (user = arg)
|
|
94
|
+
impl.fail = arg => (challenge = arg)
|
|
95
|
+
impl.authenticate(req)
|
|
96
|
+
_authCallback(req, res, next, undefined, user, [challenge])
|
|
97
|
+
})
|
|
98
|
+
}
|
|
83
99
|
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
const _mountPassportAuth = (srv, app, strategy, config) => {
|
|
101
|
+
passport = passport || _require('passport')
|
|
86
102
|
|
|
87
|
-
|
|
88
|
-
|
|
103
|
+
// initialize strategy
|
|
104
|
+
if (!_authenticators[strategy] || strategy === 'mock' || process.env.NODE_ENV === 'test') {
|
|
105
|
+
_initializers[strategy](config, srv.name)
|
|
106
|
+
_authenticators[strategy] = true
|
|
107
|
+
}
|
|
89
108
|
|
|
90
|
-
|
|
109
|
+
// authenticate
|
|
110
|
+
app.use(srv.path, passport.initialize())
|
|
111
|
+
app.use(srv.path, (req, res, next) => {
|
|
112
|
+
const options = { session: false, failWithError: true }
|
|
113
|
+
const callback = _authCallback.bind(undefined, req, res, next)
|
|
114
|
+
passport.authenticate(strategy === 'jwt' ? 'JWT' : strategy, options, callback)(req, res, next)
|
|
115
|
+
})
|
|
91
116
|
}
|
|
92
117
|
|
|
118
|
+
/*
|
|
119
|
+
* export authentication middleware
|
|
120
|
+
*/
|
|
121
|
+
// eslint-disable-next-line complexity
|
|
93
122
|
module.exports = (srv, app, options) => {
|
|
94
|
-
|
|
123
|
+
// NOTE: options.auth is not an official API
|
|
124
|
+
let config = 'auth' in options ? options.auth : cds.env.requires.auth
|
|
95
125
|
|
|
96
|
-
|
|
97
|
-
|
|
126
|
+
// cds.env.requires.auth = { kind: 'xsuaa-auth' } was briefly documented on capire -> also support
|
|
127
|
+
if (config && config.kind === 'xsuaa-auth' && !config.credentials) config = cds.env.requires.xsuaa
|
|
98
128
|
|
|
99
|
-
|
|
100
|
-
|
|
129
|
+
// mount custom authentication middleware
|
|
130
|
+
if (config && config.impl) {
|
|
131
|
+
app.use(srv.path, _require(cds.resolve(config.impl)))
|
|
101
132
|
return
|
|
102
133
|
}
|
|
103
134
|
|
|
104
|
-
|
|
135
|
+
const restricted = isRestricted(srv)
|
|
136
|
+
if (restricted && !config) {
|
|
137
|
+
// REVISIT: why exitCode needed?
|
|
138
|
+
process.exitCode = 1
|
|
139
|
+
throw new Error('Authentication required for authorization checks')
|
|
140
|
+
}
|
|
105
141
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
142
|
+
const isMultiTenant = cds.env.requires.db && cds.env.requires.db.multiTenant
|
|
143
|
+
if (isMultiTenant && !config) {
|
|
144
|
+
// REVISIT: why exitCode needed?
|
|
145
|
+
process.exitCode = 1
|
|
146
|
+
throw new Error('Authentication required for multitenancy')
|
|
110
147
|
}
|
|
111
148
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
app.use(srv.path, (req, res, next) => {
|
|
121
|
-
let user, challenge
|
|
122
|
-
impl.success = arg => (user = arg)
|
|
123
|
-
impl.fail = arg => (challenge = arg)
|
|
124
|
-
impl.authenticate(req)
|
|
125
|
-
_callback(req, res, next, undefined, user, [challenge])
|
|
126
|
-
})
|
|
149
|
+
if (!config) {
|
|
150
|
+
if (!logged) {
|
|
151
|
+
const msg = 'No authentication configured'
|
|
152
|
+
if (process.env.NODE_ENV !== 'production') LOG._debug && LOG.debug(msg)
|
|
153
|
+
else LOG._info && LOG.info(`${msg}. This is not recommended in production.`)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// no auth wanted > return
|
|
127
157
|
return
|
|
128
158
|
}
|
|
129
159
|
|
|
130
|
-
//
|
|
131
|
-
|
|
160
|
+
// strategy from kind
|
|
161
|
+
let strategy
|
|
162
|
+
// compat for auth.strategy
|
|
163
|
+
if (config.strategy) {
|
|
164
|
+
strategy = config.strategy.toLowerCase()
|
|
165
|
+
}
|
|
166
|
+
strategy = strategy || config.kind.replace('-auth', '').toLowerCase()
|
|
167
|
+
if (strategy === 'mocked') strategy = 'mock'
|
|
132
168
|
|
|
133
|
-
|
|
134
|
-
|
|
169
|
+
if (!_initializers[strategy]) {
|
|
170
|
+
// REVISIT: why exitCode needed?
|
|
171
|
+
process.exitCode = 1
|
|
172
|
+
throw new Error(`Authentication kind "${config.kind}" is not supported`)
|
|
173
|
+
}
|
|
135
174
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
175
|
+
if (!logged) LOG._debug && LOG.debug(`Using authentication kind "${config.kind}"`)
|
|
176
|
+
|
|
177
|
+
if (strategy in { dummy: 1, mock: 1 }) {
|
|
178
|
+
// > dummy or mock authentication (for development/testing)
|
|
179
|
+
_mountMockAuth(srv, app, strategy, config)
|
|
180
|
+
} else {
|
|
181
|
+
// > passport authentication
|
|
182
|
+
_mountPassportAuth(srv, app, strategy, config)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// so we don't log the same stuff multiple times
|
|
186
|
+
logged = true
|
|
145
187
|
}
|
|
@@ -1,40 +1,33 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
|
|
3
3
|
const _require = require('../../common/utils/require')
|
|
4
|
-
const
|
|
5
|
-
const xssecUtils = require('./utils/xssec')
|
|
4
|
+
const xssecUtils = require('./xssecUtils')
|
|
6
5
|
|
|
7
6
|
// use _require for a better error message
|
|
8
7
|
const { JWTStrategy: JS } = _require('@sap/xssec')
|
|
9
8
|
|
|
10
9
|
class JWTStrategy extends JS {
|
|
11
|
-
constructor(
|
|
12
|
-
super(
|
|
10
|
+
constructor(credentials) {
|
|
11
|
+
super(credentials)
|
|
12
|
+
this.credentials = credentials
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
authenticate(req, options) {
|
|
16
|
+
const credentials = this.credentials
|
|
17
|
+
|
|
16
18
|
// monkey patch success
|
|
17
19
|
const _success = this.success
|
|
18
20
|
this.success = (user, info) => {
|
|
19
21
|
// create cds.User
|
|
20
|
-
user = new cds.User(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
tenant: xssecUtils.getTenant(info) || null
|
|
27
|
-
}
|
|
28
|
-
)
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
// set _req for locale getter
|
|
32
|
-
Object.defineProperty(user, '_req', { enumerable: false, value: req })
|
|
33
|
-
|
|
22
|
+
user = new cds.User({
|
|
23
|
+
id: xssecUtils.getUserId(user, info),
|
|
24
|
+
tenant: xssecUtils.getTenant(info) || null,
|
|
25
|
+
_roles: xssecUtils.getRoles(['any', 'identified-user'], info, credentials),
|
|
26
|
+
attr: xssecUtils.getAttrForJWT(info)
|
|
27
|
+
})
|
|
34
28
|
// call "super.success"
|
|
35
29
|
_success(user, info)
|
|
36
30
|
}
|
|
37
|
-
|
|
38
31
|
super.authenticate(req, options)
|
|
39
32
|
}
|
|
40
33
|
}
|
|
@@ -5,12 +5,8 @@ class DummyStrategy {
|
|
|
5
5
|
this.name = 'dummy'
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
authenticate(
|
|
8
|
+
authenticate() {
|
|
9
9
|
const user = new cds.User.Privileged()
|
|
10
|
-
|
|
11
|
-
// set _req for locale getter
|
|
12
|
-
Object.defineProperty(user, '_req', { enumerable: false, value: req })
|
|
13
|
-
|
|
14
10
|
this.success(user)
|
|
15
11
|
}
|
|
16
12
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const LOG = cds.log()
|
|
3
3
|
|
|
4
|
-
function decode(req, header,
|
|
4
|
+
function decode(req, header, encoded) {
|
|
5
5
|
if (!req.headers[header]) return
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
: Buffer.from(req.headers[header], 'base64').toString('utf-8')
|
|
6
|
+
if (encoded) return JSON.parse(Buffer.from(req.headers[header], 'base64').toString('utf-8'))
|
|
7
|
+
return req.headers[header]
|
|
9
8
|
}
|
|
10
9
|
|
|
11
10
|
class DwcStrategy {
|
|
@@ -15,17 +14,23 @@ class DwcStrategy {
|
|
|
15
14
|
|
|
16
15
|
authenticate(req) {
|
|
17
16
|
let tenant, usr, scopes, attr
|
|
17
|
+
|
|
18
18
|
try {
|
|
19
19
|
tenant = decode(req, 'dwc-tenant')
|
|
20
20
|
usr = decode(req, 'dwc-user', true)
|
|
21
|
-
scopes = decode(req, 'dwc-scopes'
|
|
21
|
+
scopes = decode(req, 'dwc-scopes') || ''
|
|
22
22
|
attr = decode(req, 'dwc-xsuaa-attributes', true) || {}
|
|
23
23
|
} catch (e) {
|
|
24
|
-
LOG.
|
|
24
|
+
LOG._debug && LOG.debug('Error while parsing HTTP headers for DWC authentication:', e)
|
|
25
25
|
return this.fail()
|
|
26
26
|
}
|
|
27
|
+
|
|
27
28
|
if (!tenant || !usr) return this.fail()
|
|
28
29
|
|
|
30
|
+
// make scopes app-local
|
|
31
|
+
const xsappname = decode(req, 'dwc-xsuaa-xsappname') || ''
|
|
32
|
+
scopes = scopes.split(' ').map(s => (s.startsWith(xsappname + '.') ? s.replace(xsappname + '.', '') : s))
|
|
33
|
+
|
|
29
34
|
const user = new cds.User({
|
|
30
35
|
id: usr.logonName,
|
|
31
36
|
tenant,
|
|
@@ -33,9 +38,6 @@ class DwcStrategy {
|
|
|
33
38
|
attr: Object.assign(attr, usr)
|
|
34
39
|
})
|
|
35
40
|
|
|
36
|
-
// set _req for locale getter
|
|
37
|
-
Object.defineProperty(user, '_req', { enumerable: false, value: req })
|
|
38
|
-
|
|
39
41
|
this.success(user)
|
|
40
42
|
}
|
|
41
43
|
}
|
|
@@ -69,10 +69,6 @@ class MockStrategy {
|
|
|
69
69
|
const tenant = user.tenant || (user.jwt && user.jwt.zid) || null
|
|
70
70
|
|
|
71
71
|
user = new cds.User({ id: user.ID || id, _roles, attr, tenant })
|
|
72
|
-
|
|
73
|
-
// set _req for locale getter
|
|
74
|
-
Object.defineProperty(user, '_req', { enumerable: false, value: req })
|
|
75
|
-
|
|
76
72
|
this.success(user)
|
|
77
73
|
}
|
|
78
74
|
}
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
+
const CLIENT = { client_credentials: 1, client_x509: 1 }
|
|
2
|
+
|
|
1
3
|
const getUserId = (user, info) => {
|
|
2
4
|
// fallback for grant_type=client_credentials (xssec v3)
|
|
3
5
|
return user.id || (info && info.getClientId && info.getClientId())
|
|
4
6
|
}
|
|
5
7
|
|
|
6
|
-
const _addRolesFromGrantType = (roles, info) => {
|
|
8
|
+
const _addRolesFromGrantType = (roles, info, credentials) => {
|
|
7
9
|
const grantType = info && (info.grantType || (info.getGrantType && info.getGrantType()))
|
|
8
10
|
if (grantType) {
|
|
9
11
|
// > not "weak"
|
|
10
12
|
roles.push('authenticated-user')
|
|
11
|
-
if (
|
|
13
|
+
if (grantType in CLIENT) {
|
|
12
14
|
roles.push('system-user')
|
|
15
|
+
if (info.getClientId() === credentials.clientid) roles.push('internal-user')
|
|
13
16
|
}
|
|
14
17
|
}
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
const getRoles = (roles, info) => {
|
|
18
|
-
_addRolesFromGrantType(roles, info)
|
|
20
|
+
const getRoles = (roles, info, credentials) => {
|
|
21
|
+
_addRolesFromGrantType(roles, info, credentials)
|
|
19
22
|
|
|
20
23
|
// convert to object
|
|
21
24
|
roles = Object.assign(...roles.map(ele => ({ [ele]: true })))
|
|
@@ -1,41 +1,34 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
|
|
3
3
|
const _require = require('../../common/utils/require')
|
|
4
|
-
const
|
|
5
|
-
const xssecUtils = require('./utils/xssec')
|
|
4
|
+
const xssecUtils = require('./xssecUtils')
|
|
6
5
|
|
|
7
6
|
// use _require for a better error message
|
|
8
7
|
const { JWTStrategy: JS } = _require('@sap/xssec')
|
|
9
8
|
|
|
10
9
|
class XSUAAStrategy extends JS {
|
|
11
|
-
constructor(
|
|
12
|
-
super(
|
|
10
|
+
constructor(credentials) {
|
|
11
|
+
super(credentials)
|
|
12
|
+
this.credentials = credentials
|
|
13
13
|
this.name = 'xsuaa'
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
authenticate(req, options) {
|
|
17
|
+
const credentials = this.credentials
|
|
18
|
+
|
|
17
19
|
// monkey patch success
|
|
18
20
|
const _success = this.success
|
|
19
21
|
this.success = (user, info) => {
|
|
20
22
|
// create cds.User
|
|
21
|
-
user = new cds.User(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
tenant: xssecUtils.getTenant(info) || null
|
|
28
|
-
}
|
|
29
|
-
)
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
// set _req for locale getter
|
|
33
|
-
Object.defineProperty(user, '_req', { enumerable: false, value: req })
|
|
34
|
-
|
|
23
|
+
user = new cds.User({
|
|
24
|
+
id: xssecUtils.getUserId(user, info),
|
|
25
|
+
tenant: xssecUtils.getTenant(info) || null,
|
|
26
|
+
_roles: xssecUtils.getRoles(['any', 'identified-user'], info, credentials),
|
|
27
|
+
attr: xssecUtils.getAttrForXSSEC(info)
|
|
28
|
+
})
|
|
35
29
|
// call "super.success"
|
|
36
30
|
_success(user, info)
|
|
37
31
|
}
|
|
38
|
-
|
|
39
32
|
super.authenticate(req, options)
|
|
40
33
|
}
|
|
41
34
|
}
|
|
@@ -17,8 +17,29 @@ const getRequiresAsArray = definition => {
|
|
|
17
17
|
return requires
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
const isRestricted = srv => {
|
|
21
|
+
const envAuth = cds.env.requires.auth
|
|
22
|
+
if (envAuth && envAuth.restrict_all_services) return true
|
|
23
|
+
if (srv.definition['@requires']) return true
|
|
24
|
+
|
|
25
|
+
const entities = srv.entities
|
|
26
|
+
const entitiesKeys = Object.keys(entities)
|
|
27
|
+
|
|
28
|
+
return !!(
|
|
29
|
+
entitiesKeys.some(entity => entities[entity]['@requires'] || entities[entity]['@restrict']) ||
|
|
30
|
+
entitiesKeys.some(entity => {
|
|
31
|
+
const actions = entities[entity].actions
|
|
32
|
+
actions && Object.keys(actions).some(action => actions[action]['@requires'] || actions[action]['@restrict'])
|
|
33
|
+
}) ||
|
|
34
|
+
Object.keys(srv.operations).some(
|
|
35
|
+
operation => srv.operations[operation]['@requires'] || srv.operations[operation]['@restrict']
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
20
40
|
module.exports = {
|
|
21
41
|
UNAUTHORIZED,
|
|
22
42
|
FORBIDDEN,
|
|
23
|
-
getRequiresAsArray
|
|
43
|
+
getRequiresAsArray,
|
|
44
|
+
isRestricted
|
|
24
45
|
}
|