UNPKG

aws-crt

Version:

NodeJS/browser bindings to the aws-c-* libraries

421 lines 18.3 kB
"use strict"; /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AwsIotMqttConnectionConfigBuilder = void 0; const mqtt_1 = require("../common/mqtt"); const io = __importStar(require("./io")); const io_1 = require("./io"); const platform = __importStar(require("../common/platform")); const error_1 = require("./error"); const auth_1 = require("./auth"); const iot_shared = __importStar(require("../common/aws_iot_shared")); /** * Builder functions to create a {@link MqttConnectionConfig} which can then be used to create * a {@link MqttClientConnection}, configured for use with AWS IoT. * * @category IoT */ class AwsIotMqttConnectionConfigBuilder { constructor(tls_ctx_options) { this.tls_ctx_options = tls_ctx_options; this.params = { client_id: '', host_name: '', socket_options: new io.SocketOptions(), port: 8883, use_websocket: false, clean_session: false, keep_alive: undefined, will: undefined, username: "", password: undefined, tls_ctx: undefined, reconnect_min_sec: mqtt_1.DEFAULT_RECONNECT_MIN_SEC, reconnect_max_sec: mqtt_1.DEFAULT_RECONNECT_MAX_SEC }; this.is_using_custom_authorizer = false; } /** * Create a new builder with mTLS file paths * @param cert_path - Path to certificate, in PEM format * @param key_path - Path to private key, in PEM format */ static new_mtls_builder_from_path(cert_path, key_path) { let builder = new AwsIotMqttConnectionConfigBuilder(io_1.TlsContextOptions.create_client_with_mtls_from_path(cert_path, key_path)); builder.params.port = 8883; if (io.is_alpn_available()) { builder.tls_ctx_options.alpn_list.unshift('x-amzn-mqtt-ca'); } return builder; } /** * Create a new builder with mTLS cert pair in memory * @param cert - Certificate, in PEM format * @param private_key - Private key, in PEM format */ static new_mtls_builder(cert, private_key) { let builder = new AwsIotMqttConnectionConfigBuilder(io_1.TlsContextOptions.create_client_with_mtls(cert, private_key)); builder.params.port = 8883; if (io.is_alpn_available()) { builder.tls_ctx_options.alpn_list.unshift('x-amzn-mqtt-ca'); } return builder; } /** * Create a new builder with mTLS using a PKCS#11 library for private key operations. * * NOTE: This configuration only works on Unix devices. * @param pkcs11_options - PKCS#11 options. */ static new_mtls_pkcs11_builder(pkcs11_options) { let builder = new AwsIotMqttConnectionConfigBuilder(io_1.TlsContextOptions.create_client_with_mtls_pkcs11(pkcs11_options)); builder.params.port = 8883; if (io.is_alpn_available()) { builder.tls_ctx_options.alpn_list.unshift('x-amzn-mqtt-ca'); } return builder; } /** * Create a new builder with mTLS using a certificate in a Windows certificate store. * * NOTE: This configuration only works on Windows devices. * @param certificate_path - Path to certificate in a Windows certificate store. * The path must use backslashes and end with the certificate's thumbprint. * Example: `CurrentUser\MY\A11F8A9B5DF5B98BA3508FBCA575D09570E0D2C6` */ static new_mtls_windows_cert_store_path_builder(certificate_path) { let builder = new AwsIotMqttConnectionConfigBuilder(io_1.TlsContextOptions.create_client_with_mtls_windows_cert_store_path(certificate_path)); builder.params.port = 8883; if (io.is_alpn_available()) { builder.tls_ctx_options.alpn_list.unshift('x-amzn-mqtt-ca'); } return builder; } /** * Creates a new builder with default Tls options. This requires setting the connection details manually. */ static new_default_builder() { let ctx_options = new io.TlsContextOptions(); let builder = new AwsIotMqttConnectionConfigBuilder(ctx_options); return builder; } static new_websocket_builder(...args) { return this.new_with_websockets(...args); } static configure_websocket_handshake(builder, options) { if (options) { if (builder == null || builder == undefined) { throw new error_1.CrtError("AwsIotMqttConnectionConfigBuilder configure_websocket_handshake: builder not defined"); } builder.params.websocket_handshake_transform = (request, done) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const signing_config = (_b = (_a = options.create_signing_config) === null || _a === void 0 ? void 0 : _a.call(options)) !== null && _b !== void 0 ? _b : { algorithm: auth_1.AwsSigningAlgorithm.SigV4, signature_type: auth_1.AwsSignatureType.HttpRequestViaQueryParams, provider: options.credentials_provider, region: options.region, service: (_c = options.service) !== null && _c !== void 0 ? _c : "iotdevicegateway", signed_body_value: auth_1.AwsSignedBodyValue.EmptySha256, omit_session_token: true, }; try { yield (0, auth_1.aws_sign_request)(request, signing_config); done(); } catch (error) { if (error instanceof error_1.CrtError) { done(error.error_code); } else { done(3); /* TODO: AWS_ERROR_UNKNOWN */ } } }); } return builder; } /** * Configures the connection to use MQTT over websockets. Forces the port to 443. */ static new_with_websockets(options) { let tls_ctx_options = options === null || options === void 0 ? void 0 : options.tls_ctx_options; if (!tls_ctx_options) { tls_ctx_options = new io_1.TlsContextOptions(); tls_ctx_options.alpn_list = []; } let builder = new AwsIotMqttConnectionConfigBuilder(tls_ctx_options); builder.params.use_websocket = true; builder.params.proxy_options = options === null || options === void 0 ? void 0 : options.proxy_options; if (builder.tls_ctx_options) { builder.params.port = 443; } this.configure_websocket_handshake(builder, options); return builder; } /** * Overrides the default system trust store. * @param ca_dirpath - Only used on Unix-style systems where all trust anchors are * stored in a directory (e.g. /etc/ssl/certs). * @param ca_filepath - Single file containing all trust CAs, in PEM format */ with_certificate_authority_from_path(ca_dirpath, ca_filepath) { this.tls_ctx_options.override_default_trust_store_from_path(ca_dirpath, ca_filepath); return this; } /** * Overrides the default system trust store. * @param ca - Buffer containing all trust CAs, in PEM format */ with_certificate_authority(ca) { this.tls_ctx_options.override_default_trust_store(ca); return this; } /** * Configures the IoT endpoint for this connection * @param endpoint The IoT endpoint to connect to */ with_endpoint(endpoint) { this.params.host_name = endpoint; return this; } /** * The port to connect to on the IoT endpoint * @param port The port to connect to on the IoT endpoint. Usually 8883 for MQTT, or 443 for websockets */ with_port(port) { this.params.port = port; return this; } /** * Configures the client_id to use to connect to the IoT Core service * @param client_id The client id for this connection. Needs to be unique across all devices/clients. */ with_client_id(client_id) { this.params.client_id = client_id; return this; } /** * Determines whether or not the service should try to resume prior subscriptions, if it has any * @param clean_session true if the session should drop prior subscriptions when this client connects, false to resume the session */ with_clean_session(clean_session) { this.params.clean_session = clean_session; return this; } /** * Configures MQTT keep-alive via PING messages. Note that this is not TCP keepalive. * @param keep_alive How often in seconds to send an MQTT PING message to the service to keep the connection alive */ with_keep_alive_seconds(keep_alive) { this.params.keep_alive = keep_alive; return this; } /** * Configures the TCP socket timeout (in milliseconds) * @param timeout_ms TCP socket timeout * @deprecated */ with_timeout_ms(timeout_ms) { this.with_ping_timeout_ms(timeout_ms); return this; } /** * Configures the PINGREQ response timeout (in milliseconds) * @param ping_timeout PINGREQ response timeout */ with_ping_timeout_ms(ping_timeout) { this.params.ping_timeout = ping_timeout; return this; } /** * Configures the protocol operation timeout (in milliseconds) * @param protocol_operation_timeout protocol operation timeout */ with_protocol_operation_timeout_ms(protocol_operation_timeout) { this.params.protocol_operation_timeout = protocol_operation_timeout; return this; } /** * Configures the will message to be sent when this client disconnects * @param will The will topic, qos, and message */ with_will(will) { this.params.will = will; return this; } /** * Configures the common settings for the socket to use when opening a connection to the server * @param socket_options The socket settings */ with_socket_options(socket_options) { this.params.socket_options = socket_options; return this; } /** * Configures AWS credentials (usually from Cognito) for this connection * @param aws_region The service region to connect to * @param aws_access_id IAM Access ID * @param aws_secret_key IAM Secret Key * @param aws_sts_token STS token from Cognito (optional) */ with_credentials(aws_region, aws_access_id, aws_secret_key, aws_sts_token) { return AwsIotMqttConnectionConfigBuilder.configure_websocket_handshake(this, { credentials_provider: auth_1.AwsCredentialsProvider.newStatic(aws_access_id, aws_secret_key, aws_sts_token), region: aws_region, service: "iotdevicegateway", }); } /** * Configure the http proxy options to use to establish the connection * @param proxy_options proxy options to use to establish the mqtt connection */ with_http_proxy_options(proxy_options) { this.params.proxy_options = proxy_options; return this; } /** * Sets the custom authorizer settings. This function will modify the username, port, and TLS options. * * @param username The username to use with the custom authorizer. If an empty string is passed, it will * check to see if a username has already been set (via WithUsername function). If no * username is set then no username will be passed with the MQTT connection. * @param authorizerName The name of the custom authorizer. If an empty string is passed, then * 'x-amz-customauthorizer-name' will not be added with the MQTT connection. * @param authorizerSignature The signature of the custom authorizer. If an empty string is passed, then * 'x-amz-customauthorizer-signature' will not be added with the MQTT connection. * @param password The password to use with the custom authorizer. If null is passed, then no password will * be set. */ with_custom_authorizer(username, authorizer_name, authorizer_signature, password) { this.is_using_custom_authorizer = true; let username_string = iot_shared.populate_username_string_with_custom_authorizer("", username, authorizer_name, authorizer_signature, this.params.username); this.params.username = username_string; this.params.password = password; if (!this.params.use_websocket) { this.tls_ctx_options.alpn_list = ["mqtt"]; } this.params.port = 443; return this; } /** * Sets username for the connection * * @param username the username that will be passed with the MQTT connection */ with_username(username) { this.params.username = username; return this; } /** * Sets password for the connection * * @param password the password that will be passed with the MQTT connection */ with_password(password) { this.params.password = password; return this; } /** * Configure the max reconnection period (in second). The reonnection period will * be set in range of [reconnect_min_sec,reconnect_max_sec]. * @param reconnect_max_sec max reconnection period */ with_reconnect_max_sec(max_sec) { this.params.reconnect_max_sec = max_sec; return this; } /** * Configure the min reconnection period (in second). The reonnection period will * be set in range of [reconnect_min_sec,reconnect_max_sec]. * @param reconnect_min_sec min reconnection period */ with_reconnect_min_sec(min_sec) { this.params.reconnect_min_sec = min_sec; return this; } /** * Returns the configured MqttConnectionConfig. On the first invocation of this function, the TLS context is cached * and re-used on all subsequent calls to build(). * @returns The configured MqttConnectionConfig */ build() { var _a, _b, _c; if (this.params.client_id === undefined || this.params.host_name === undefined) { throw 'client_id and endpoint are required'; } // Check to see if a custom authorizer is being used but not through the builder if (this.is_using_custom_authorizer == false) { if (iot_shared.is_string_and_not_empty(this.params.username)) { if (((_a = this.params.username) === null || _a === void 0 ? void 0 : _a.indexOf("x-amz-customauthorizer-name=")) != -1 || ((_b = this.params.username) === null || _b === void 0 ? void 0 : _b.indexOf("x-amz-customauthorizer-signature=")) != -1) { this.is_using_custom_authorizer = true; } } } // Is the user trying to connect using a custom authorizer? if (this.is_using_custom_authorizer == true) { if (this.params.port != 443) { console.log("Warning: Attempting to connect to authorizer with unsupported port. Port is not 443..."); } } /* * By caching and reusing the TLS context we get an enormous memory savings on a per-connection basis. * The tradeoff is that you can't modify TLS options in between calls to build. * Previously we were making a new one with every single connection which had a huge negative impact on large * scale tests. */ if (this.params.tls_ctx === undefined) { this.params.tls_ctx = new io.ClientTlsContext(this.tls_ctx_options); } // Add the metrics string if (iot_shared.is_string_and_not_empty(this.params.username) == false) { this.params.username = "?SDK=NodeJSv2&Version="; } else { if (((_c = this.params.username) === null || _c === void 0 ? void 0 : _c.indexOf("?")) != -1) { this.params.username += "&SDK=NodeJSv2&Version="; } else { this.params.username += "?SDK=NodeJSv2&Version="; } } this.params.username += platform.crt_version(); return this.params; } } exports.AwsIotMqttConnectionConfigBuilder = AwsIotMqttConnectionConfigBuilder; //# sourceMappingURL=aws_iot.js.map