This commit is contained in:
Ramya Rao 2020-11-02 11:48:34 -08:00 коммит произвёл GitHub
Родитель 937f4b5012
Коммит 54247fdd1b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
42 изменённых файлов: 400 добавлений и 759 удалений

Просмотреть файл

@ -4,8 +4,39 @@
- `AmqpAnnotatedMessage` interface that closely represents the AMQP annotated message from the [AMQP spec](https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#section-message-format) has been added. New `AmqpMessageHeaders` and `AmqpMessageProperties` interfaces(properties with camelCasing) have been added in the place of re-exports from "rhea" library(properties with snake_casing).
[PR 12091](https://github.com/Azure/azure-sdk-for-js/pull/12091)
- `Message` from "rhea" library which was being exported as `AmqpMessage` is removed.
[PR 12169](https://github.com/Azure/azure-sdk-for-js/pull/12169)
### Breaking changes
We are cleaning the public API surface by
- removing exports that are either not used by either `@azure/event-hubs` and `@azure/service-bus` packages (which are the two main consumers of this package)
- AsyncLockOptions
- executePromisesSequentially
- Func
- getNewAsyncLock
- isNode
- randomNumberFromInterval
- Timeout
- moving the clases/methods/interfaces that are very specific to Event Hubs/Service Bus to their corresponding packages.
- SharedKeyCredential
- As part of this move the `negotiateClaim()` method now takes the token string directly instead of the `AccessToken` object.
- EventHubConnectionConfig
- avoid re-exporting things from `rhea-promise` and `@azure/core-auth`
- Dictionary
- isAmqpError
- Message
- TokenCredential
- isTokenCredential
- AccessToken
- removing all IotHub related artifacts. These existed to support the IotHub support we had in Event Hubs v2 which has since been removed in Event Hubs v5 for a better separation of concerns
- IotHubConnectionConfig
- IotHubConnectionStringModel
- IotSharedKeyCredential
- isIotHubConnectionString
- removing all Event Hubs, Storage and Service Bus interfaces meant to be used with the `parseConnectionString()` method
- ServiceBusConnectionStringModel
- StorageConnectionStringModel
- EventHubsConnectionStringModel
## 1.1.7 (2020-10-28)

Просмотреть файл

@ -69,7 +69,6 @@
},
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.1.3",
"@azure/logger": "^1.0.0",
"@types/async-lock": "^1.1.0",
"@types/is-buffer": "^2.0.0",

Просмотреть файл

@ -5,13 +5,9 @@
```ts
import { AbortSignalLike } from '@azure/abort-controller';
import { AccessToken } from '@azure/core-auth';
import { AmqpError } from 'rhea-promise';
import AsyncLock from 'async-lock';
import { Connection } from 'rhea-promise';
import { Dictionary } from 'rhea-promise';
import { isAmqpError } from 'rhea-promise';
import { isTokenCredential } from '@azure/core-auth';
import { Message } from 'rhea-promise';
import { MessageHeader } from 'rhea-promise';
import { MessageProperties } from 'rhea-promise';
@ -21,11 +17,8 @@ import { ReqResLink } from 'rhea-promise';
import { Sender } from 'rhea-promise';
import { SenderOptions } from 'rhea-promise';
import { Session } from 'rhea-promise';
import { TokenCredential } from '@azure/core-auth';
import { WebSocketImpl } from 'rhea-promise';
export { AccessToken }
// @public
export interface AmqpAnnotatedMessage {
applicationProperties?: {
@ -89,14 +82,6 @@ export const AmqpMessageProperties: {
export { AsyncLock }
// @public
export interface AsyncLockOptions {
domainReentrant?: boolean;
maxPending?: number;
Promise?: any;
timeout?: number;
}
// @public
export class CbsClient {
constructor(connection: Connection, connectionLock: string);
@ -106,7 +91,7 @@ export class CbsClient {
readonly connectionLock: string;
readonly endpoint: string;
init(): Promise<void>;
negotiateClaim(audience: string, tokenObject: AccessToken, tokenType: TokenType): Promise<CbsResponse>;
negotiateClaim(audience: string, token: string, tokenType: TokenType): Promise<CbsResponse>;
remove(): void;
readonly replyTo: string;
}
@ -250,7 +235,6 @@ export interface ConnectionContextBase {
dataTransformer: DataTransformer;
negotiateClaimLock: string;
refreshConnection: () => void;
readonly tokenCredential: SharedKeyCredential | TokenCredential;
wasConnectionCloseCalled: boolean;
}
@ -392,7 +376,6 @@ export interface CreateConnectionContextBaseParameters {
dataTransformer?: DataTransformer;
isEntityPathRequired?: boolean;
operationTimeoutInMs?: number;
tokenCredential?: SharedKeyCredential | TokenCredential;
}
// @public
@ -413,8 +396,6 @@ export const defaultLock: AsyncLock;
// @public
export function delay<T>(delayInMs: number, abortSignal?: AbortSignalLike, abortErrorMsg?: string, value?: T): Promise<T>;
export { Dictionary }
// @public
export enum ErrorNameConditionMapper {
AddressAlreadyInUseError = "com.microsoft:address-already-in-use",
@ -464,98 +445,12 @@ export enum ErrorNameConditionMapper {
UnauthorizedError = "amqp:unauthorized-access"
}
// @public
export interface EventHubConnectionConfig extends ConnectionConfig {
entityPath: string;
getManagementAddress(): string;
getManagementAudience(): string;
getReceiverAddress(partitionId: string | number, consumergroup?: string): string;
getReceiverAudience(partitionId: string | number, consumergroup?: string): string;
getSenderAddress(partitionId?: string | number): string;
getSenderAudience(partitionId?: string | number): string;
}
// @public
export const EventHubConnectionConfig: {
create(connectionString: string, path?: string | undefined): EventHubConnectionConfig;
createFromConnectionConfig(config: ConnectionConfig): EventHubConnectionConfig;
validate(config: EventHubConnectionConfig): void;
};
// @public
export interface EventHubConnectionStringModel {
// (undocumented)
[x: string]: any;
// (undocumented)
Endpoint: string;
// (undocumented)
EntityPath?: string;
// (undocumented)
SharedAccessKey: string;
// (undocumented)
SharedAccessKeyName: string;
}
// @public (undocumented)
export function executePromisesSequentially(promiseFactories: Array<any>, kickstart?: any): Promise<any>;
// @public
export type Func<T, V> = (a: T) => V;
// @public
export function getNewAsyncLock(options?: AsyncLockOptions): AsyncLock;
// @public (undocumented)
export interface IotHubConnectionConfig {
connectionString: string;
deviceId?: string;
entityPath: string;
host: string;
hostName: string;
sharedAccessKey: string;
sharedAccessKeyName: string;
}
// @public
export const IotHubConnectionConfig: {
create(connectionString: string, path?: string | undefined): IotHubConnectionConfig;
validate(config: IotHubConnectionConfig): void;
convertToEventHubConnectionConfig(iotHubConfig: IotHubConnectionConfig): EventHubConnectionConfig;
};
// @public
export interface IotHubConnectionStringModel {
// (undocumented)
DeviceId?: string;
// (undocumented)
HostName: string;
// (undocumented)
SharedAccessKey: string;
// (undocumented)
SharedAccessKeyName: string;
}
// @public
export class IotSharedKeyCredential extends SharedKeyCredential {
getToken(audience: string): AccessToken;
}
export { isAmqpError }
// @public
export function isIotHubConnectionString(connectionString: string): boolean;
// @public
export function isMessagingError(error: Error | MessagingError): error is MessagingError;
// @public
export const isNode: boolean;
// @public
export function isSystemError(err: any): err is NetworkSystemError;
export { isTokenCredential }
// @public
export const logger: import("@azure/logger").AzureLogger;
@ -605,9 +500,6 @@ export type ParsedOutput<T> = {
[P in keyof T]: T[P];
};
// @public
export function randomNumberFromInterval(min: number, max: number): number;
// @public
export class RequestResponseLink implements ReqResLink {
constructor(session: Session, sender: Sender, receiver: Receiver);
@ -685,44 +577,6 @@ export interface SendRequestOptions {
timeoutInMs?: number;
}
// @public
export interface ServiceBusConnectionStringModel {
// (undocumented)
[x: string]: any;
// (undocumented)
Endpoint: string;
// (undocumented)
EntityPath?: string;
// (undocumented)
SharedAccessKey: string;
// (undocumented)
SharedAccessKeyName: string;
}
// @public
export class SharedKeyCredential {
constructor(keyName: string, key: string);
protected _createToken(expiry: number, audience: string, hashInput?: string | Buffer): AccessToken;
static fromConnectionString(connectionString: string): SharedKeyCredential;
getToken(audience: string): AccessToken;
key: string;
keyName: string;
}
// @public
export interface StorageConnectionStringModel {
// (undocumented)
[x: string]: any;
// (undocumented)
AccountKey: string;
// (undocumented)
AccountName: string;
// (undocumented)
DefaultEndpointsProtocol: string;
// (undocumented)
EndpointSuffix: string;
}
// @public
export enum SystemErrorConditionMapper {
// (undocumented)
@ -747,22 +601,6 @@ export enum SystemErrorConditionMapper {
ETIMEDOUT = "com.microsoft:timeout"
}
// @public
export class Timeout {
// (undocumented)
clear(): void;
// (undocumented)
set<T>(t: number, value?: T): Promise<T>;
// (undocumented)
static set<T>(t: number, value?: T): Promise<T>;
// (undocumented)
wrap<T>(promise: Promise<T>, t: number, value?: T): Promise<T>;
// (undocumented)
static wrap<T>(promise: Promise<T>, t: number, value?: T): Promise<T>;
}
export { TokenCredential }
// @public
export enum TokenType {
CbsTokenTypeJwt = "jwt",

Просмотреть файл

@ -4,9 +4,7 @@
import { Connection, ConnectionOptions, generate_uuid } from "rhea-promise";
import { CbsClient } from "./cbs";
import { DataTransformer, DefaultDataTransformer } from "./dataTransformer";
import { TokenCredential } from "@azure/core-auth";
import { ConnectionConfig } from "./connectionConfig/connectionConfig";
import { SharedKeyCredential } from "./auth/sas";
import { Constants } from "./util/constants";
import { getFrameworkInfo, getPlatformInfo } from "./util/runtimeInfo";
@ -32,11 +30,6 @@ export interface ConnectionContextBase {
* acquire the lock for negotiating cbs claim by an entity on that connection.
*/
negotiateClaimLock: string;
/**
* @property {SharedKeyCredential | TokenCredential} tokenCredential The credential to be used for getting tokens
* for authentication for the EventHub client.
*/
readonly tokenCredential: SharedKeyCredential | TokenCredential;
/**
* @property {Connection} connection The underlying AMQP connection.
*/
@ -102,11 +95,6 @@ export interface CreateConnectionContextBaseParameters {
* the AMQP connection.
*/
connectionProperties: ConnectionProperties;
/**
* @property {SharedKeyCredential | TokenCredential} [tokenCredential] The credential to be used for Authentication.
* Default value: SharedKeyCredentials.
*/
tokenCredential?: SharedKeyCredential | TokenCredential;
/**
* @property {DataTransformer} [dataTransformer] The datatransformer to be used for encoding and
* decoding messages. Default value: DefaultDataTransformer
@ -190,12 +178,6 @@ export const ConnectionContextBase = {
connectionId: connection.id,
cbsSession: new CbsClient(connection, connectionLock),
config: parameters.config,
tokenCredential:
parameters.tokenCredential ||
new SharedKeyCredential(
parameters.config.sharedAccessKeyName,
parameters.config.sharedAccessKey
),
dataTransformer: parameters.dataTransformer || new DefaultDataTransformer(),
refreshConnection() {
const connection = new Connection(connectionOptions);

Просмотреть файл

@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { SharedKeyCredential } from "./sas";
import { AccessToken } from "@azure/core-auth";
import { Buffer } from "buffer";
/**
* @class IotSharedKeyCredential
* @ignore
* Defines the IotSharedKeyCredential for IotHub.
*/
export class IotSharedKeyCredential extends SharedKeyCredential {
/**
* Gets the sas token for the specified audience for IotHub.
* @ignore
* @param {string} [audience] - The audience for which the token is desired. If not
* provided then the Endpoint from the connection string will be applied.
*/
getToken(audience: string): AccessToken {
return this._createToken(
Math.floor(Date.now() / 1000) + 3600,
audience,
Buffer.from(this.key, "base64")
);
}
}

Просмотреть файл

@ -2,7 +2,6 @@
// Licensed under the MIT license.
import { TokenType } from "./auth/token";
import { AccessToken } from "@azure/core-auth";
import {
Message as RheaMessage,
Connection,
@ -181,18 +180,18 @@ export class CbsClient {
*
* - **ManagementClient**
* - `"sb://<your-namespace>.servicebus.windows.net/<event-hub-name>/$management"`.
* @param {TokenInfo} tokenObject The token object that needs to be sent in the put-token request.
* @param {string} token The token that needs to be sent in the put-token request.
* @return {Promise<any>} Returns a Promise that resolves when $cbs authentication is successful
* and rejects when an error occurs during $cbs authentication.
*/
async negotiateClaim(
audience: string,
tokenObject: AccessToken,
token: string,
tokenType: TokenType
): Promise<CbsResponse> {
try {
const request: RheaMessage = {
body: tokenObject.token,
body: token,
message_id: generate_uuid(),
reply_to: this.replyTo,
to: this.endpoint,

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { ServiceBusConnectionStringModel, parseConnectionString } from "../util/utils";
import { parseConnectionString } from "../util/utils";
import { WebSocketImpl } from "rhea-promise";
/**
@ -83,7 +83,12 @@ export const ConnectionConfig = {
create(connectionString: string, path?: string): ConnectionConfig {
connectionString = String(connectionString);
const parsedCS = parseConnectionString<ServiceBusConnectionStringModel>(connectionString);
const parsedCS = parseConnectionString<{
Endpoint: string;
SharedAccessKeyName: string;
SharedAccessKey: string;
EntityPath?: string;
}>(connectionString);
if (!parsedCS.Endpoint) {
throw new TypeError("Missing Endpoint in Connection String.");
}

Просмотреть файл

@ -1,127 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { ConnectionConfig } from "./connectionConfig";
import { EventHubConnectionConfig } from "./eventhubConnectionConfig";
import { IotHubConnectionStringModel, parseConnectionString } from "../util/utils";
/**
* @ignore
*/
export interface IotHubConnectionConfig {
/**
* @property {string} endpoint - The iothub endpoint `"<iothub-namespace>.azure-devices.net"`.
*/
hostName: string;
/**
* @property {string} host - The host `"<yournamespace>"`.
*/
host: string;
/**
* @property {string} connectionString - The IotHub connection string.
*/
connectionString: string;
/**
* @property {string} entityPath - The name/path of the entity to which the connection needs to happen.
*/
entityPath: string;
/**
* @property {string} sharedAccessKeyName - The name of the access key.
*/
sharedAccessKeyName: string;
/**
* @property {string} sharedAccessKey - The secret value of the access key.
*/
sharedAccessKey: string;
/**
* @property {string} [deviceId] - The unique device identifier.
*/
deviceId?: string;
}
/**
* @module IotHubConnectionConfig
* @ignore
*/
export const IotHubConnectionConfig = {
/**
* Creates the connection config.
* @ignore
* @param {string} connectionString - The event hub connection string
* @param {string} [path] - The name/path of the entity (hub name) to which the connection needs to happen
*/
create(connectionString: string, path?: string): IotHubConnectionConfig {
connectionString = String(connectionString);
const parsedCS = parseConnectionString<IotHubConnectionStringModel>(connectionString);
if (!path) {
path = "messages/events";
}
const result: IotHubConnectionConfig = {
connectionString: connectionString,
hostName: parsedCS.HostName,
host: parsedCS && parsedCS.HostName ? parsedCS.HostName.split(".")[0] : "",
entityPath: path,
sharedAccessKeyName: parsedCS.SharedAccessKeyName,
sharedAccessKey: parsedCS.SharedAccessKey,
deviceId: parsedCS.DeviceId
};
return result;
},
/**
* Validates the properties of connection config.
* @ignore
* @param {ConnectionConfig} config The connection config to be validated.
*/
validate(config: IotHubConnectionConfig): void {
if (!config) {
throw new TypeError("Missing configuration");
}
if (!config.hostName) {
throw new TypeError("Missing 'hostName' in configuration");
}
config.hostName = String(config.hostName);
if (!config.entityPath) {
throw new TypeError("Missing 'entityPath' in configuration");
}
config.entityPath = String(config.entityPath);
if (!config.sharedAccessKeyName) {
throw new TypeError("Missing 'sharedAccessKeyName' in configuration");
}
config.sharedAccessKeyName = String(config.sharedAccessKeyName);
if (!config.sharedAccessKey) {
throw new TypeError("Missing 'sharedAccessKey' in configuration");
}
config.sharedAccessKey = String(config.sharedAccessKey);
if (config.deviceId) {
config.deviceId = String(config.deviceId);
}
},
/**
* Convert iothub connection config to eventhub connection config.
* @ignore
* @param {IotHubConnectionConfig} iotHubConfig
*/
convertToEventHubConnectionConfig(
iotHubConfig: IotHubConnectionConfig
): EventHubConnectionConfig {
IotHubConnectionConfig.validate(iotHubConfig);
const config: ConnectionConfig = {
sharedAccessKey: iotHubConfig.sharedAccessKey,
sharedAccessKeyName: iotHubConfig.sharedAccessKeyName,
entityPath: iotHubConfig.entityPath,
host: iotHubConfig.hostName,
// `sb://` prefix to match with the endpoint in the connection string from the portal
endpoint: `sb://${iotHubConfig.hostName}/`,
connectionString: iotHubConfig.connectionString
};
return EventHubConnectionConfig.createFromConnectionConfig(config);
}
};

Просмотреть файл

@ -7,13 +7,8 @@ export { RequestResponseLink, SendRequestOptions } from "./requestResponseLink";
export { retry, RetryOptions, RetryConfig, RetryOperationType, RetryMode } from "./retry";
export { DataTransformer, DefaultDataTransformer } from "./dataTransformer";
export { TokenType } from "./auth/token";
export { AccessToken, TokenCredential, isTokenCredential } from "@azure/core-auth";
export { SharedKeyCredential } from "./auth/sas";
export { IotSharedKeyCredential } from "./auth/iotSas";
export { ConnectionConfig, ConnectionConfigOptions } from "./connectionConfig/connectionConfig";
export { EventHubConnectionConfig } from "./connectionConfig/eventhubConnectionConfig";
export { IotHubConnectionConfig } from "./connectionConfig/iothubConnectionConfig";
export { CbsClient, CbsResponse } from "./cbs";
export { Constants } from "./util/constants";
@ -24,7 +19,6 @@ export {
ConnectionProperties,
CreateConnectionContextBaseParameters
} from "./ConnectionContextBase";
export { Dictionary, isAmqpError } from "rhea-promise";
export {
MessagingError,
MessageErrorCodes,
@ -40,22 +34,10 @@ export {
} from "./errors";
export {
delay,
Timeout,
EventHubConnectionStringModel,
executePromisesSequentially,
parseConnectionString,
IotHubConnectionStringModel,
StorageConnectionStringModel,
defaultLock,
Func,
ParsedOutput,
getNewAsyncLock,
AsyncLockOptions,
ServiceBusConnectionStringModel,
isIotHubConnectionString,
randomNumberFromInterval,
AsyncLock,
isNode,
WebSocketOptions
} from "./util/utils";
export { AmqpAnnotatedMessage } from "./amqpAnnotatedMessage";

Просмотреть файл

@ -57,49 +57,6 @@ export interface WebSocketOptions {
export const isNode =
!!process && !!process.version && !!process.versions && !!process.versions.node;
/**
* Describes the servicebus connection string model.
*/
export interface ServiceBusConnectionStringModel {
Endpoint: string;
SharedAccessKeyName: string;
SharedAccessKey: string;
EntityPath?: string;
[x: string]: any;
}
/**
* Describes the eventhub connection string model.
*/
export interface EventHubConnectionStringModel {
Endpoint: string;
SharedAccessKeyName: string;
SharedAccessKey: string;
EntityPath?: string;
[x: string]: any;
}
/**
* Describes the storage connection string model.
*/
export interface StorageConnectionStringModel {
DefaultEndpointsProtocol: string;
AccountName: string;
AccountKey: string;
EndpointSuffix: string;
[x: string]: any;
}
/**
* Describes the iothub connection string model.
*/
export interface IotHubConnectionStringModel {
HostName: string;
SharedAccessKeyName: string;
SharedAccessKey: string;
DeviceId?: string;
}
/**
* Defines an object with possible properties defined in T.
* @type ParsedOutput<T>

Просмотреть файл

@ -1,10 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { ConnectionConfig, EventHubConnectionConfig, IotHubConnectionConfig } from "../src";
import { ConnectionConfig } from "../src";
import * as chai from "chai";
import { isSharedAccessSignature } from "../src/connectionConfig/connectionConfig";
import { SharedAccessSignatureCredential } from "../src/auth/sas";
const should = chai.should();
describe("ConnectionConfig", function() {
@ -197,241 +196,6 @@ describe("ConnectionConfig", function() {
});
});
describe("EventHub", function() {
it("should fail if connection config does not contain path and the connectionstring also does not contain EntityPath", function(done) {
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak";
try {
EventHubConnectionConfig.create(connectionString);
done(new Error("Should not have reached here."));
} catch (err) {
err.message.should.match(/Either provide "path" or the "connectionString".*/gi);
}
done();
});
it("should correctly populate config properties from an EventHubs connection string and the helper methods should work as expected", function(done) {
const config = EventHubConnectionConfig.create(
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep"
);
config.should.have.property("host").that.equals("hostname.servicebus.windows.net");
config.should.have.property("sharedAccessKeyName").that.equals("sakName");
config.should.have.property("sharedAccessKey").that.equals("sak");
config.should.have.property("entityPath").that.equals("ep");
config.getManagementAddress().should.equal("ep/$management");
config.getSenderAddress().should.equal("ep");
config.getSenderAddress("0").should.equal("ep/Partitions/0");
config.getSenderAddress(0).should.equal("ep/Partitions/0");
config.getReceiverAddress("0").should.equal("ep/ConsumerGroups/$default/Partitions/0");
config.getReceiverAddress(0).should.equal("ep/ConsumerGroups/$default/Partitions/0");
config.getReceiverAddress("0", "cg").should.equal("ep/ConsumerGroups/cg/Partitions/0");
config.getReceiverAddress(0, "cg").should.equal("ep/ConsumerGroups/cg/Partitions/0");
config
.getManagementAudience()
.should.equal("sb://hostname.servicebus.windows.net/ep/$management");
config.getSenderAudience().should.equal("sb://hostname.servicebus.windows.net/ep");
config
.getSenderAudience("0")
.should.equal("sb://hostname.servicebus.windows.net/ep/Partitions/0");
config
.getSenderAudience(0)
.should.equal("sb://hostname.servicebus.windows.net/ep/Partitions/0");
config
.getReceiverAudience("0")
.should.equal(
"sb://hostname.servicebus.windows.net/ep/ConsumerGroups/$default/Partitions/0"
);
config
.getReceiverAudience(0)
.should.equal(
"sb://hostname.servicebus.windows.net/ep/ConsumerGroups/$default/Partitions/0"
);
config
.getReceiverAudience("0", "cg")
.should.equal("sb://hostname.servicebus.windows.net/ep/ConsumerGroups/cg/Partitions/0");
config
.getReceiverAudience(0, "cg")
.should.equal("sb://hostname.servicebus.windows.net/ep/ConsumerGroups/cg/Partitions/0");
done();
});
it("requires that Endpoint be present in the connection string", (done) => {
const connectionString = `Endpoint=sb://a`;
should.throw(() => {
EventHubConnectionConfig.create(connectionString);
}, /must contain EntityPath/);
done();
});
});
describe("IotHub", function() {
const iotString =
"HostName=someiot.azure-devices.net;SharedAccessKeyName=sakName;SharedAccessKey=sak;DeviceId=device-1234";
it("should correctly create an iothub connection config from an iothub connectionstring", function(done) {
const config = IotHubConnectionConfig.create(iotString);
config.should.have.property("hostName").that.equals("someiot.azure-devices.net");
config.should.have.property("host").that.equals("someiot");
config.should.have.property("sharedAccessKeyName").that.equals("sakName");
config.should.have.property("sharedAccessKey").that.equals("sak");
config.should.have.property("entityPath").that.equals("messages/events");
done();
});
it("populates path from the path argument if provided", function(done) {
const config = IotHubConnectionConfig.create(iotString, "abc");
config.should.have.property("entityPath").that.equals("abc");
done();
});
it("converts an IotHubConnectionConfig to an EventHubConnectionConfig", function(done) {
const config = IotHubConnectionConfig.create(iotString);
const ehConfig = IotHubConnectionConfig.convertToEventHubConnectionConfig(config);
ehConfig.should.have.property("endpoint").that.equals("sb://someiot.azure-devices.net/");
ehConfig.should.have.property("host").that.equals("someiot.azure-devices.net");
ehConfig.should.have.property("sharedAccessKeyName").that.equals("sakName");
ehConfig.should.have.property("sharedAccessKey").that.equals("sak");
ehConfig.should.have.property("entityPath").that.equals("messages/events");
ehConfig.getManagementAddress().should.equal("messages/events/$management");
ehConfig.getSenderAddress().should.equal("messages/events");
ehConfig.getSenderAddress("0").should.equal("messages/events/Partitions/0");
ehConfig.getSenderAddress(0).should.equal("messages/events/Partitions/0");
ehConfig
.getReceiverAddress("0")
.should.equal("messages/events/ConsumerGroups/$default/Partitions/0");
ehConfig
.getReceiverAddress(0)
.should.equal("messages/events/ConsumerGroups/$default/Partitions/0");
ehConfig
.getReceiverAddress("0", "cg")
.should.equal("messages/events/ConsumerGroups/cg/Partitions/0");
ehConfig
.getReceiverAddress(0, "cg")
.should.equal("messages/events/ConsumerGroups/cg/Partitions/0");
ehConfig
.getManagementAudience()
.should.equal("sb://someiot.azure-devices.net/messages/events/$management");
ehConfig.getSenderAudience().should.equal("sb://someiot.azure-devices.net/messages/events");
ehConfig
.getSenderAudience("0")
.should.equal("sb://someiot.azure-devices.net/messages/events/Partitions/0");
ehConfig
.getSenderAudience(0)
.should.equal("sb://someiot.azure-devices.net/messages/events/Partitions/0");
ehConfig
.getReceiverAudience("0")
.should.equal(
"sb://someiot.azure-devices.net/messages/events/ConsumerGroups/$default/Partitions/0"
);
ehConfig
.getReceiverAudience(0)
.should.equal(
"sb://someiot.azure-devices.net/messages/events/ConsumerGroups/$default/Partitions/0"
);
ehConfig
.getReceiverAudience("0", "cg")
.should.equal(
"sb://someiot.azure-devices.net/messages/events/ConsumerGroups/cg/Partitions/0"
);
ehConfig
.getReceiverAudience(0, "cg")
.should.equal(
"sb://someiot.azure-devices.net/messages/events/ConsumerGroups/cg/Partitions/0"
);
done();
});
describe("Throws error if required connection config properties are not present", function() {
const connectionString = `
Endpoint=sb://someiot.azure-devices.net/;
SharedAccessKeyName=sakName;
SharedAccessKey=sakName;
EntityPath=messages/events;
`;
it("requires that connection config be present", (done) => {
should.throw(() => {
IotHubConnectionConfig.validate(undefined as any);
}, /Missing configuration/);
done();
});
it("requires that hostName be present in the connection config", (done) => {
const config: IotHubConnectionConfig = {
connectionString: connectionString,
hostName: "",
host: "someiot",
sharedAccessKeyName: "sakName",
sharedAccessKey: "sak",
entityPath: "messages/events"
};
should.throw(() => {
IotHubConnectionConfig.validate(config);
}, /Missing 'hostName'/);
done();
});
it("requires that host be present in the connection config", (done) => {
const config: IotHubConnectionConfig = {
connectionString: connectionString,
hostName: "someiot.azure-devices.net",
host: "someiot",
sharedAccessKeyName: "sakName",
sharedAccessKey: "sak",
entityPath: ""
};
should.throw(() => {
IotHubConnectionConfig.validate(config);
}, /Missing 'entityPath'/);
done();
});
it("requires that sharedAccessKeyName be present in the connection config", (done) => {
const config: IotHubConnectionConfig = {
connectionString: connectionString,
hostName: "someiot.azure-devices.net",
host: "someiot",
sharedAccessKeyName: "",
sharedAccessKey: "sak",
entityPath: "messages/events"
};
should.throw(() => {
IotHubConnectionConfig.validate(config);
}, /Missing 'sharedAccessKeyName'/);
done();
});
it("requires that sharedAccessKey be present in the connection config", (done) => {
const config: IotHubConnectionConfig = {
connectionString: connectionString,
hostName: "someiot.azure-devices.net",
host: "someiot",
sharedAccessKeyName: "sakName",
sharedAccessKey: "",
entityPath: "messages/events"
};
should.throw(() => {
IotHubConnectionConfig.validate(config);
}, /Missing 'sharedAccessKey'/);
done();
});
});
});
describe("SharedAccessSignature", () => {
[
"Endpoint=hello;SharedAccessSignature=SharedAccessSignature sr=<resource>&sig=someb64=&se=<expiry>&skn=<keyname>",
@ -461,28 +225,5 @@ describe("ConnectionConfig", function() {
connectionString: "Endpoint=hello;SharedAccessSignature=SharedAccessSignature hellosig"
});
});
it("SharedAccessSignatureCredential", () => {
const sasCred = new SharedAccessSignatureCredential("SharedAccessSignature se=<blah>");
const accessToken = sasCred.getToken("audience isn't used");
should.equal(
accessToken.token,
"SharedAccessSignature se=<blah>",
"SAS URI we were constructed with should just be returned verbatim without interpretation (and the audience is ignored)"
);
should.equal(
accessToken.expiresOnTimestamp,
0,
"SAS URI always returns 0 for expiry (ignoring what's in the SAS token)"
);
// these just exist because we're a SharedKeyCredential but we don't currently
// parse any attributes out (they're available but we've carved out a spot so
// they're not needed.)
should.equal(sasCred.key, "");
should.equal(sasCred.keyName, "");
});
});
});

Просмотреть файл

@ -3,13 +3,7 @@
import * as chai from "chai";
const should = chai.should();
import {
CbsClient,
ConnectionConfig,
ConnectionContextBase,
DefaultDataTransformer,
SharedKeyCredential
} from "../src";
import { CbsClient, ConnectionConfig, ConnectionContextBase, DefaultDataTransformer } from "../src";
import { Connection } from "rhea-promise";
import { isNode } from "../src/util/utils";
@ -32,10 +26,8 @@ describe("ConnectionContextBase", function() {
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
should.exist(context.tokenCredential);
should.exist(context.dataTransformer);
context.wasConnectionCloseCalled.should.equal(false);
context.tokenCredential.should.instanceOf(SharedKeyCredential);
context.connection.should.instanceOf(Connection);
context.connection.options.properties!.product.should.equal("MSJSClient");
context.connection.options.properties!["user-agent"].should.equal("/js-amqp-client");
@ -141,7 +133,6 @@ describe("ConnectionContextBase", function() {
should.exist(context.connectionId);
should.exist(context.connectionLock);
should.exist(context.negotiateClaimLock);
should.exist(context.tokenCredential);
should.exist(context.dataTransformer);
context.wasConnectionCloseCalled.should.equal(false);
context.cbsSession.should.instanceOf(CbsClient);

Просмотреть файл

@ -91,9 +91,12 @@
"@azure/core-amqp": "2.0.0-beta.1",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"@azure/core-tracing": "1.0.0-preview.9",
"@azure/core-auth": "^1.1.3",
"@azure/logger": "^1.0.0",
"@opentelemetry/api": "^0.10.2",
"buffer": "^5.2.1",
"is-buffer": "^2.0.3",
"jssha": "^3.1.0",
"process": "^0.11.10",
"rhea-promise": "^1.0.0",
"tslib": "^2.0.0",

Просмотреть файл

@ -10,7 +10,7 @@ import { OperationTracingOptions } from '@azure/core-tracing';
import { RetryOptions } from '@azure/core-amqp';
import { Span } from '@opentelemetry/api';
import { SpanContext } from '@opentelemetry/api';
import { TokenCredential } from '@azure/core-amqp';
import { TokenCredential } from '@azure/core-auth';
import { WebSocketImpl } from 'rhea-promise';
import { WebSocketOptions } from '@azure/core-amqp';

Просмотреть файл

@ -13,17 +13,15 @@ import {
ConnectionContextBase,
Constants,
CreateConnectionContextBaseParameters,
EventHubConnectionConfig,
SharedKeyCredential,
TokenCredential,
isTokenCredential,
parseConnectionString,
EventHubConnectionStringModel,
ConnectionConfig
} from "@azure/core-amqp";
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
import { ManagementClient, ManagementClientOptions } from "./managementClient";
import { EventHubClientOptions } from "./models/public";
import { Connection, ConnectionEvents, Dictionary, EventContext, OnAmqpEvent } from "rhea-promise";
import { EventHubConnectionConfig } from "./eventhubConnectionConfig";
import { SharedKeyCredential } from "./eventhubSharedKeyCredential";
/**
* @internal
@ -37,6 +35,11 @@ export interface ConnectionContext extends ConnectionContextBase {
* parsing the connection string.
*/
readonly config: EventHubConnectionConfig;
/**
* @property {SharedKeyCredential | TokenCredential} [tokenCredential] The credential to be used for Authentication.
* Default value: SharedKeyCredentials.
*/
tokenCredential: SharedKeyCredential | TokenCredential;
/**
* @property wasConnectionCloseCalled Indicates whether the close() method was
* called on theconnection object.
@ -163,7 +166,6 @@ export namespace ConnectionContext {
const parameters: CreateConnectionContextBaseParameters = {
config: config,
tokenCredential: tokenCredential,
// re-enabling this will be a post-GA discussion.
// dataTransformer: options.dataTransformer,
isEntityPathRequired: true,
@ -175,6 +177,7 @@ export namespace ConnectionContext {
};
// Let us create the base context and then add EventHub specific ConnectionContext properties.
const connectionContext = ConnectionContextBase.create(parameters) as ConnectionContext;
connectionContext.tokenCredential = tokenCredential;
connectionContext.wasConnectionCloseCalled = false;
connectionContext.senders = {};
connectionContext.receivers = {};
@ -450,7 +453,7 @@ export function createConnectionContext(
hostOrConnectionString = String(hostOrConnectionString);
if (!isTokenCredential(credentialOrOptions)) {
const parsedCS = parseConnectionString<EventHubConnectionStringModel>(hostOrConnectionString);
const parsedCS = parseConnectionString<{ EntityPath?: string }>(hostOrConnectionString);
if (
!(parsedCS.EntityPath || (typeof eventHubNameOrOptions === "string" && eventHubNameOrOptions))
) {

Просмотреть файл

@ -3,7 +3,7 @@
import { getTracer } from "@azure/core-tracing";
import { Span, SpanContext, SpanKind } from "@opentelemetry/api";
import { EventHubConnectionConfig } from "@azure/core-amqp";
import { EventHubConnectionConfig } from "../eventhubConnectionConfig";
/**
* @internal

Просмотреть файл

@ -11,7 +11,7 @@ import {
} from "./models/public";
import { InMemoryCheckpointStore } from "./inMemoryCheckpointStore";
import { CheckpointStore, EventProcessor, FullEventProcessorOptions } from "./eventProcessor";
import { Constants, TokenCredential } from "@azure/core-amqp";
import { Constants } from "@azure/core-amqp";
import { logger } from "./log";
import {
@ -19,7 +19,7 @@ import {
Subscription,
SubscriptionEventHandlers
} from "./eventHubConsumerClientModels";
import { isTokenCredential } from "@azure/core-amqp";
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
import { EventHubProperties, PartitionProperties } from "./managementClient";
import { PartitionGate } from "./impl/partitionGate";
import { v4 as uuid } from "uuid";

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { isTokenCredential, TokenCredential } from "@azure/core-amqp";
import { isTokenCredential, TokenCredential } from "@azure/core-auth";
import { getTracer } from "@azure/core-tracing";
import { CanonicalCode, Link, Span, SpanContext, SpanKind } from "@opentelemetry/api";
import { ConnectionContext, createConnectionContext } from "./connectionContext";

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT license.
/* eslint-disable eqeqeq */
import { ConnectionConfig } from "./connectionConfig";
import { ConnectionConfig } from "@azure/core-amqp";
/**
* Describes the connection config object that is created after parsing an EventHub connection

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { ServiceBusConnectionStringModel, parseConnectionString } from "../util/utils";
import { parseConnectionString } from "@azure/core-amqp";
import { AccessToken } from "@azure/core-auth";
import { Buffer } from "buffer";
import isBuffer from "is-buffer";
@ -78,9 +78,11 @@ export class SharedKeyCredential {
* @param {string} connectionString - The EventHub/ServiceBus connection string
*/
static fromConnectionString(connectionString: string): SharedKeyCredential {
const parsed = parseConnectionString<
ServiceBusConnectionStringModel & { SharedAccessSignature: string }
>(connectionString);
const parsed = parseConnectionString<{
SharedAccessSignature: string;
SharedAccessKeyName: string;
SharedAccessKey: string;
}>(connectionString);
if (parsed.SharedAccessSignature == null) {
return new SharedKeyCredential(parsed.SharedAccessKeyName, parsed.SharedAccessKey);

Просмотреть файл

@ -35,5 +35,6 @@ export { EventDataBatch, TryAddOptions } from "./eventDataBatch";
export { Checkpoint } from "./partitionProcessor";
export { CheckpointStore, PartitionOwnership } from "./eventProcessor";
export { CloseReason } from "./models/public";
export { MessagingError, RetryOptions, TokenCredential, WebSocketOptions } from "@azure/core-amqp";
export { MessagingError, RetryOptions, WebSocketOptions } from "@azure/core-amqp";
export { TokenCredential } from "@azure/core-auth";
export { logger } from "./log";

Просмотреть файл

@ -2,16 +2,12 @@
// Licensed under the MIT license.
import { v4 as uuid } from "uuid";
import {
AccessToken,
Constants,
SharedKeyCredential,
TokenType,
defaultLock
} from "@azure/core-amqp";
import { Constants, TokenType, defaultLock } from "@azure/core-amqp";
import { AccessToken } from "@azure/core-auth";
import { ConnectionContext } from "./connectionContext";
import { AwaitableSender, Receiver } from "rhea-promise";
import { logger } from "./log";
import { SharedKeyCredential } from "../src/eventhubSharedKeyCredential";
/**
* @ignore
@ -175,7 +171,7 @@ export class LinkEntity {
this.address
);
await defaultLock.acquire(this._context.negotiateClaimLock, () => {
return this._context.cbsSession.negotiateClaim(this.audience, tokenObject, tokenType);
return this._context.cbsSession.negotiateClaim(this.audience, tokenObject.token, tokenType);
});
logger.verbose(
"[%s] Negotiated claim for %s '%s' with with address: %s",

Просмотреть файл

@ -9,7 +9,6 @@ import {
RetryOperationType,
RetryOptions,
SendRequestOptions,
SharedKeyCredential,
defaultLock,
retry,
translate
@ -33,6 +32,8 @@ import { OperationNames } from "./models/private";
import { Span, SpanContext, SpanKind, CanonicalCode } from "@opentelemetry/api";
import { getParentSpan, OperationOptions } from "./util/operationOptions";
import { getTracer } from "@azure/core-tracing";
import { SharedKeyCredential } from "../src/eventhubSharedKeyCredential";
/**
* Describes the runtime information of an Event Hub.
*/

Просмотреть файл

@ -1,15 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import {
parseConnectionString,
ServiceBusConnectionStringModel,
SharedKeyCredential
} from "@azure/core-amqp";
import { parseConnectionString } from "@azure/core-amqp";
import { EventHubConsumerClient } from "../src/eventHubConsumerClient";
import { EnvVarKeys, getEnvVars } from "./utils/testUtils";
import chai from "chai";
import { EventHubProducerClient } from "../src";
import { SharedKeyCredential } from "../src/eventhubSharedKeyCredential";
const should = chai.should();
const env = getEnvVars();
@ -18,7 +15,7 @@ describe("Authentication via SAS", () => {
const service = {
connectionString: env[EnvVarKeys.EVENTHUB_CONNECTION_STRING],
path: env[EnvVarKeys.EVENTHUB_NAME],
fqdn: parseConnectionString<ServiceBusConnectionStringModel>(
fqdn: parseConnectionString<{ Endpoint: string }>(
env[EnvVarKeys.EVENTHUB_CONNECTION_STRING]
).Endpoint.replace(/\/+$/, "")
};

Просмотреть файл

@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { EventHubConnectionConfig } from "../src/eventhubConnectionConfig";
import chai from "chai";
const should = chai.should();
describe("ConnectionConfig", function() {
describe("EventHub", function() {
it("should fail if connection config does not contain path and the connectionstring also does not contain EntityPath", function(done) {
const connectionString =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak";
try {
EventHubConnectionConfig.create(connectionString);
done(new Error("Should not have reached here."));
} catch (err) {
err.message.should.match(/Either provide "path" or the "connectionString".*/gi);
}
done();
});
it("should correctly populate config properties from an EventHubs connection string and the helper methods should work as expected", function(done) {
const config = EventHubConnectionConfig.create(
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep"
);
config.should.have.property("host").that.equals("hostname.servicebus.windows.net");
config.should.have.property("sharedAccessKeyName").that.equals("sakName");
config.should.have.property("sharedAccessKey").that.equals("sak");
config.should.have.property("entityPath").that.equals("ep");
config.getManagementAddress().should.equal("ep/$management");
config.getSenderAddress().should.equal("ep");
config.getSenderAddress("0").should.equal("ep/Partitions/0");
config.getSenderAddress(0).should.equal("ep/Partitions/0");
config.getReceiverAddress("0").should.equal("ep/ConsumerGroups/$default/Partitions/0");
config.getReceiverAddress(0).should.equal("ep/ConsumerGroups/$default/Partitions/0");
config.getReceiverAddress("0", "cg").should.equal("ep/ConsumerGroups/cg/Partitions/0");
config.getReceiverAddress(0, "cg").should.equal("ep/ConsumerGroups/cg/Partitions/0");
config
.getManagementAudience()
.should.equal("sb://hostname.servicebus.windows.net/ep/$management");
config.getSenderAudience().should.equal("sb://hostname.servicebus.windows.net/ep");
config
.getSenderAudience("0")
.should.equal("sb://hostname.servicebus.windows.net/ep/Partitions/0");
config
.getSenderAudience(0)
.should.equal("sb://hostname.servicebus.windows.net/ep/Partitions/0");
config
.getReceiverAudience("0")
.should.equal(
"sb://hostname.servicebus.windows.net/ep/ConsumerGroups/$default/Partitions/0"
);
config
.getReceiverAudience(0)
.should.equal(
"sb://hostname.servicebus.windows.net/ep/ConsumerGroups/$default/Partitions/0"
);
config
.getReceiverAudience("0", "cg")
.should.equal("sb://hostname.servicebus.windows.net/ep/ConsumerGroups/cg/Partitions/0");
config
.getReceiverAudience(0, "cg")
.should.equal("sb://hostname.servicebus.windows.net/ep/ConsumerGroups/cg/Partitions/0");
done();
});
it("requires that Endpoint be present in the connection string", (done) => {
const connectionString = `Endpoint=sb://a`;
should.throw(() => {
EventHubConnectionConfig.create(connectionString);
}, /must contain EntityPath/);
done();
});
});
});

Просмотреть файл

@ -5,7 +5,7 @@ import chai from "chai";
import { createMessageSpan } from "../../src/diagnostics/messageSpan";
import { TraceFlags, SpanContext } from "@opentelemetry/api";
import { TestTracer, setTracer, getTracer } from "@azure/core-tracing";
import { EventHubConnectionConfig } from "@azure/core-amqp";
import { EventHubConnectionConfig } from "../../src/eventhubConnectionConfig";
const should = chai.should();
const assert = chai.assert;

Просмотреть файл

@ -1,11 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import * as chai from "chai";
chai.should();
import debugModule from "debug";
const debug = debugModule("azure:core-amqp:token-spec");
import { IotSharedKeyCredential, SharedKeyCredential } from "../src";
import chai from "chai";
const should = chai.should();
import {
SharedKeyCredential,
SharedAccessSignatureCredential
} from "../src/eventhubSharedKeyCredential";
describe("SharedKeyCredential", function(): void {
it("should work as expected with required parameters", async function(): Promise<void> {
@ -13,9 +14,7 @@ describe("SharedKeyCredential", function(): void {
const key = "importantValue";
const tokenProvider = new SharedKeyCredential(keyName, key);
const now = Math.floor(Date.now() / 1000) + 3600;
debug(">>> now: %d", now);
const tokenInfo = tokenProvider.getToken("myaudience");
debug(">>> Token Info is: %O", tokenInfo);
tokenInfo.token.should.match(
/SharedAccessSignature sr=myaudience&sig=(.*)&se=\d{10}&skn=myKeyName/g
);
@ -28,28 +27,32 @@ describe("SharedKeyCredential", function(): void {
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const tokenProvider = SharedKeyCredential.fromConnectionString(cs);
const now = Math.floor(Date.now() / 1000) + 3600;
debug(">>> now: %d", now);
const tokenInfo = tokenProvider.getToken("sb://hostname.servicebus.windows.net/");
debug(">>> Token Info is: %O", tokenInfo);
tokenInfo.token.should.match(
/SharedAccessSignature sr=sb%3A%2F%2Fhostname.servicebus.windows.net%2F&sig=(.*)&se=\d{10}&skn=sakName/g
);
tokenInfo.expiresOnTimestamp.should.equal(now);
});
});
it("SharedAccessSignatureCredential", () => {
const sasCred = new SharedAccessSignatureCredential("SharedAccessSignature se=<blah>");
const accessToken = sasCred.getToken("audience isn't used");
describe("IotSharedKeyCredential", function(): void {
it("should work as expected with required parameters", async function(): Promise<void> {
const keyName = "myKeyName";
const key = "importantValue";
const tokenProvider = new IotSharedKeyCredential(keyName, key);
const now = Math.floor(Date.now() / 1000) + 3600;
debug(">>> now: %d", now);
const tokenInfo = tokenProvider.getToken("myaudience");
debug(">>> Token Info is: %O", tokenInfo);
tokenInfo.token.should.match(
/SharedAccessSignature sr=myaudience&sig=(.*)&se=\d{10}&skn=myKeyName/g
should.equal(
accessToken.token,
"SharedAccessSignature se=<blah>",
"SAS URI we were constructed with should just be returned verbatim without interpretation (and the audience is ignored)"
);
tokenInfo.expiresOnTimestamp.should.equal(now);
should.equal(
accessToken.expiresOnTimestamp,
0,
"SAS URI always returns 0 for expiry (ignoring what's in the SAS token)"
);
// these just exist because we're a SharedKeyCredential but we don't currently
// parse any attributes out (they're available but we've carved out a spot so
// they're not needed.)
should.equal(sasCred.key, "");
should.equal(sasCred.keyName, "");
});
});

Просмотреть файл

@ -102,12 +102,14 @@
"@azure/core-http": "^1.2.0",
"@azure/core-tracing": "1.0.0-preview.9",
"@azure/core-paging": "^1.1.1",
"@azure/core-auth": "^1.1.3",
"@azure/logger": "^1.0.0",
"@opentelemetry/api": "^0.10.2",
"@types/is-buffer": "^2.0.0",
"@types/long": "^4.0.0",
"buffer": "^5.2.1",
"is-buffer": "^2.0.3",
"jssha": "^3.1.0",
"long": "^4.0.0",
"process": "^0.11.10",
"tslib": "^2.0.0",

Просмотреть файл

@ -20,7 +20,7 @@ import { RetryOptions } from '@azure/core-amqp';
import { ServiceClient } from '@azure/core-http';
import { Span } from '@opentelemetry/api';
import { SpanContext } from '@opentelemetry/api';
import { TokenCredential } from '@azure/core-amqp';
import { TokenCredential } from '@azure/core-auth';
import { TokenType } from '@azure/core-amqp';
import { UserAgentOptions } from '@azure/core-http';
import { WebSocketImpl } from 'rhea-promise';

Просмотреть файл

@ -8,10 +8,9 @@ import {
ConnectionContextBase,
Constants,
CreateConnectionContextBaseParameters,
SharedKeyCredential,
TokenCredential,
delay
} from "@azure/core-amqp";
import { TokenCredential } from "@azure/core-auth";
import { ServiceBusClientOptions } from "./constructorHelpers";
import { Connection, ConnectionEvents, EventContext, OnAmqpEvent } from "rhea-promise";
import { MessageSender } from "./core/messageSender";
@ -20,6 +19,7 @@ import { MessageReceiver } from "./core/messageReceiver";
import { ManagementClient } from "./core/managementClient";
import { formatUserAgentPrefix } from "./util/utils";
import { getRuntimeInfo } from "./util/runtimeInfo";
import { SharedKeyCredential } from "./servicebusSharedKeyCredential";
/**
* @internal
@ -28,6 +28,11 @@ import { getRuntimeInfo } from "./util/runtimeInfo";
* tokenCredential, senders, receivers, etc. about the ServiceBus client.
*/
export interface ConnectionContext extends ConnectionContextBase {
/**
* @property {SharedKeyCredential | TokenCredential} [tokenCredential] The credential to be used for Authentication.
* Default value: SharedKeyCredentials.
*/
tokenCredential: SharedKeyCredential | TokenCredential;
/**
* @property A map of active Service Bus Senders with sender name as key.
*/
@ -141,7 +146,6 @@ export namespace ConnectionContext {
)} ${getRuntimeInfo()}`;
const parameters: CreateConnectionContextBaseParameters = {
config: config,
tokenCredential: tokenCredential,
// re-enabling this will be a post-GA discussion similar to event-hubs.
// dataTransformer: options.dataTransformer,
isEntityPathRequired: false,
@ -153,6 +157,7 @@ export namespace ConnectionContext {
};
// Let us create the base context and then add ServiceBus specific ConnectionContext properties.
const connectionContext = ConnectionContextBase.create(parameters) as ConnectionContext;
connectionContext.tokenCredential = tokenCredential;
connectionContext.senders = {};
connectionContext.messageReceivers = {};
connectionContext.messageSessions = {};

Просмотреть файл

@ -1,15 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import {
ConnectionConfig,
RetryOptions,
SharedKeyCredential,
TokenCredential,
WebSocketOptions
} from "@azure/core-amqp";
import { ConnectionConfig, RetryOptions, WebSocketOptions } from "@azure/core-amqp";
import { TokenCredential } from "@azure/core-auth";
import { ConnectionContext } from "./connectionContext";
import { UserAgentOptions } from "@azure/core-http";
import { SharedKeyCredential } from "./servicebusSharedKeyCredential";
/**
* Describes the options that can be provided while creating the ServiceBusClient.

Просмотреть файл

@ -2,14 +2,13 @@
// Licensed under the MIT license.
import {
AccessToken,
Constants,
SharedKeyCredential,
TokenType,
defaultLock,
RequestResponseLink,
MessagingError
} from "@azure/core-amqp";
import { AccessToken } from "@azure/core-auth";
import { ConnectionContext } from "../connectionContext";
import {
AwaitableSender,
@ -22,6 +21,7 @@ import {
import { getUniqueName, StandardAbortMessage } from "../util/utils";
import { AbortError, AbortSignalLike } from "@azure/abort-controller";
import { ServiceBusLogger } from "../log";
import { SharedKeyCredential } from "../servicebusSharedKeyCredential";
/**
* @internal
@ -444,7 +444,7 @@ export abstract class LinkEntity<LinkT extends Receiver | AwaitableSender | Requ
}
await defaultLock.acquire(this._context.negotiateClaimLock, () => {
this.checkIfConnectionReady();
return this._context.cbsSession.negotiateClaim(this.audience, tokenObject, tokenType);
return this._context.cbsSession.negotiateClaim(this.audience, tokenObject.token, tokenType);
});
this._logger.verbose(
"%s Negotiated claim for %s '%s' with with address: %s",

Просмотреть файл

@ -10,10 +10,10 @@ export {
MessageErrorCodes,
MessagingError,
RetryOptions,
TokenCredential,
TokenType,
WebSocketOptions
} from "@azure/core-amqp";
export { TokenCredential } from "@azure/core-auth";
export { OperationOptions } from "@azure/core-http";
export { Delivery, WebSocketImpl } from "rhea-promise";
export { ServiceBusClientOptions } from "./constructorHelpers";

Просмотреть файл

@ -1,12 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import {
Constants as AMQPConstants,
isTokenCredential,
parseConnectionString,
TokenCredential
} from "@azure/core-amqp";
import { Constants as AMQPConstants, parseConnectionString } from "@azure/core-amqp";
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
import {
bearerTokenAuthenticationPolicy,
createPipelineFromOptions,

Просмотреть файл

@ -1,7 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { TokenCredential, isTokenCredential, ConnectionConfig } from "@azure/core-amqp";
import { ConnectionConfig } from "@azure/core-amqp";
import { TokenCredential, isTokenCredential } from "@azure/core-auth";
import {
ServiceBusClientOptions,
createConnectionContextForConnectionString,

Просмотреть файл

@ -0,0 +1,126 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { parseConnectionString } from "@azure/core-amqp";
import { AccessToken } from "@azure/core-auth";
import { Buffer } from "buffer";
import isBuffer from "is-buffer";
import jssha from "jssha";
/**
* @class SharedKeyCredential
* Defines the SharedKeyCredential .
*/
export class SharedKeyCredential {
/**
* @property {string} keyName - The name of the EventHub/ServiceBus key.
*/
keyName: string;
/**
* @property {string} key - The secret value associated with the above EventHub/ServiceBus key.
*/
key: string;
/**
* Initializes a new instance of SharedKeyCredential
* @constructor
* @param {string} keyName - The name of the EventHub/ServiceBus key.
* @param {string} key - The secret value associated with the above EventHub/ServiceBus key
*/
constructor(keyName: string, key: string) {
this.keyName = keyName;
this.key = key;
}
/**
* Gets the sas token for the specified audience
* @param {string} [audience] - The audience for which the token is desired.
*/
getToken(audience: string): AccessToken {
return this._createToken(Math.floor(Date.now() / 1000) + 3600, audience);
}
/**
* Creates the sas token based on the provided information
* @param {string | number} expiry - The time period in unix time after which the token will expire.
* @param {string} [audience] - The audience for which the token is desired.
* @param {string | Buffer} [hashInput] The input to be provided to hmac to create the hash.
*/
protected _createToken(
expiry: number,
audience: string,
hashInput?: string | Buffer
): AccessToken {
audience = encodeURIComponent(audience);
const keyName = encodeURIComponent(this.keyName);
const stringToSign = audience + "\n" + expiry;
hashInput = hashInput || this.key;
let shaObj: any;
if (isBuffer(hashInput)) {
shaObj = new jssha("SHA-256", "ARRAYBUFFER");
shaObj.setHMACKey(hashInput, "ARRAYBUFFER");
shaObj.update(Buffer.from(stringToSign));
} else {
shaObj = new jssha("SHA-256", "TEXT");
shaObj.setHMACKey(hashInput, "TEXT");
shaObj.update(stringToSign);
}
const sig = encodeURIComponent(shaObj.getHMAC("B64"));
return {
token: `SharedAccessSignature sr=${audience}&sig=${sig}&se=${expiry}&skn=${keyName}`,
expiresOnTimestamp: expiry
};
}
/**
* Creates a token provider from the EventHub/ServiceBus connection string;
* @param {string} connectionString - The EventHub/ServiceBus connection string
*/
static fromConnectionString(connectionString: string): SharedKeyCredential {
const parsed = parseConnectionString<{
SharedAccessSignature: string;
SharedAccessKeyName: string;
SharedAccessKey: string;
}>(connectionString);
if (parsed.SharedAccessSignature == null) {
return new SharedKeyCredential(parsed.SharedAccessKeyName, parsed.SharedAccessKey);
} else {
return new SharedAccessSignatureCredential(parsed.SharedAccessSignature);
}
}
}
/**
* A credential that takes a SharedAccessSignature:
* `SharedAccessSignature sr=<resource>&sig=<signature>&se=<expiry>&skn=<keyname>`
*
* @internal
* @ignore
*/
export class SharedAccessSignatureCredential extends SharedKeyCredential {
private _accessToken: AccessToken;
/**
* @param sharedAccessSignature A shared access signature of the form
* `SharedAccessSignature sr=<resource>&sig=<signature>&se=<expiry>&skn=<keyname>`
*/
constructor(sharedAccessSignature: string) {
super("", "");
this._accessToken = {
token: sharedAccessSignature,
expiresOnTimestamp: 0
};
}
/**
* Retrieve a valid token for authenticaton.
*
* @param _audience Not applicable in SharedAccessSignatureCredential as the token is not re-generated at every invocation of the method
*/
getToken(_audience: string): AccessToken {
return this._accessToken;
}
}

Просмотреть файл

@ -4,7 +4,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { AccessToken, SharedKeyCredential } from "@azure/core-amqp";
import { AccessToken } from "@azure/core-auth";
import { SharedKeyCredential } from "../servicebusSharedKeyCredential";
import { HttpHeaders, ServiceClientCredentials, WebResource } from "@azure/core-http";
import { generateKey } from "./crypto";

Просмотреть файл

@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { isNode } from "@azure/core-amqp";
import { PageSettings } from "@azure/core-paging";
import { DefaultAzureCredential } from "@azure/identity";
import chai from "chai";
@ -14,7 +13,7 @@ import { RuleProperties, CreateRuleOptions } from "../src/serializers/ruleResour
import { CreateSubscriptionOptions } from "../src/serializers/subscriptionResourceSerializer";
import { CreateTopicOptions } from "../src/serializers/topicResourceSerializer";
import { ServiceBusAdministrationClient } from "../src/serviceBusAtomManagementClient";
import { EntityStatus, EntityAvailabilityStatus } from "../src/util/utils";
import { EntityStatus, EntityAvailabilityStatus, isNode } from "../src/util/utils";
import { EnvVarNames, getEnvVars } from "./utils/envVarUtils";
import { recreateQueue, recreateSubscription, recreateTopic } from "./utils/managementUtils";
import { EntityNames } from "./utils/testUtils";

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { SharedKeyCredential } from "@azure/core-amqp";
import { SharedKeyCredential } from "../src/servicebusSharedKeyCredential";
import chai from "chai";
import { ServiceBusClient, ServiceBusReceiver, parseServiceBusConnectionString } from "../src";
import { getEnvVars } from "./utils/envVarUtils";

Просмотреть файл

@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import chai from "chai";
const should = chai.should();
import {
SharedKeyCredential,
SharedAccessSignatureCredential
} from "../../src/servicebusSharedKeyCredential";
describe("SharedKeyCredential", function(): void {
it("should work as expected with required parameters", async function(): Promise<void> {
const keyName = "myKeyName";
const key = "importantValue";
const tokenProvider = new SharedKeyCredential(keyName, key);
const now = Math.floor(Date.now() / 1000) + 3600;
const tokenInfo = tokenProvider.getToken("myaudience");
tokenInfo.token.should.match(
/SharedAccessSignature sr=myaudience&sig=(.*)&se=\d{10}&skn=myKeyName/g
);
tokenInfo.expiresOnTimestamp.should.equal(now);
});
it("should work as expected when created from a connection string", async function(): Promise<
void
> {
const cs =
"Endpoint=sb://hostname.servicebus.windows.net/;SharedAccessKeyName=sakName;SharedAccessKey=sak;EntityPath=ep";
const tokenProvider = SharedKeyCredential.fromConnectionString(cs);
const now = Math.floor(Date.now() / 1000) + 3600;
const tokenInfo = tokenProvider.getToken("sb://hostname.servicebus.windows.net/");
tokenInfo.token.should.match(
/SharedAccessSignature sr=sb%3A%2F%2Fhostname.servicebus.windows.net%2F&sig=(.*)&se=\d{10}&skn=sakName/g
);
tokenInfo.expiresOnTimestamp.should.equal(now);
});
it("SharedAccessSignatureCredential", () => {
const sasCred = new SharedAccessSignatureCredential("SharedAccessSignature se=<blah>");
const accessToken = sasCred.getToken("audience isn't used");
should.equal(
accessToken.token,
"SharedAccessSignature se=<blah>",
"SAS URI we were constructed with should just be returned verbatim without interpretation (and the audience is ignored)"
);
should.equal(
accessToken.expiresOnTimestamp,
0,
"SAS URI always returns 0 for expiry (ignoring what's in the SAS token)"
);
// these just exist because we're a SharedKeyCredential but we don't currently
// parse any attributes out (they're available but we've carved out a spot so
// they're not needed.)
should.equal(sasCred.key, "");
should.equal(sasCred.keyName, "");
});
});

Просмотреть файл

@ -8,7 +8,8 @@ import {
ReceiverEvents,
ReceiverOptions
} from "rhea-promise";
import { DefaultDataTransformer, AccessToken, Constants } from "@azure/core-amqp";
import { DefaultDataTransformer, Constants } from "@azure/core-amqp";
import { AccessToken } from "@azure/core-auth";
import { EventEmitter } from "events";
import { getUniqueName } from "../../src/util/utils";
import { Link } from "rhea-promise/typings/lib/link";

Просмотреть файл

@ -18,7 +18,7 @@ import {
getRandomTestClientTypeWithNoSessions
} from "./utils/testutils2";
import { getDeliveryProperty } from "./utils/misc";
import { isNode } from "@azure/core-amqp";
import { isNode } from "../src/util/utils";
import { verifyMessageCount } from "./utils/managementUtils";
import sinon from "sinon";