@gravity-ui/gateway 4.8.0 → 4.10.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/README.md CHANGED
@@ -7,6 +7,7 @@ A flexible and powerful Express controller for working with REST and gRPC APIs i
7
7
  - [Installation](#installation)
8
8
  - [Basic Usage](#basic-usage)
9
9
  - [Configuration](#configuration)
10
+ - [`proxyHeaders`](#proxyheaders)
10
11
  - [Validation Schema](#validation-schema)
11
12
  - [Using the API in Node.js](#using-the-api-in-nodejs)
12
13
  - [Schema Scopes](#schema-scopes)
@@ -14,13 +15,16 @@ A flexible and powerful Express controller for working with REST and gRPC APIs i
14
15
  - [Overriding Endpoints](#overriding-endpoints)
15
16
  - [Authentication](#authentication)
16
17
  - [Error Handling](#error-handling)
17
- - [gRPC Reflection](#grpc-reflection-for-grpc-actions)
18
18
  - [Retryable Errors](#retryable-errors)
19
+ - [REST-actions](#rest-actions)
20
+ - [gRPC-actions](#grpc-actions)
19
21
  - [Request Cancellation](#request-cancellation)
20
22
  - [Response Content Type Validation](#response-content-type-validation)
23
+ - [gRPC Reflection for gRPC Actions](#grpc-reflection-for-grpc-actions)
21
24
  - [Development](#development)
22
25
  - [Running Tests](#running-tests)
23
26
  - [Contributing](#contributing)
27
+ - [License](#license)
24
28
 
25
29
  ## Installation
26
30
 
@@ -173,10 +177,13 @@ interface GatewayConfig {
173
177
  // List of paths to the necessary proto files for the gateway.
174
178
  includeProtoRoots?: string[];
175
179
 
176
- // Configuration of the path to the certificate in gRPC.
180
+ // Configuration of the path to the CA certificate in gRPC.
177
181
  // Set to null to use system certificates by default.
178
182
  caCertificatePath?: string | null;
179
-
183
+ // Configuration of the path to the client certificate for mTLS in gRPC.
184
+ clientCertificatePath?: string | null;
185
+ // Configuration of the path to the client private key for mTLS in gRPC.
186
+ clientKeyPath?: string | null;
180
187
  // Telemetry sending configuration.
181
188
  sendStats?: SendStats;
182
189
 
@@ -250,6 +257,8 @@ The `extra` parameter contains additional information about the request:
250
257
  - `protopath`: The proto path (for gRPC actions)
251
258
  - `protokey`: The proto key (for gRPC actions)
252
259
 
260
+ When accessing headers, the gateway first tries to get the header with the exact case provided by the user. If the header doesn't exist with that exact case, it falls back to looking for the same header name in lowercase. This behavior ensures compatibility with various HTTP clients that might send headers with different casing.
261
+
253
262
  You can set headers for a specific action using `ApiServiceBaseActionConfig.proxyHeaders`:
254
263
 
255
264
  ```javascript
@@ -312,6 +321,9 @@ const config = {
312
321
  includeProtoRoots: ['...'],
313
322
  timeout: 25000, // default 25 seconds
314
323
  caCertificatePath: '...',
324
+ // Optional: paths for mTLS client certificate and key
325
+ clientCertificatePath: '...',
326
+ clientKeyPath: '...',
315
327
  };
316
328
 
317
329
  const {api: gatewayApi} = getGatewayControllers({root: Schema}, config);
@@ -19,6 +19,6 @@ export interface GrpcContext {
19
19
  credentials: CredentialsMap;
20
20
  }
21
21
  export declare function createRoot(includeGrpcPaths?: string[]): protobufjs.Root;
22
- export declare function getCredentialsMap(caCertificatePath?: string | null): CredentialsMap;
22
+ export declare function getCredentialsMap(caCertificatePath?: string | null, clientCertificatePath?: string | null, clientKeyPath?: string | null): CredentialsMap;
23
23
  export default function createGrpcAction<Context extends GatewayContext>({ root, credentials }: GrpcContext, endpoints: EndpointsConfig | undefined, config: ApiServiceGrpcActionConfig<Context, any, any>, serviceKey: string, actionName: string, options: GatewayApiOptions<Context>, ErrorConstructor: AppErrorConstructor, serviceSchema?: Pick<BaseSchema[string], 'getAuthHeaders'>): (actionConfig: ApiActionConfig<Context, any, any>) => Promise<import("../models/common").GatewayActionClientStreamResponse<any> | import("../models/common").GatewayActionServerStreamResponse<any> | import("../models/common").GatewayActionDuplexStreamResponse<any> | import("../models/common").GatewayActionUnaryResponse<any>>;
24
24
  export {};
@@ -59,13 +59,21 @@ function createRoot(includeGrpcPaths) {
59
59
  return root;
60
60
  }
61
61
  exports.createRoot = createRoot;
62
- function getCredentialsMap(caCertificatePath) {
62
+ function getCredentialsMap(caCertificatePath, clientCertificatePath, clientKeyPath) {
63
63
  let certificate;
64
+ let clientCertificate;
65
+ let clientKey;
64
66
  if (caCertificatePath && fs_1.default.existsSync(caCertificatePath)) {
65
67
  certificate = fs_1.default.readFileSync(caCertificatePath);
66
68
  }
69
+ if (clientCertificatePath && fs_1.default.existsSync(clientCertificatePath)) {
70
+ clientCertificate = fs_1.default.readFileSync(clientCertificatePath);
71
+ }
72
+ if (clientKeyPath && fs_1.default.existsSync(clientKeyPath)) {
73
+ clientKey = fs_1.default.readFileSync(clientKeyPath);
74
+ }
67
75
  return {
68
- secure: grpc.ChannelCredentials.createSsl(certificate),
76
+ secure: grpc.ChannelCredentials.createSsl(certificate, clientKey, clientCertificate),
69
77
  secureWithoutRootCert: grpc.ChannelCredentials.createSsl(),
70
78
  insecure: grpc.ChannelCredentials.createInsecure(),
71
79
  };
@@ -108,7 +116,10 @@ function createMetadata({ options, actionConfig, config, params, serviceName, pr
108
116
  }
109
117
  for (const headerName of proxyHeaders) {
110
118
  if (metadata[headerName] === undefined) {
111
- metadata[headerName] = headers[headerName];
119
+ metadata[headerName] =
120
+ headers[headerName] !== undefined
121
+ ? headers[headerName]
122
+ : headers[headerName.toLowerCase()];
112
123
  }
113
124
  }
114
125
  const authHeaders = ((_b = (_a = config.getAuthHeaders) !== null && _a !== void 0 ? _a : serviceSchema === null || serviceSchema === void 0 ? void 0 : serviceSchema.getAuthHeaders) !== null && _b !== void 0 ? _b : options.getAuthHeaders)({
@@ -170,12 +181,37 @@ function clearInstancesCache(service, instancesMap, cachePath, closeTimeout, ctx
170
181
  function getChannelCredential(config, endpointData, credentials) {
171
182
  let endpointInsecure;
172
183
  let endpointSecureWithoutRootCert;
184
+ let endpointCaCertificatePath;
185
+ let endpointClientCertificatePath;
186
+ let endpointClientKeyPath;
173
187
  if ((0, common_2.isExtendedGrpcActionEndpoint)(endpointData)) {
174
188
  endpointInsecure = endpointData === null || endpointData === void 0 ? void 0 : endpointData.insecure;
175
189
  endpointSecureWithoutRootCert = endpointData === null || endpointData === void 0 ? void 0 : endpointData.secureWithoutRootCert;
190
+ endpointCaCertificatePath = endpointData === null || endpointData === void 0 ? void 0 : endpointData.caCertificatePath;
191
+ endpointClientCertificatePath = endpointData === null || endpointData === void 0 ? void 0 : endpointData.clientCertificatePath;
192
+ endpointClientKeyPath = endpointData === null || endpointData === void 0 ? void 0 : endpointData.clientKeyPath;
176
193
  }
177
194
  const isInsecure = config.insecure || endpointInsecure;
178
195
  const isSecureWithoutRootCert = config.secureWithoutRootCert || endpointSecureWithoutRootCert;
196
+ // If endpoint-specific certificates are provided, create new credentials
197
+ if (endpointCaCertificatePath || endpointClientCertificatePath || endpointClientKeyPath) {
198
+ let certificate;
199
+ let clientCertificate;
200
+ let clientKey;
201
+ const caCertPath = endpointCaCertificatePath || config.caCertificatePath;
202
+ const clientCertPath = endpointClientCertificatePath || config.clientCertificatePath;
203
+ const clientKeyPath = endpointClientKeyPath || config.clientKeyPath;
204
+ if (caCertPath && fs_1.default.existsSync(caCertPath)) {
205
+ certificate = fs_1.default.readFileSync(caCertPath);
206
+ }
207
+ if (clientCertPath && fs_1.default.existsSync(clientCertPath)) {
208
+ clientCertificate = fs_1.default.readFileSync(clientCertPath);
209
+ }
210
+ if (clientKeyPath && fs_1.default.existsSync(clientKeyPath)) {
211
+ clientKey = fs_1.default.readFileSync(clientKeyPath);
212
+ }
213
+ return grpc.ChannelCredentials.createSsl(certificate, clientKey, clientCertificate);
214
+ }
179
215
  let creds = credentials.secure;
180
216
  if (isInsecure) {
181
217
  creds = credentials.insecure;
@@ -133,7 +133,10 @@ function createRestAction(endpoints, config, serviceKey, actionName, options, Er
133
133
  }
134
134
  for (const headerName of proxyHeaders) {
135
135
  if (actionHeaders[headerName] === undefined) {
136
- actionHeaders[headerName] = requestHeaders[headerName];
136
+ actionHeaders[headerName] =
137
+ requestHeaders[headerName] !== undefined
138
+ ? requestHeaders[headerName]
139
+ : requestHeaders[headerName.toLowerCase()];
137
140
  }
138
141
  }
139
142
  if (requestId) {
package/build/index.js CHANGED
@@ -259,7 +259,7 @@ function getGatewayControllers(schemasByScope, config) {
259
259
  console.warn('Error when parse GATEWAY_ENDPOINTS_OVERRIDES', err);
260
260
  }
261
261
  }
262
- const credentials = (0, grpc_1.getCredentialsMap)(config.caCertificatePath);
262
+ const credentials = (0, grpc_1.getCredentialsMap)(config.caCertificatePath, config.clientCertificatePath, config.clientKeyPath);
263
263
  for (const scope of (0, common_1.getKeys)(schemasByScope)) {
264
264
  apiByScope[scope] = generateGatewayApi(schemasByScope[scope], config, { root: (0, grpc_1.createRoot)(config.includeProtoRoots), credentials }, apiByScope);
265
265
  }
@@ -118,6 +118,9 @@ export interface ExtendedBaseActionEndpoint {
118
118
  export interface ExtendedGrpcActionEndpoint extends ExtendedBaseActionEndpoint {
119
119
  insecure?: boolean;
120
120
  secureWithoutRootCert?: boolean;
121
+ caCertificatePath?: string;
122
+ clientCertificatePath?: string;
123
+ clientKeyPath?: string;
121
124
  grpcOptions?: object;
122
125
  }
123
126
  export interface ExtendedRestActionEndpoint extends ExtendedBaseActionEndpoint {
@@ -154,6 +157,9 @@ export interface ApiServiceBaseGrpcActionConfig<Context extends GatewayContext,
154
157
  protoKey: string;
155
158
  insecure?: boolean;
156
159
  secureWithoutRootCert?: boolean;
160
+ caCertificatePath?: string;
161
+ clientCertificatePath?: string;
162
+ clientKeyPath?: string;
157
163
  encodedFields?: string[];
158
164
  type?: HandlerType;
159
165
  decodeAnyMessageProtoLoaderOptions?: protobufjs.IConversionOptions;
@@ -274,6 +280,8 @@ export interface GatewayConfig<Context extends GatewayContext, Req extends Gatew
274
280
  sendStats?: SendStats<Context>;
275
281
  includeProtoRoots?: string[];
276
282
  caCertificatePath: string | null;
283
+ clientCertificatePath?: string | null;
284
+ clientKeyPath?: string | null;
277
285
  proxyHeaders: ProxyHeaders;
278
286
  proxyDebugHeaders?: ProxyHeaders;
279
287
  withDebugHeaders?: boolean | ((req: Req, res: Res) => boolean);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/gateway",
3
- "version": "4.8.0",
3
+ "version": "4.10.0",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "main": "build/index.js",