Skip to content

Commit

Permalink
Ying/eh (#572)
Browse files Browse the repository at this point in the history
* remove env

* change electron helper
  • Loading branch information
YingXue authored Nov 8, 2022
1 parent ca0beed commit 527f6ba
Showing 1 changed file with 125 additions and 82 deletions.
207 changes: 125 additions & 82 deletions public/utils/connStringHelper.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,126 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/***********************************************************
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License
**********************************************************/
import { AmqpError, isAmqpError, Connection, ReceiverEvents, parseConnectionString } from "rhea-promise";
import * as crypto from "crypto";

// The following helper functions are directly copied from:
// https://raw.githubusercontent.com/Azure/azure-sdk-for-js/main/sdk/eventhub/event-hubs/samples/v5/typescript/src/iothubConnectionString.ts
export const convertIotHubToEventHubsConnectionString = async (connectionString: string): Promise<string> =>
{
const { HostName, SharedAccessKeyName, SharedAccessKey } = parseConnectionString<{
HostName: string;
SharedAccessKeyName: string;
SharedAccessKey: string;
}>(connectionString);

// Verify that the required info is in the connection string.
if (!HostName || !SharedAccessKey || !SharedAccessKeyName) {
throw new Error(`Invalid IotHub connection string.`);
}

//Extract the IotHub name from the hostname.
const [iotHubName] = HostName.split(".");

if (!iotHubName) {
throw new Error(`Unable to extract the IotHub name from the connection string.`);
}

// Generate a token to authenticate to the service.
// The code for generateSasToken can be found at https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#security-tokens
const token = generateSasToken(`${HostName}/messages/events`, SharedAccessKey, SharedAccessKeyName, 5);

const connection = new Connection({
transport: "tls",
host: HostName,
hostname: HostName,
username: `${SharedAccessKeyName}@sas.root.${iotHubName}`,
port: 5671,
reconnect: false,
password: token
});
await connection.open();

// Create the receiver that will trigger a redirect error.
const receiver = await connection.createReceiver({ source: { address: `amqps://${HostName}/messages/events/$management` }});

return new Promise((resolve, reject) => {
receiver.on(ReceiverEvents.receiverError, (context) => {
const error = context.receiver && context.receiver.error;
if (isAmqpError(error) && (error as AmqpError).condition === "amqp:link:redirect") {
const hostname = (error as AmqpError).info?.hostname;
if (!hostname) {
reject(error);
} else {
resolve(`Endpoint=sb://${hostname}/;EntityPath=${iotHubName};SharedAccessKeyName=${SharedAccessKeyName};SharedAccessKey=${SharedAccessKey}`);
}
} else {
reject(error);
}
connection.close().catch(() => {
/* ignore error */
});
});
});
}

// This code copied from event hub's sample
// https://raw.githubusercontent.com/Azure/azure-sdk-for-js/main/sdk/eventhub/event-hubs/samples/v5/typescript/src/iothubConnectionString.ts
const generateSasToken = (resourceUri: string, signingKey: string, policyName: string, expiresInMins: number): string => {
resourceUri = encodeURIComponent(resourceUri);
const expiresInSeconds = Math.ceil(Date.now() / 1000 + expiresInMins * 60);
const toSign = resourceUri + "\n" + expiresInSeconds;

// Use the crypto module to create the hmac.
const hmac = crypto.createHmac("sha256", Buffer.from(signingKey, "base64"));
hmac.update(toSign);
const base64UriEncoded = encodeURIComponent(hmac.digest("base64"));

// Construct authorization string.
return `SharedAccessSignature sr=${resourceUri}&sig=${base64UriEncoded}&se=${expiresInSeconds}&skn=${policyName}`;
}
/**
* @summary Demonstrates how to convert an IoT Hub connection string to an Event Hubs connection string that points to the built-in messaging endpoint.
*/

/*
* The Event Hubs connection string is then used with the EventHubConsumerClient to receive events.
*
* More information about the built-in messaging endpoint can be found at:
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-messages-read-builtin
*/

import * as crypto from "crypto";
import { Buffer } from "buffer";
import { AmqpError, Connection, ReceiverEvents, parseConnectionString } from "rhea-promise";
import * as rheaPromise from "rhea-promise";
import { ErrorNameConditionMapper as AMQPError } from "@azure/core-amqp";

/**
* Type guard for AmqpError.
* @param err - An unknown error.
*/
function isAmqpError(err: any): err is AmqpError {
return rheaPromise.isAmqpError(err);
}

// This code is modified from https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#security-tokens.
function generateSasToken(
resourceUri: string,
signingKey: string,
policyName: string,
expiresInMins: number
): string {
resourceUri = encodeURIComponent(resourceUri);

const expiresInSeconds = Math.ceil(Date.now() / 1000 + expiresInMins * 60);
const toSign = resourceUri + "\n" + expiresInSeconds;

// Use the crypto module to create the hmac.
const hmac = crypto.createHmac("sha256", Buffer.from(signingKey, "base64"));
hmac.update(toSign);
const base64UriEncoded = encodeURIComponent(hmac.digest("base64"));

// Construct authorization string.
return `SharedAccessSignature sr=${resourceUri}&sig=${base64UriEncoded}&se=${expiresInSeconds}&skn=${policyName}`;
}

/**
* Converts an IotHub Connection string into an Event Hubs-compatible connection string.
* @param connectionString - An IotHub connection string in the format:
* `"HostName=<your-iot-hub>.azure-devices.net;SharedAccessKeyName=<KeyName>;SharedAccessKey=<Key>"`
* @returns An Event Hubs-compatible connection string in the format:
* `"Endpoint=sb://<hostname>;EntityPath=<your-iot-hub>;SharedAccessKeyName=<KeyName>;SharedAccessKey=<Key>"`
*/
export async function convertIotHubToEventHubsConnectionString(connectionString: string): Promise<string> {
const { HostName, SharedAccessKeyName, SharedAccessKey } = parseConnectionString<{
HostName: string;
SharedAccessKeyName: string;
SharedAccessKey: string;
}>(connectionString);

// Verify that the required info is in the connection string.
if (!HostName || !SharedAccessKey || !SharedAccessKeyName) {
throw new Error(`Invalid IotHub connection string.`);
}

//Extract the IotHub name from the hostname.
const [iotHubName] = HostName.split(".");

if (!iotHubName) {
throw new Error(`Unable to extract the IotHub name from the connection string.`);
}

// Generate a token to authenticate to the service.
// The code for generateSasToken can be found at https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#security-tokens
const token = generateSasToken(
`${HostName}/messages/events`,
SharedAccessKey,
SharedAccessKeyName,
5 // token expires in 5 minutes
);

const connection = new Connection({
transport: "tls",
host: HostName,
hostname: HostName,
username: `${SharedAccessKeyName}@sas.root.${iotHubName}`,
port: 5671,
reconnect: false,
password: token
});
await connection.open();

// Create the receiver that will trigger a redirect error.
const receiver = await connection.createReceiver({
source: { address: `amqps://${HostName}/messages/events/$management` },
});

return new Promise((resolve, reject) => {
receiver.on(ReceiverEvents.receiverError, (context) => {
const error = context.receiver && context.receiver.error;
if (isAmqpError(error) && error.condition === AMQPError.LinkRedirectError && error.info) {
const hostname = error.info.hostname;
// an example: "amqps://iothub.test-1234.servicebus.windows.net:5671/hub-name/$management"
const iotAddress = error.info.address;
const regex = /:\d+\/(.*)\/\$management/i;
const regexResults = regex.exec(iotAddress);
if (!hostname || !regexResults) {
reject(error);
} else {
const eventHubName = regexResults[1];
resolve(
`Endpoint=sb://${hostname}/;EntityPath=${eventHubName};SharedAccessKeyName=${SharedAccessKeyName};SharedAccessKey=${SharedAccessKey}`
);
}
} else {
reject(error);
}
connection.close().catch(() => {
/* ignore error */
});
});
});
}

0 comments on commit 527f6ba

Please sign in to comment.