@cap-js/ord 1.3.14 → 1.4.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/README.md +98 -13
- package/cds-plugin.js +0 -6
- package/lib/access-strategies.js +172 -0
- package/lib/auth/authentication.js +351 -0
- package/lib/auth/cf-mtls.js +516 -0
- package/lib/auth/mtls-endpoint-service.js +141 -0
- package/lib/build.js +7 -10
- package/lib/constants.js +45 -0
- package/lib/defaults.js +14 -10
- package/lib/extendOrdWithCustom.js +39 -7
- package/lib/index.js +8 -5
- package/lib/logger.js +3 -12
- package/lib/mcpAdapter.js +132 -0
- package/lib/metaData.js +26 -5
- package/lib/ord-service.js +20 -6
- package/lib/ord.js +35 -4
- package/lib/templates.js +128 -16
- package/package.json +11 -8
- package/lib/authentication.js +0 -153
package/lib/templates.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
const cds = require("@sap/cds");
|
|
2
2
|
const { hasSAPPolicyLevel } = require("./utils");
|
|
3
|
+
const { isMCPPluginReady } = require("./mcpAdapter");
|
|
3
4
|
const defaults = require("./defaults");
|
|
4
5
|
const _ = require("lodash");
|
|
5
6
|
const {
|
|
6
|
-
AUTHENTICATION_TYPE,
|
|
7
7
|
DATA_PRODUCT_ANNOTATION,
|
|
8
8
|
DATA_PRODUCT_SIMPLE_ANNOTATION,
|
|
9
9
|
DATA_PRODUCT_TYPE,
|
|
10
10
|
DESCRIPTION_PREFIX,
|
|
11
11
|
ENTITY_RELATIONSHIP_ANNOTATION,
|
|
12
12
|
LEVEL,
|
|
13
|
+
MCP_CUSTOM_TYPE,
|
|
13
14
|
ORD_EXTENSIONS_PREFIX,
|
|
14
15
|
ORD_ODM_ENTITY_NAME_ANNOTATION,
|
|
15
16
|
ORD_RESOURCE_TYPE,
|
|
@@ -21,7 +22,8 @@ const {
|
|
|
21
22
|
CONTENT_MERGE_KEY,
|
|
22
23
|
CDS_ELEMENT_KIND,
|
|
23
24
|
} = require("./constants");
|
|
24
|
-
const
|
|
25
|
+
const Logger = require("./logger");
|
|
26
|
+
const { ensureAccessStrategies } = require("./access-strategies");
|
|
25
27
|
|
|
26
28
|
function unflatten(flattedObject) {
|
|
27
29
|
let result = {};
|
|
@@ -66,8 +68,13 @@ const _generatePaths = (srv, srvDefinition) => {
|
|
|
66
68
|
|
|
67
69
|
//putting OData as default in case of non-supported protocol
|
|
68
70
|
if (paths.length === 0) {
|
|
69
|
-
srvDefinition
|
|
70
|
-
|
|
71
|
+
if (isPrimaryDataProductService(srvDefinition)) {
|
|
72
|
+
// Data product services use REST protocol, not OData
|
|
73
|
+
paths.push({ kind: "rest", path: protocols.path4(srvDefinition) });
|
|
74
|
+
} else {
|
|
75
|
+
srvDefinition["@odata"] = true;
|
|
76
|
+
paths.push({ kind: "odata", path: protocols.path4(srvDefinition) });
|
|
77
|
+
}
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
return paths;
|
|
@@ -152,29 +159,32 @@ function _getEntityVersion(entity) {
|
|
|
152
159
|
}
|
|
153
160
|
|
|
154
161
|
/**
|
|
155
|
-
*
|
|
162
|
+
* Pure template function for creating ORD resource definitions.
|
|
163
|
+
* This function does not make assumptions about access strategies - they must be provided explicitly.
|
|
164
|
+
* Access strategies are validated and will fallback to 'open' in non-strict mode if missing.
|
|
165
|
+
*
|
|
156
166
|
* @param {string} resourceType The type of the resource.
|
|
157
167
|
* @param {string} mediaType The media type of the resource.
|
|
158
168
|
* @param {string} ordId The ordId of the resource.
|
|
159
169
|
* @param {string} serviceName The name of the service.
|
|
160
170
|
* @param {string} fileExtension The file extension of the resource.
|
|
161
|
-
* @param {Array} accessStrategies The array of accessStrategies objects
|
|
171
|
+
* @param {Array} accessStrategies The array of accessStrategies objects (required, no default)
|
|
162
172
|
* @returns {Object} A resource definition object.
|
|
163
173
|
* @private
|
|
164
174
|
*/
|
|
165
|
-
function _getResourceDefinition(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
function _getResourceDefinition(resourceType, mediaType, ordId, serviceName, fileExtension, accessStrategies) {
|
|
176
|
+
// Validate and ensure access strategies are present
|
|
177
|
+
// In non-strict mode, this will log error and fallback to 'open'
|
|
178
|
+
// In strict mode (cds.env.ord.strictAccessStrategies = true), this will throw
|
|
179
|
+
const validatedStrategies = ensureAccessStrategies(accessStrategies, {
|
|
180
|
+
resourceName: `${serviceName} (${resourceType})`,
|
|
181
|
+
});
|
|
182
|
+
|
|
173
183
|
return {
|
|
174
184
|
type: resourceType,
|
|
175
185
|
mediaType: `application/${mediaType}`,
|
|
176
186
|
url: `/ord/v1/${ordId}/${serviceName}.${fileExtension}`,
|
|
177
|
-
accessStrategies,
|
|
187
|
+
accessStrategies: validatedStrategies,
|
|
178
188
|
};
|
|
179
189
|
}
|
|
180
190
|
|
|
@@ -197,7 +207,7 @@ const createGroupsTemplateForService = (serviceName, serviceDefinition, appConfi
|
|
|
197
207
|
const visibility = _handleVisibility(ordExtensions, serviceDefinition, appConfig.env?.defaultVisibility);
|
|
198
208
|
if (visibility === RESOURCE_VISIBILITY.private) {
|
|
199
209
|
// If the visibility of the service is private, do not create a group
|
|
200
|
-
Logger.
|
|
210
|
+
Logger.info("Skipping group creation for private service:", serviceName);
|
|
201
211
|
return undefined;
|
|
202
212
|
}
|
|
203
213
|
|
|
@@ -581,6 +591,107 @@ function _getPackageID(namespace, packageIds, resourceType, visibility = RESOURC
|
|
|
581
591
|
return packageIds.find((id) => id.includes(`-${resourceType}-`)) || packageIds.find((id) => id.includes(namespace));
|
|
582
592
|
}
|
|
583
593
|
|
|
594
|
+
/**
|
|
595
|
+
* Helper function to create a single MCP API resource.
|
|
596
|
+
*
|
|
597
|
+
* @param {object} metadata - The MCP metadata object.
|
|
598
|
+
* @param {object} appConfig - The application configuration.
|
|
599
|
+
* @param {Array} packageIds - The available package identifiers.
|
|
600
|
+
* @param {Array} accessStrategies - The array of accessStrategies objects.
|
|
601
|
+
* @returns {Object} An MCP API resource object.
|
|
602
|
+
*/
|
|
603
|
+
function createSingleMCPResource(metadata, appConfig, packageIds, accessStrategies) {
|
|
604
|
+
const visibility = metadata?.visibility || RESOURCE_VISIBILITY.public;
|
|
605
|
+
|
|
606
|
+
// Generate ordId based on visibility
|
|
607
|
+
let resourceId;
|
|
608
|
+
if (visibility === RESOURCE_VISIBILITY.public) {
|
|
609
|
+
resourceId = "mcp-server"; // Default public resource
|
|
610
|
+
} else {
|
|
611
|
+
resourceId = `mcp-server-${visibility}`; // mcp-server-internal, mcp-server-private
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const ordId = `${appConfig.ordNamespace}:apiResource:${resourceId}:v1`;
|
|
615
|
+
|
|
616
|
+
// Use metadata with proper fallbacks
|
|
617
|
+
const title = metadata?.title || `MCP Server for ${appConfig.appName}`;
|
|
618
|
+
const shortDescription =
|
|
619
|
+
metadata?.shortDescription || `This is the MCP server to interact with the ${appConfig.appName}`;
|
|
620
|
+
const description = metadata?.description || `This is the MCP server to interact with the ${appConfig.appName}`;
|
|
621
|
+
const version = metadata?.version || "1.0.0";
|
|
622
|
+
|
|
623
|
+
// Handle entryPoints: convert string to array, with fallback
|
|
624
|
+
const entryPoints = metadata?.entryPoints ? metadata.entryPoints : ["/rest/mcp/streaming"];
|
|
625
|
+
|
|
626
|
+
const packageId = _getPackageID(appConfig.ordNamespace, packageIds, ORD_RESOURCE_TYPE.api, visibility);
|
|
627
|
+
// Fallback: ensure partOfPackage never undefined to satisfy generic apiResources tests
|
|
628
|
+
const effectivePackageId = packageId || `${appConfig.ordNamespace}:package:${visibility}:v1`;
|
|
629
|
+
|
|
630
|
+
return {
|
|
631
|
+
ordId,
|
|
632
|
+
title,
|
|
633
|
+
shortDescription,
|
|
634
|
+
description,
|
|
635
|
+
version,
|
|
636
|
+
lastUpdate: appConfig.lastUpdate,
|
|
637
|
+
visibility,
|
|
638
|
+
partOfPackage: effectivePackageId,
|
|
639
|
+
releaseStatus: "active",
|
|
640
|
+
apiProtocol: "mcp",
|
|
641
|
+
resourceDefinitions: [
|
|
642
|
+
{
|
|
643
|
+
type: "custom",
|
|
644
|
+
customType: MCP_CUSTOM_TYPE,
|
|
645
|
+
mediaType: "application/json",
|
|
646
|
+
url: `/ord/v1/${ordId}/mcp-server-definition.mcp.json`,
|
|
647
|
+
accessStrategies,
|
|
648
|
+
},
|
|
649
|
+
],
|
|
650
|
+
entryPoints,
|
|
651
|
+
extensible: { supported: "no" },
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* This is a template function to create MCP API Resource object(s).
|
|
657
|
+
*
|
|
658
|
+
* The generated MCP API resource(s) can be customized via the generic overwrite mechanism
|
|
659
|
+
* using custom.ord.json. Properties like visibility, title, version, entryPoints, etc.
|
|
660
|
+
* can be overridden by matching the ordId pattern.
|
|
661
|
+
*
|
|
662
|
+
* Metadata values are sourced from the MCP plugin's generateORDMetadata function when available,
|
|
663
|
+
* which reads from cds.env.mcp configuration. This allows users to customize MCP metadata via
|
|
664
|
+
* package.json or .cdsrc.json files.
|
|
665
|
+
*
|
|
666
|
+
* @param {object} appConfig - The application configuration.
|
|
667
|
+
* @param {Array} packageIds - The available package identifiers.
|
|
668
|
+
* @param {Array} accessStrategies The array of accessStrategies objects
|
|
669
|
+
* @returns {Object|Array} An MCP API resource object or array of objects.
|
|
670
|
+
*/
|
|
671
|
+
const createMCPAPIResourceTemplate = (appConfig, packageIds, accessStrategies) => {
|
|
672
|
+
// Get ORD metadata from MCP plugin if available
|
|
673
|
+
let ordMetadata = null;
|
|
674
|
+
// Use comprehensive check for MCP plugin readiness
|
|
675
|
+
const shouldAddMCP = isMCPPluginReady();
|
|
676
|
+
if (shouldAddMCP) {
|
|
677
|
+
try {
|
|
678
|
+
const { generateORDMetadata } = require("@btp-ai/mcp-plugin/lib/utils/metadata");
|
|
679
|
+
ordMetadata = generateORDMetadata();
|
|
680
|
+
} catch (error) {
|
|
681
|
+
Logger.warn("Failed to generate MCP ORD metadata:", error.message);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Handle both array and single object responses from MCP plugin
|
|
686
|
+
if (Array.isArray(ordMetadata)) {
|
|
687
|
+
return ordMetadata.map((metadata) =>
|
|
688
|
+
createSingleMCPResource(metadata, appConfig, packageIds, accessStrategies),
|
|
689
|
+
);
|
|
690
|
+
} else {
|
|
691
|
+
return createSingleMCPResource(ordMetadata || {}, appConfig, packageIds, accessStrategies);
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
|
|
584
695
|
function _propagateORDVisibility(model) {
|
|
585
696
|
for (const [name, def] of Object.entries(model.definitions)) {
|
|
586
697
|
if (def.kind === CDS_ELEMENT_KIND.service && def[ORD_EXTENSIONS_PREFIX + "visibility"]) {
|
|
@@ -606,6 +717,7 @@ module.exports = {
|
|
|
606
717
|
createGroupsTemplateForService,
|
|
607
718
|
createAPIResourceTemplate,
|
|
608
719
|
createEventResourceTemplate,
|
|
720
|
+
createMCPAPIResourceTemplate,
|
|
609
721
|
_getPackageID,
|
|
610
722
|
_getEntityTypeMappings,
|
|
611
723
|
_getExposedEntityTypes,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js/ord",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "CAP Plugin for generating ORD document.",
|
|
5
5
|
"repository": "cap-js/ord",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"workspaces": [
|
|
10
10
|
"xmpl",
|
|
11
11
|
"xmpl_java",
|
|
12
|
-
"__tests__/integration-test-app"
|
|
12
|
+
"__tests__/integration/integration-test-app"
|
|
13
13
|
],
|
|
14
14
|
"main": "cds-plugin.js",
|
|
15
15
|
"files": [
|
|
@@ -20,20 +20,23 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"lint": "npx eslint .",
|
|
23
|
-
"test": "jest --ci --collectCoverage",
|
|
24
|
-
"test:integration": "jest __tests__/integration.test.js --
|
|
23
|
+
"test": "jest __tests__/unit --ci --collectCoverage",
|
|
24
|
+
"test:integration:basic": "jest __tests__/integration/basic-auth.test.js --testPathIgnorePatterns=/node_modules/ --forceExit",
|
|
25
|
+
"test:integration:mtls": "jest __tests__/integration/mtls-auth.test.js --testPathIgnorePatterns=/node_modules/ --forceExit",
|
|
25
26
|
"update-snapshot": "jest --ci --updateSnapshot",
|
|
26
27
|
"cds:version": "cds v -i"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"eslint": "^9.2.0",
|
|
31
|
+
"express": "^4",
|
|
30
32
|
"jest": "^30.0.0",
|
|
31
|
-
"prettier": "3.
|
|
32
|
-
"supertest": "^7.0.0"
|
|
33
|
+
"prettier": "3.7.4",
|
|
34
|
+
"supertest": "^7.0.0",
|
|
35
|
+
"@cap-js/sqlite": "^2",
|
|
36
|
+
"@sap/cds-dk": ">=8.9.5"
|
|
33
37
|
},
|
|
34
38
|
"peerDependencies": {
|
|
35
|
-
"@sap/cds": ">=8.9.4"
|
|
36
|
-
"@sap/cds-dk": ">=8.9.5"
|
|
39
|
+
"@sap/cds": ">=8.9.4"
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
42
|
"@cap-js/asyncapi": "^1.0.3",
|
package/lib/authentication.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
const cds = require("@sap/cds");
|
|
2
|
-
const { AUTHENTICATION_TYPE, BASIC_AUTH_HEADER_KEY, AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP } = require("./constants");
|
|
3
|
-
const { Logger } = require("./logger");
|
|
4
|
-
const bcrypt = require("bcryptjs");
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Compares a plain text password with a hashed password
|
|
8
|
-
* @param {string} password Plain text password to check
|
|
9
|
-
* @param {string} hashedPassword Hashed password to compare against
|
|
10
|
-
* @returns {Promise<boolean>} Promise resolving to true if passwords match, false otherwise
|
|
11
|
-
*/
|
|
12
|
-
async function _comparePassword(password, hashedPassword) {
|
|
13
|
-
if (!password || !hashedPassword) {
|
|
14
|
-
throw new Error("Password and hashed password are required");
|
|
15
|
-
}
|
|
16
|
-
return await bcrypt.compare(password, hashedPassword.replace(/^\$2y/, "$2a"));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Validates if a string is a bcrypt hash
|
|
21
|
-
* @param {string} hash String to validate
|
|
22
|
-
* @returns {boolean} boolean indicating if the string is a bcrypt hash
|
|
23
|
-
*/
|
|
24
|
-
function _isBcryptHash(hash) {
|
|
25
|
-
return /^\$2[ayb]\$\d{2}\$[A-Za-z0-9./]{53}$/.test(hash);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Create authentication configuration based on data given in the environment variables.
|
|
30
|
-
* @returns {Object} Authentication configuration object or default configuration object as a fallback.
|
|
31
|
-
*/
|
|
32
|
-
function createAuthConfig() {
|
|
33
|
-
const defaultAuthConfig = {
|
|
34
|
-
types: [AUTHENTICATION_TYPE.Open],
|
|
35
|
-
accessStrategies: [{ type: AUTHENTICATION_TYPE.Open }],
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
const authConfig = {};
|
|
40
|
-
|
|
41
|
-
authConfig.types = process.env.ORD_AUTH_TYPE
|
|
42
|
-
? [...new Set(JSON.parse(process.env.ORD_AUTH_TYPE))]
|
|
43
|
-
: [...new Set(cds.env.authentication?.types)];
|
|
44
|
-
|
|
45
|
-
if (!authConfig.types || authConfig.types.length === 0) {
|
|
46
|
-
Logger.error("createAuthConfig:", 'No authorization type is provided. Defaulting to "Open" authentication');
|
|
47
|
-
return defaultAuthConfig;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (authConfig.types.some((authType) => !Object.values(AUTHENTICATION_TYPE).includes(authType))) {
|
|
51
|
-
return Object.assign(defaultAuthConfig, { error: "Invalid authentication type" });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
authConfig.types.includes(AUTHENTICATION_TYPE.Open) &&
|
|
56
|
-
authConfig.types.includes(AUTHENTICATION_TYPE.Basic)
|
|
57
|
-
) {
|
|
58
|
-
return Object.assign(defaultAuthConfig, {
|
|
59
|
-
error: "Open authentication cannot be combined with any other authentication type",
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (authConfig.types.includes(AUTHENTICATION_TYPE.Basic)) {
|
|
64
|
-
const credentials = process.env.BASIC_AUTH
|
|
65
|
-
? JSON.parse(process.env.BASIC_AUTH)
|
|
66
|
-
: cds.env.authentication.credentials;
|
|
67
|
-
|
|
68
|
-
// Check all passwords in credentials map
|
|
69
|
-
for (const [username, password] of Object.entries(credentials)) {
|
|
70
|
-
if (!_isBcryptHash(password)) {
|
|
71
|
-
Logger.error("createAuthConfig:", `Password for user "${username}" must be a bcrypt hash`);
|
|
72
|
-
return Object.assign(defaultAuthConfig, { error: "All passwords must be bcrypt hashes" });
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// Store the complete credentials map
|
|
76
|
-
authConfig.credentials = credentials;
|
|
77
|
-
}
|
|
78
|
-
authConfig.accessStrategies = authConfig.types.map((type) => ({
|
|
79
|
-
type: AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP[type],
|
|
80
|
-
}));
|
|
81
|
-
return authConfig;
|
|
82
|
-
} catch (error) {
|
|
83
|
-
return Object.assign(defaultAuthConfig, { error: error.message });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Validate, store authentication configuration in cds.context if undefined, and return the context.
|
|
89
|
-
*/
|
|
90
|
-
function getAuthConfig() {
|
|
91
|
-
if (cds.context?.authConfig) return cds.context?.authConfig;
|
|
92
|
-
|
|
93
|
-
const authConfig = createAuthConfig();
|
|
94
|
-
|
|
95
|
-
if (authConfig.error) {
|
|
96
|
-
Logger.error("Authentication configuration error: " + authConfig.error);
|
|
97
|
-
throw new Error("Invalid authentication configuration");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// set the context
|
|
101
|
-
cds.context = {
|
|
102
|
-
authConfig,
|
|
103
|
-
};
|
|
104
|
-
return cds.context?.authConfig;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Middleware to authenticate the request based on the authentication configuration.
|
|
109
|
-
*/
|
|
110
|
-
async function authenticate(req, res, next) {
|
|
111
|
-
const authConfig = cds.context.authConfig;
|
|
112
|
-
|
|
113
|
-
if (authConfig.types.includes(AUTHENTICATION_TYPE.Open)) {
|
|
114
|
-
res.status(200);
|
|
115
|
-
return next();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
!Object.keys(req.headers).includes(BASIC_AUTH_HEADER_KEY) &&
|
|
120
|
-
authConfig.types.includes(AUTHENTICATION_TYPE.Basic)
|
|
121
|
-
) {
|
|
122
|
-
return res.status(401).setHeader("WWW-Authenticate", 'Basic realm="401"').send("Authentication required.");
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (req.headers[BASIC_AUTH_HEADER_KEY] && authConfig.types.includes(AUTHENTICATION_TYPE.Basic)) {
|
|
126
|
-
const authHeader = req.headers[BASIC_AUTH_HEADER_KEY];
|
|
127
|
-
|
|
128
|
-
if (!authHeader.startsWith("Basic ")) {
|
|
129
|
-
return res.status(401).send("Invalid authentication type");
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const [username, password] = Buffer.from(authHeader.split(" ")[1], "base64").toString().split(":");
|
|
133
|
-
const credentials = authConfig.credentials;
|
|
134
|
-
const storedPassword = credentials[username];
|
|
135
|
-
|
|
136
|
-
if (storedPassword && (await _comparePassword(password, storedPassword))) {
|
|
137
|
-
res.status(200);
|
|
138
|
-
return next();
|
|
139
|
-
} else {
|
|
140
|
-
return res.status(401).send("Invalid credentials");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// In other cases, the request is unauthorized.
|
|
145
|
-
// TODO: Add support for UCL-mTLS authorization.
|
|
146
|
-
return res.status(401).send("Not authorized");
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
module.exports = {
|
|
150
|
-
authenticate,
|
|
151
|
-
createAuthConfig,
|
|
152
|
-
getAuthConfig,
|
|
153
|
-
};
|