TokenProviders - Adding many features, including interactive login (#167)

* -Added InteractiveLoginProvider
-Restructured Token Providers to support more features and avoid code duplication
-Restructured connection builder to be more type safe and less verbose

* -Added more tests (with many more to come)
-More robust options
-Add toString for connection string builder

* Added no cred login and aad federated security

* -Added init for callback provider
-Added tests for certificate

* Added tests for all connection builders and fixed bugs

* Added more tests and context

* Added more tests

* Added tests to complete the coverage

* Error fixes

* Update security.ts

* Changed order back to avoid breaking change

* Rename to fit with other sdks
This commit is contained in:
AsafMah 2022-02-21 10:38:40 +02:00 коммит произвёл GitHub
Родитель adaced569e
Коммит b0e5a096a7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 1177 добавлений и 203 удалений

5
.github/workflows/build.yml поставляемый
Просмотреть файл

@ -25,6 +25,9 @@ jobs:
npm i
npm run lint
npm run testPipeline
env:
AUTO_TEST: true
LOGIN_TEST: true
- working-directory: ./azure-kusto-ingest
run: |
npm i
@ -38,6 +41,8 @@ jobs:
TENANT_ID: "72f988bf-86f1-41af-91ab-2d7cd011db47"
ENGINE_CONNECTION_STRING: "https://sdkse2etest.eastus.kusto.windows.net"
DM_CONNECTION_STRING: "https://ingest-sdkse2etest.eastus.kusto.windows.net"
AUTO_TEST: true
LOGIN_TEST: true
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
- name: Publish Unit Test Results

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

@ -141,7 +141,10 @@ export class KustoClient {
headers["x-ms-client-request-id"] = clientRequestId || clientRequestPrefix + `${uuidv4()}`;
headers.Authorization = await this.aadHelper._getAuthHeader();
const authHeader = await this.aadHelper.getAuthHeader();
if (authHeader != null) {
headers.Authorization = authHeader;
}
return this._doRequest(endpoint, executionType, headers, payloadContent, timeout, properties);
}

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

@ -2,87 +2,111 @@
// Licensed under the MIT License.
import { DeviceCodeResponse } from "@azure/msal-common";
import { KeyOfType } from "./typeUtilts";
interface MappingType {
propName: string,
mappedTo: string,
validNames: string[]
validNames: string[],
isSecret?: boolean,
isBool?: boolean,
}
const KeywordMapping: { [name: string]: MappingType } = Object.freeze({
type KcsbMappedKeys = KeyOfType<KustoConnectionStringBuilder, string | boolean | undefined>;
// This type gurantess that we don't have properties in KeywordMapping that don't exist in KustoConnectionStringBuilder
type KeywordMappingRecordType = Partial<Record<KcsbMappedKeys, MappingType>>;
const KeywordMapping: KeywordMappingRecordType = Object.freeze<Readonly<KeywordMappingRecordType>>({
dataSource: {
propName: "dataSource",
mappedTo: "Data Source",
validNames: ["data source", "addr", "address", "network address", "server"]
validNames: ["data source", "addr", "address", "network address", "server"],
},
aadFederatedSecurity: {
mappedTo: "AAD Federated Security",
validNames: ["aad federated security", "federated security", "federated", "fed", "aadfed"],
isBool: true,
},
aadUserId: {
propName: "aadUserId",
mappedTo: "AAD User ID",
validNames: ["aad user id"]
validNames: ["aad user id"],
},
password: {
propName: "password",
mappedTo: "Password",
validNames: ["password", "pwd"]
validNames: ["password", "pwd"],
isSecret: true,
},
applicationClientId: {
propName: "applicationClientId",
mappedTo: "Application Client Id",
validNames: ["application client id", "appclientid"]
validNames: ["application client id", "appclientid"],
},
applicationKey: {
propName: "applicationKey",
mappedTo: "Application Key",
validNames: ["application key", "appkey"]
validNames: ["application key", "appkey"],
isSecret: true,
},
applicationCertificatePrivateKey: {
propName: "applicationCertificatePrivateKey",
mappedTo: "application Certificate PrivateKey",
validNames: ["application Certificate PrivateKey"]
mappedTo: "Application Certificate PrivateKey",
validNames: ["Application Certificate PrivateKey"],
isSecret: true,
},
applicationCertificateThumbprint: {
propName: "applicationCertificateThumbprint",
mappedTo: "Application Certificate Thumbprint",
validNames: ["application certificate thumbprint"]
validNames: ["application certificate thumbprint", "AppCert"],
},
applicationCertificateX5c: {
propName: "applicationCertificateX5c",
mappedTo: "Application Certificate x5c",
validNames: ["application certificate x5c"]
validNames: ["application certificate x5c", "Application Certificate Send Public Certificate", "Application Certificate SendX5c", "SendX5c"],
},
authorityId: {
propName: "authorityId",
mappedTo: "Authority Id",
validNames: ["authority id", "authorityid", "authority", "tenantid", "tenant", "tid"]
}
validNames: ["authority id", "authorityid", "authority", "tenantid", "tenant", "tid"],
},
});
const getPropName = (key: string): string => {
const getPropName = (key: string): [string, MappingType] => {
const _key = key.trim().toLowerCase();
for (const keyword of Object.keys(KeywordMapping)) {
const k = KeywordMapping[keyword];
if (k.validNames.indexOf(_key) >= 0) {
return k.propName;
const k = KeywordMapping[keyword as KcsbMappedKeys];
if (!k) {
continue;
}
if (k.validNames.map(n => n.trim().toLowerCase()).indexOf(_key) >= 0) {
return [keyword, k];
}
}
throw new Error(key);
throw new Error("Failed to get prop: " + key);
};
export class KustoConnectionStringBuilder {
[prop: string]: string | boolean | ((info: DeviceCodeResponse) => void) | undefined;
static readonly SecretReplacement = "****";
// tslint:disable-next-line:no-console
static defaultDeviceCallback: (response: DeviceCodeResponse) => void = (response) => console.log(response.message);
dataSource?: string;
aadFederatedSecurity?: boolean;
aadUserId?: string;
password?: string;
applicationClientId?: string;
msiClientId?: string;
applicationKey?: string;
applicationCertificatePrivateKey?: string;
applicationCertificateThumbprint?: string;
authorityId?: string;
applicationCertificateX5c?: string;
authorityId: string = "common";
deviceCodeCallback?: (response: DeviceCodeResponse) => void;
tokenProvider?: () => Promise<string>;
loginHint?: string;
timeoutMs?: number;
accessToken?: string;
useDeviceCodeAuth?: boolean;
useUserPromptAuth?: boolean;
useAzLoginAuth?: boolean;
useManagedIdentityAuth?: boolean;
constructor(connectionString: string) {
if (!connectionString || connectionString.trim().length === 0) throw new Error("Missing connection string");
if (connectionString.trim().length === 0) throw new Error("Missing connection string");
if (connectionString.endsWith("/") || connectionString.endsWith("\\")) {
connectionString = connectionString.slice(0, -1);
@ -92,90 +116,165 @@ export class KustoConnectionStringBuilder {
connectionString = "Data Source=" + connectionString;
}
this[KeywordMapping.authorityId.propName] = "common";
const params = connectionString.split(";");
for (const item of params) {
const kvp = item.split("=");
this[getPropName(kvp[0])] = kvp[1].trim();
const [mappingTypeName, mappingType] = getPropName(kvp[0]);
if (mappingType.isBool) {
this[mappingTypeName as KeyOfType<KustoConnectionStringBuilder, boolean | undefined>] = kvp[1].trim().toLowerCase() === "true";
} else {
this[mappingTypeName as KeyOfType<KustoConnectionStringBuilder, string | undefined>] = kvp[1]?.trim();
}
}
}
toString(removeSecrets: boolean = true): string {
return Object.entries(KeywordMapping).map(([key, mappingType]) => {
const value = this[key as KcsbMappedKeys];
if (!mappingType || value === undefined) {
return "";
}
if (mappingType.isSecret && removeSecrets) {
return `${mappingType.mappedTo}=${KustoConnectionStringBuilder.SecretReplacement}`;
}
return `${mappingType.mappedTo}=${value.toString()}`;
}).filter(x => x !== "").join(";");
}
static fromExisting(other: KustoConnectionStringBuilder): KustoConnectionStringBuilder {
return Object.assign({}, other);
}
static withAadUserPasswordAuthentication(connectionString: string, userId: string, password: string, authorityId?: string) {
if (!userId || userId.trim().length === 0) throw new Error("Invalid user");
if (!password || password.trim().length === 0) throw new Error("Invalid password");
if (userId.trim().length === 0) throw new Error("Invalid user");
if (password.trim().length === 0) throw new Error("Invalid password");
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb[KeywordMapping.aadUserId.propName] = userId;
kcsb[KeywordMapping.password.propName] = password;
kcsb[KeywordMapping.authorityId.propName] = authorityId || "common";
kcsb.aadFederatedSecurity = true;
kcsb.aadUserId = userId;
kcsb.password = password;
if (authorityId) {
kcsb.authorityId = authorityId;
}
return kcsb;
}
static withAadApplicationKeyAuthentication(connectionString: string, aadAppId: string, appKey: string, authorityId?: string) {
if (!aadAppId || aadAppId.trim().length === 0) throw new Error("Invalid app id");
if (!appKey || appKey.trim().length === 0) throw new Error("Invalid app key");
if (aadAppId.trim().length === 0) throw new Error("Invalid app id");
if (appKey.trim().length === 0) throw new Error("Invalid app key");
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb[KeywordMapping.applicationClientId.propName] = aadAppId;
kcsb[KeywordMapping.applicationKey.propName] = appKey;
kcsb[KeywordMapping.authorityId.propName] = authorityId || "common";
kcsb.aadFederatedSecurity = true;
kcsb.applicationClientId = aadAppId;
kcsb.applicationKey = appKey;
if (authorityId) {
kcsb.authorityId = authorityId;
}
return kcsb;
}
static withAadApplicationCertificateAuthentication(connectionString: string, aadAppId: string, applicationCertificatePrivateKey: string, applicationCertificateThumbprint: string, authorityId?: string, applicationCertificateX5c?: string) {
if (!aadAppId || aadAppId.trim().length === 0) throw new Error("Invalid app id");
if (!applicationCertificatePrivateKey || applicationCertificatePrivateKey.trim().length === 0) throw new Error("Invalid certificate");
if (!applicationCertificateThumbprint || applicationCertificateThumbprint.trim().length === 0) throw new Error("Invalid thumbprint");
static withAadApplicationCertificateAuthentication(
connectionString: string,
aadAppId: string,
applicationCertificatePrivateKey: string,
applicationCertificateThumbprint: string,
authorityId?: string,
applicationCertificateX5c?: string,
) {
if (aadAppId.trim().length === 0) throw new Error("Invalid app id");
if (applicationCertificatePrivateKey.trim().length === 0) throw new Error("Invalid certificate");
if (applicationCertificateThumbprint.trim().length === 0) throw new Error("Invalid thumbprint");
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb[KeywordMapping.applicationClientId.propName] = aadAppId;
kcsb[KeywordMapping.applicationCertificatePrivateKey.propName] = applicationCertificatePrivateKey;
kcsb[KeywordMapping.applicationCertificateThumbprint.propName] = applicationCertificateThumbprint;
kcsb[KeywordMapping.applicationCertificateX5c.propName] = applicationCertificateX5c;
kcsb[KeywordMapping.authorityId.propName] = authorityId || "common";
kcsb.aadFederatedSecurity = true;
kcsb.applicationClientId = aadAppId;
kcsb.applicationCertificatePrivateKey = applicationCertificatePrivateKey;
kcsb.applicationCertificateThumbprint = applicationCertificateThumbprint;
kcsb.applicationCertificateX5c = applicationCertificateX5c;
if (authorityId) {
kcsb.authorityId = authorityId;
}
return kcsb;
}
static withAadDeviceAuthentication(connectionString: string, authorityId: string = "common", deviceCodeCallback?: (response: DeviceCodeResponse) => void) {
static withAadDeviceAuthentication(connectionString: string, authorityId: string = "common", deviceCodeCallback: (response: DeviceCodeResponse) => void = KustoConnectionStringBuilder.defaultDeviceCallback) {
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb[KeywordMapping.authorityId.propName] = authorityId;
kcsb.aadFederatedSecurity = true;
kcsb.authorityId = authorityId;
kcsb.deviceCodeCallback = deviceCodeCallback;
kcsb.useDeviceCodeAuth = true
return kcsb;
}
static withAadManagedIdentities(connectionString: string, msiClientId?: string) {
static withAadManagedIdentities(connectionString: string, msiClientId?: string, authorityId?: string, timeoutMs?: number) {
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb.aadFederatedSecurity = true;
if (authorityId) {
kcsb.authorityId = authorityId;
}
kcsb.msiClientId = msiClientId;
kcsb.managedIdentity = true;
kcsb.timeoutMs = timeoutMs;
kcsb.useManagedIdentityAuth = true;
return kcsb;
}
static withAzLoginIdentity(connectionString: string) {
static withAzLoginIdentity(connectionString: string, authorityId?: string, timeoutMs?: number,) {
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb.aadFederatedSecurity = true;
kcsb.useAzLoginAuth = true;
if (authorityId) {
kcsb.authorityId = authorityId;
}
kcsb.timeoutMs = timeoutMs;
kcsb.azLoginIdentity = true;
return kcsb;
}
static withAccessToken(connectionString: string, accessToken?: string) {
static withAccessToken(connectionString: string, accessToken: string) {
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb.aadFederatedSecurity = true;
kcsb.accessToken = accessToken;
return kcsb;
}
static withTokenProvider(connectionString: string, tokenProvider: () => Promise<string>) {
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb.aadFederatedSecurity = true;
kcsb.tokenProvider = tokenProvider;
return kcsb;
}
static withUserPrompt(connectionString: string, authorityId?: string, clientId?: string, timeoutMs?: number, loginHint?: string) {
const kcsb = new KustoConnectionStringBuilder(connectionString);
kcsb.aadFederatedSecurity = true;
kcsb.useUserPromptAuth = true;
if (authorityId) {
kcsb.authorityId = authorityId;
}
kcsb.loginHint = loginHint;
kcsb.applicationClientId = clientId;
kcsb.timeoutMs = timeoutMs;
return kcsb;
}
}
export default KustoConnectionStringBuilder;

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

@ -0,0 +1,6 @@
export class KustoAuthenticationError extends Error {
constructor(message: string, public inner: Error | undefined, public tokenProviderName: string, public context: Record<string, any>) {
super(message);
this.name = "KustoAuthenticationError";
}
}

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

@ -3,9 +3,10 @@
import KustoConnectionStringBuilder from "./connectionBuilder";
import "./tokenProvider";
import * as TokenProvider from "./tokenProvider";
import { KustoAuthenticationError } from "./errors";
export class AadHelper {
tokenProvider: TokenProvider.TokenProviderBase;
tokenProvider?: TokenProvider.TokenProviderBase;
constructor(kcsb: KustoConnectionStringBuilder) {
if (!kcsb.dataSource) {
@ -19,26 +20,35 @@ export class AadHelper {
} else if (!!kcsb.applicationClientId &&
!!kcsb.applicationCertificateThumbprint && !!kcsb.applicationCertificatePrivateKey) {
this.tokenProvider = new TokenProvider.ApplicationCertificateTokenProvider(kcsb.dataSource, kcsb.applicationClientId, kcsb.applicationCertificateThumbprint, kcsb.applicationCertificatePrivateKey, kcsb.applicationCertificateX5c as string | undefined, kcsb.authorityId);
} else if (kcsb.managedIdentity) {
this.tokenProvider = new TokenProvider.MsiTokenProvider(kcsb.dataSource, kcsb.msiClientId as string | undefined);
} else if (kcsb.azLoginIdentity) {
this.tokenProvider = new TokenProvider.AzCliTokenProvider(kcsb.dataSource);
} else if (kcsb.useManagedIdentityAuth) {
this.tokenProvider = new TokenProvider.MsiTokenProvider(kcsb.dataSource, kcsb.authorityId, kcsb.msiClientId, kcsb.timeoutMs);
} else if (kcsb.useAzLoginAuth) {
this.tokenProvider = new TokenProvider.AzCliTokenProvider(kcsb.dataSource, kcsb.authorityId, undefined, kcsb.timeoutMs);
} else if (kcsb.accessToken) {
this.tokenProvider = new TokenProvider.BasicTokenProvider(kcsb.dataSource, kcsb.accessToken as string);
} else {
let callback = kcsb.deviceCodeCallback;
if (!callback) {
// tslint:disable-next-line:no-console
callback = (response) => console.log(response.message);
} else if (kcsb.useUserPromptAuth) {
this.tokenProvider = new TokenProvider.UserPromptProvider(kcsb.dataSource, kcsb.authorityId, kcsb.applicationClientId, kcsb.timeoutMs, kcsb.loginHint);
} else if (kcsb.tokenProvider) {
this.tokenProvider = new TokenProvider.CallbackTokenProvider(kcsb.dataSource, kcsb.tokenProvider);
} else if (kcsb.useDeviceCodeAuth){
if (kcsb.deviceCodeCallback === undefined) {
throw new KustoAuthenticationError("Device code authentication requires a callback function", undefined, TokenProvider.DeviceLoginTokenProvider.name, {});
}
this.tokenProvider = new TokenProvider.DeviceLoginTokenProvider(kcsb.dataSource, callback);
this.tokenProvider = new TokenProvider.DeviceLoginTokenProvider(kcsb.dataSource, kcsb.deviceCodeCallback, kcsb.authorityId);
}
}
async _getAuthHeader(): Promise<string> {
const token = await this.tokenProvider.acquireToken();
return `${token.tokenType} ${token.accessToken}`;
async getAuthHeader(): Promise<string | null> {
if (!this.tokenProvider) {
return null;
}
try {
const token = await this.tokenProvider.acquireToken();
return `${token.tokenType} ${token.accessToken}`;
} catch (e) {
throw new KustoAuthenticationError(e instanceof Error ? e.message : `${e}`, e instanceof Error ? e : undefined, this.tokenProvider.constructor.name, this.tokenProvider.context());
}
}
}
export default AadHelper;
export default AadHelper;

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

@ -1,9 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { AuthenticationResult, PublicClientApplication, ConfidentialClientApplication } from "@azure/msal-node";
import { ConfidentialClientApplication, PublicClientApplication } from "@azure/msal-node";
import { DeviceCodeResponse } from "@azure/msal-common";
import { ManagedIdentityCredential, AzureCliCredential} from "@azure/identity";
import { CloudSettings, CloudInfo } from "./cloudSettings"
import { AzureCliCredential, InteractiveBrowserCredential, ManagedIdentityCredential, TokenCredentialOptions, } from "@azure/identity";
import { CloudInfo, CloudSettings } from "./cloudSettings"
import { TokenCredential } from "@azure/core-auth";
// We want all the Token Providers in this file
/* tslint:disable:max-classes-per-file */
@ -13,6 +14,12 @@ export declare type TokenResponse = {
accessToken: string;
}
interface TokenType {
tokenType: string,
accessToken: string
}
const BEARER_TYPE = "Bearer";
/**
@ -25,10 +32,14 @@ export abstract class TokenProviderBase {
abstract acquireToken(): Promise<TokenResponse>;
context(): Record<string, any> {
return {};
}
protected constructor(kustoUri: string) {
this.kustoUri = kustoUri;
if (kustoUri != null) {
const suffix = this.kustoUri.endsWith("/") ? ".default" : "/.default";
const suffix = (!this.kustoUri.endsWith("/") ? "/" : "") + ".default";
this.scopes = [kustoUri + suffix];
}
}
@ -67,64 +78,26 @@ export class CallbackTokenProvider extends TokenProviderBase {
}
}
// MSI Token Provider obtains a token from the MSI endpoint
// The args parameter is a dictionary conforming with the ManagedIdentityCredential initializer API arguments
export class MsiTokenProvider extends TokenProviderBase {
clientId?: string;
managedIdentityCredential!: ManagedIdentityCredential;
constructor(kustoUri: string, clientId?: string) {
super(kustoUri);
this.clientId = clientId;
}
async acquireToken(): Promise<TokenResponse> {
if (this.managedIdentityCredential == null) {
this.managedIdentityCredential = this.clientId ? new ManagedIdentityCredential(this.clientId) : new ManagedIdentityCredential();
}
const msiToken = await this.managedIdentityCredential.getToken(this.kustoUri);
return { tokenType: BEARER_TYPE, accessToken: msiToken.token };
}
}
/**
* AzCli Token Provider obtains a refresh token from the AzCli cache and uses it to authenticate with MSAL
*/
export class AzCliTokenProvider extends TokenProviderBase {
azureCliCredentials!: AzureCliCredential;
constructor(kustoUri: string) {
super(kustoUri);
}
async acquireToken(): Promise<TokenResponse> {
if (this.azureCliCredentials == null) {
this.azureCliCredentials = new AzureCliCredential();
}
const response = await this.azureCliCredentials.getToken(this.scopes);
return { tokenType: BEARER_TYPE, accessToken: response.token };
}
}
/**
* Acquire a token from MSAL
*/
abstract class MsalTokenProvider extends TokenProviderBase {
cloudInfo!: CloudInfo;
authorityId?: string;
authorityId: string;
initialized: boolean;
abstract initClient(): void;
abstract acquireMsalToken(): Promise<AuthenticationResult | null>;
authorityUri!: string;
protected constructor(kustoUri: string, authorityId?: string) {
abstract initClient(): void;
abstract acquireMsalToken(): Promise<TokenType | null>;
protected constructor(kustoUri: string, authorityId: string) {
super(kustoUri);
this.initialized = false;
this.authorityId = authorityId;
}
async acquireToken(): Promise<TokenResponse> {
let token;
if (!this.initialized) {
if (this.cloudInfo == null) {
this.cloudInfo = await CloudSettings.getInstance().getCloudInfoForCluster(this.kustoUri);
@ -133,17 +106,119 @@ abstract class MsalTokenProvider extends TokenProviderBase {
resourceUri = resourceUri.replace(".kusto.", ".kustomfa.")
}
this.scopes = [resourceUri + "/.default"]
this.authorityUri = CloudSettings.getAuthorityUri(this.cloudInfo, this.authorityId);
this.initClient();
}
this.initialized = true;
}
token = await this.acquireMsalToken();
const token = await this.acquireMsalToken();
if (token) {
return { tokenType: token.tokenType, accessToken: token.accessToken }
}
throw new Error("Failed to get token from msal");
}
context(): Record<string, any> {
return { ...super.context(), kustoUri: this.kustoUri, authorityId: this.authorityId };
}
}
export abstract class AzureIdentityProvider extends MsalTokenProvider {
private credential!: TokenCredential;
protected authorityHost!: string;
constructor(kustoUri: string, authorityId: string, protected clientId?: string, private timeoutMs?: number) {
super(kustoUri, authorityId);
}
initClient(): void {
this.authorityHost = this.cloudInfo.LoginEndpoint;
this.credential = this.getCredential();
}
getCommonOptions(): { authorityHost: string; clientId: string | undefined; tenantId: string } {
return {
authorityHost: this.authorityHost,
tenantId: this.authorityId,
clientId: this.clientId,
}
}
async acquireMsalToken(): Promise<TokenType | null> {
const response = await this.credential.getToken(this.scopes, {
requestOptions: {
timeout: this.timeoutMs
},
tenantId: this.authorityId
});
if (response === null) {
throw new Error("Failed to get token from msal");
}
return { tokenType: BEARER_TYPE, accessToken: response.token };
}
context(): Record<string, any> {
let base: Record<string, any> = { ...super.context(), kustoUri: this.kustoUri, authorityId: this.authorityId };
if (this.clientId) {
base = { ...base, clientId: this.clientId };
}
if (this.timeoutMs) {
base = { ...base, timeoutMs: this.timeoutMs };
}
return base;
}
abstract getCredential(): TokenCredential;
}
/**
* MSI Token Provider obtains a token from the MSI endpoint
* The args parameter is a dictionary conforming with the ManagedIdentityCredential initializer API arguments
*/
export class MsiTokenProvider extends AzureIdentityProvider {
getCredential(): TokenCredential {
const options: TokenCredentialOptions = this.getCommonOptions();
return this.clientId ? new ManagedIdentityCredential(this.clientId, options) : new ManagedIdentityCredential(options);
}
}
/**
* AzCli Token Provider obtains a refresh token from the AzCli cache and uses it to authenticate with MSAL
*/
export class AzCliTokenProvider extends AzureIdentityProvider {
getCredential(): TokenCredential {
return new AzureCliCredential(this.getCommonOptions());
}
}
/**
* UserPromptProvider will pop up a login prompt to acquire a token.
*/
export class UserPromptProvider extends AzureIdentityProvider {
// The default port is 80, which can lead to permission errors, so we'll choose another port
readonly BrowserPort = 23145;
constructor(kustoUri: string, authorityId: string, clientId?: string, timeoutMs?: number, private loginHint?: string) {
super(kustoUri, authorityId, clientId, timeoutMs);
}
getCredential(): TokenCredential {
return new InteractiveBrowserCredential({
...this.getCommonOptions(),
loginHint: this.loginHint,
redirectUri: `http://localhost:${this.BrowserPort}/`
});
}
context(): Record<string, any> {
let base = super.context();
if (this.loginHint) {
base = { ...base, loginHint: this.loginHint };
}
return base;
}
}
/**
@ -155,7 +230,7 @@ export class UserPassTokenProvider extends MsalTokenProvider {
homeAccountId?: string;
msalClient!: PublicClientApplication;
constructor(kustoUri: string, userName: string, password: string, authorityId?: string) {
constructor(kustoUri: string, userName: string, password: string, authorityId: string) {
super(kustoUri, authorityId);
this.userName = userName;
this.password = password;
@ -165,13 +240,13 @@ export class UserPassTokenProvider extends MsalTokenProvider {
const clientConfig = {
auth: {
clientId: this.cloudInfo.KustoClientAppId,
authority: CloudSettings.getAuthorityUri(this.cloudInfo, this.authorityId),
authority: this.authorityUri,
}
};
this.msalClient = new PublicClientApplication(clientConfig);
}
async acquireMsalToken(): Promise<AuthenticationResult | null> {
async acquireMsalToken(): Promise<TokenType | null> {
let token = null;
if (this.homeAccountId != null) {
const account = await this.msalClient.getTokenCache().getAccountByHomeId(this.homeAccountId)
@ -185,6 +260,10 @@ export class UserPassTokenProvider extends MsalTokenProvider {
}
return token;
}
context(): Record<string, any> {
return { ...super.context(), userName: this.userName, homeAccountId: this.homeAccountId };
}
}
/**
@ -195,7 +274,7 @@ export class DeviceLoginTokenProvider extends MsalTokenProvider {
homeAccountId?: string;
msalClient!: PublicClientApplication;
constructor(kustoUri: string, deviceCodeCallback: (response: DeviceCodeResponse) => void, authorityId?: string) {
constructor(kustoUri: string, deviceCodeCallback: (response: DeviceCodeResponse) => void, authorityId: string) {
super(kustoUri, authorityId);
this.deviceCodeCallback = deviceCodeCallback;
}
@ -204,13 +283,13 @@ export class DeviceLoginTokenProvider extends MsalTokenProvider {
const clientConfig = {
auth: {
clientId: this.cloudInfo.KustoClientAppId,
authority: CloudSettings.getAuthorityUri(this.cloudInfo, this.authorityId),
authority: this.authorityUri,
},
};
this.msalClient = new PublicClientApplication(clientConfig);
}
async acquireMsalToken(): Promise<AuthenticationResult | null> {
async acquireMsalToken(): Promise<TokenType | null> {
let token = null;
if (this.homeAccountId != null) {
const account = await this.msalClient.getTokenCache().getAccountByHomeId(this.homeAccountId)
@ -234,7 +313,7 @@ export class ApplicationKeyTokenProvider extends MsalTokenProvider {
appKey: string;
msalClient!: ConfidentialClientApplication;
constructor(kustoUri: string, appClientId: string, appKey: string, authorityId?: string) {
constructor(kustoUri: string, appClientId: string, appKey: string, authorityId: string) {
super(kustoUri, authorityId);
this.appClientId = appClientId;
this.appKey = appKey;
@ -245,15 +324,19 @@ export class ApplicationKeyTokenProvider extends MsalTokenProvider {
auth: {
clientId: this.appClientId,
clientSecret: this.appKey,
authority: CloudSettings.getAuthorityUri(this.cloudInfo, this.authorityId),
authority: this.authorityUri,
}
};
this.msalClient = new ConfidentialClientApplication(clientConfig);
}
acquireMsalToken(): Promise<AuthenticationResult | null> {
acquireMsalToken(): Promise<TokenType | null> {
return this.msalClient.acquireTokenByClientCredential({ scopes: this.scopes });
}
context(): Record<string, any> {
return { ...super.context(), clientId: this.appClientId };
}
}
/**
@ -268,7 +351,7 @@ export class ApplicationCertificateTokenProvider extends MsalTokenProvider {
msalClient!: ConfidentialClientApplication;
constructor(kustoUri: string, appClientId: string, certThumbprint: string, certPrivateKey: string, certX5c?: string, authorityId?: string) {
super(kustoUri, authorityId);
super(kustoUri, authorityId!);
this.appClientId = appClientId;
this.certThumbprint = certThumbprint;
this.certPrivateKey = certPrivateKey;
@ -279,7 +362,7 @@ export class ApplicationCertificateTokenProvider extends MsalTokenProvider {
const clientConfig = {
auth: {
clientId: this.appClientId,
authority: CloudSettings.getAuthorityUri(this.cloudInfo, this.authorityId),
authority: this.authorityUri,
clientCertificate: {
thumbprint: this.certThumbprint,
privateKey: this.certPrivateKey,
@ -290,7 +373,11 @@ export class ApplicationCertificateTokenProvider extends MsalTokenProvider {
this.msalClient = new ConfidentialClientApplication(clientConfig);
}
acquireMsalToken(): Promise<AuthenticationResult | null> {
acquireMsalToken(): Promise<TokenType | null> {
return this.msalClient.acquireTokenByClientCredential({ scopes: this.scopes });
}
context(): Record<string, any> {
return { ...super.context(), clientId: this.appClientId, thumbprint: this.certThumbprint };
}
}

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

@ -0,0 +1,4 @@
export type KeyOfType<T, V> = keyof {
[P in keyof T as T[P] extends V? P: never]: unknown
}

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

@ -107,7 +107,7 @@ describe("KustoClient", () => {
const clientRequestProps = new ClientRequestProperties();
const timeoutMs = moment.duration(2.51, "minutes").asMilliseconds();
clientRequestProps.setTimeout(timeoutMs);
client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") };
client.aadHelper.getAuthHeader = () => { return Promise.resolve("MockToken") };
client._doRequest = (_endpoint, _executionType, _headers, payload, timeout) => {
const payloadObj = JSON.parse(payload);
assert.strictEqual(payloadObj.properties.Options.servertimeout, "00:02:30.6");
@ -125,7 +125,7 @@ describe("KustoClient", () => {
const clientRequestProps = new ClientRequestProperties();
const timeoutMs = moment.duration(2.51, "minutes").asMilliseconds();
clientRequestProps.setClientTimeout(timeoutMs);
client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") };
client.aadHelper.getAuthHeader = () => { return Promise.resolve("MockToken") };
client._doRequest = (_endpoint, _executionType, _headers, payload, timeout) => {
JSON.parse(payload);
assert.strictEqual(timeout, timeoutMs);
@ -139,7 +139,7 @@ describe("KustoClient", () => {
const url = "https://cluster.kusto.windows.net";
const client = new KustoClient(url);
client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") };
client.aadHelper.getAuthHeader = () => { return Promise.resolve("MockToken") };
client._doRequest = (_endpoint, _executionType, _headers, _payload, timeout) => {
assert.strictEqual(timeout, moment.duration(4.5, "minutes").asMilliseconds());
return Promise.resolve(new KustoResponseDataSetV2([]));
@ -151,7 +151,7 @@ describe("KustoClient", () => {
it("default timeout for admin", async () => {
const url = "https://cluster.kusto.windows.net";
const client = new KustoClient(url);
client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") };
client.aadHelper.getAuthHeader = () => { return Promise.resolve("MockToken") };
client._doRequest = (_endpoint, _executionType, _headers, _payload, timeout) => {
assert.strictEqual(timeout, moment.duration(10.5, "minutes").asMilliseconds());
return Promise.resolve(new KustoResponseDataSetV2([]));
@ -171,7 +171,7 @@ describe("KustoClient", () => {
clientRequestProps.clientRequestId = clientRequestId;
clientRequestProps.application = application;
clientRequestProps.user = user;
client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") };
client.aadHelper.getAuthHeader = () => { return Promise.resolve("MockToken") };
client._doRequest = (_endpoint, _executionType, headers) => {
assert.strictEqual(headers["x-ms-client-request-id"], clientRequestId);
assert.strictEqual(headers["x-ms-app"], application);
@ -186,7 +186,7 @@ describe("KustoClient", () => {
const url = "https://cluster.kusto.windows.net";
const client = new KustoClient(url);
client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") };
client.aadHelper.getAuthHeader = () => { return Promise.resolve("MockToken") };
client._doRequest = (endpoint, executionType, _headers, _payload, _timeout, _properties) => {
assert.strictEqual(endpoint, `${url}/v1/rest/query`);
assert.strictEqual(executionType, ExecutionType.QueryV1);

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

@ -1,11 +1,129 @@
/* tslint:disable:no-console */
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import assert from "assert";
import { v4 as uuidv4 } from 'uuid';
import {KustoConnectionStringBuilder} from "../source/connectionBuilder";
import { KustoConnectionStringBuilder } from "../source/connectionBuilder";
import { DeviceCodeResponse } from "@azure/msal-common";
function doComparsion(
kcsbs: KustoConnectionStringBuilder[],
expectedProperties: Partial<Record<keyof KustoConnectionStringBuilder, unknown>>,
expectedToString: string,
expectedToStringWithSecrets: string
) {
for (const [i, kcsb] of kcsbs.entries()) {
console.log(`Checking connection string #${i} - ${kcsb.toString(false)}`);
const clone = KustoConnectionStringBuilder.fromExisting(kcsb);
const emptyFields = [
"aadUserId",
"applicationClientId",
"password",
"msiClientId",
"applicationKey",
"applicationCertificatePrivateKey",
"applicationCertificateThumbprint",
"applicationCertificateX5c",
"deviceCodeCallback",
"loginHint",
"timeoutMs",
"accessToken",
"isAzLoginIdentity",
"isManagedIdentity",
"isInteractiveLogin",
"isDeviceCode"
];
for (const entry of Object.entries(expectedProperties)) {
const [key, value] = entry;
const kcsbEntry = kcsb[key as keyof KustoConnectionStringBuilder];
if (typeof kcsbEntry === "function") {
assert.notStrictEqual(kcsbEntry, undefined, `${key} is not defined`);
continue;
}
assert.strictEqual(kcsbEntry, value, `${key} is not equal to ${value}`);
assert.strictEqual(clone[key as keyof KustoConnectionStringBuilder], value, `${key} is not equal to ${value} in clone`);
}
for (const field of emptyFields.filter(f => !(f in expectedProperties))) {
assert.strictEqual(kcsb[field as keyof KustoConnectionStringBuilder], undefined, `${field} should be undefined`);
assert.strictEqual(clone[field as keyof KustoConnectionStringBuilder], undefined, `${field} should be undefined in clone`);
}
assert.strictEqual(
kcsb.toString(),
expectedToString
)
assert.strictEqual(
kcsb.toString(false),
expectedToStringWithSecrets
)
}
}
describe("KustoConnectionStringBuilder", () => {
describe("validation tests", () => {
it("throws when empty connection string is provided", () => {
assert.throws(() => new KustoConnectionStringBuilder(" "), Error, "Missing connection string");
});
it("removes trailing dashes from data source", () => {
const kcsbForward = new KustoConnectionStringBuilder("https://test.kusto.windows.net/");
assert.strictEqual(kcsbForward.dataSource, "https://test.kusto.windows.net");
const kcsbBack = new KustoConnectionStringBuilder("https://test.kusto.windows.net\\");
assert.strictEqual(kcsbBack.dataSource, "https://test.kusto.windows.net");
});
it("throws when user or password is empty", () => {
assert.throws(
() => KustoConnectionStringBuilder.withAadUserPasswordAuthentication("https://test.kusto.windows.net/", " ", "password"),
Error,
"Invalid user"
);
assert.throws(
() => KustoConnectionStringBuilder.withAadUserPasswordAuthentication("https://test.kusto.windows.net/", "user", " "),
Error,
"Invalid password"
);
});
it("throws when appId or appKey is empty", () => {
assert.throws(
() => KustoConnectionStringBuilder.withAadApplicationKeyAuthentication("https://test.kusto.windows.net/", " ", "password"),
Error,
"Invalid app id"
);
assert.throws(() => KustoConnectionStringBuilder.withAadApplicationKeyAuthentication(
"https://test.kusto.windows.net/",
"53e12945-98b5-4d5c-9465-fd6b6edf848e",
" "
), Error, "Invalid app key");
});
it("throws when certificate values are empty", () => {
assert.throws(
() => KustoConnectionStringBuilder.withAadApplicationCertificateAuthentication("https://test.kusto.windows.net/", " ", "private", "thumb"),
Error,
"Invalid app id"
);
assert.throws(
() => KustoConnectionStringBuilder.withAadApplicationCertificateAuthentication("https://test.kusto.windows.net/", "53e12945-98b5-4d5c-9465-fd6b6edf848e", " ", "thumb"),
Error,
"Invalid app certificate"
);
assert.throws(
() => KustoConnectionStringBuilder.withAadApplicationCertificateAuthentication("https://test.kusto.windows.net/", "53e12945-98b5-4d5c-9465-fd6b6edf848e", "private", " "),
Error,
"Invalid app thumbprint"
);
});
});
describe("#constructor(connectionString)", () => {
it("from string with no creds", () => {
const kcsbs = [
@ -13,74 +131,472 @@ describe("KustoConnectionStringBuilder", () => {
new KustoConnectionStringBuilder("data Source=localhost"),
new KustoConnectionStringBuilder("Addr=localhost"),
new KustoConnectionStringBuilder("Addr = localhost"),
KustoConnectionStringBuilder.withAadDeviceAuthentication("localhost", "common"),
];
for (const kcsb of kcsbs) {
assert.strictEqual(kcsb.dataSource, "localhost");
assert.strictEqual(kcsb.authorityId, "common");
const emptyFields = ["aadUserId", "password", "applicationClientId", "applicationKey"];
for (const field of emptyFields) {
assert.strictEqual(kcsb[field], undefined);
}
}
doComparsion(
kcsbs,
{
dataSource: "localhost",
authorityId: "common"
},
"Data Source=localhost;Authority Id=common",
"Data Source=localhost;Authority Id=common"
);
});
it("from string with username auth", () => {
describe("from username auth", () => {
const expectedUser = "test";
const expectedPassword = "Pa$$w0rd";
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;AAD User ID=${expectedUser};password=${expectedPassword}`),
new KustoConnectionStringBuilder(`Data Source=localhost ; AaD User ID=${expectedUser}; Password =${expectedPassword}`),
new KustoConnectionStringBuilder(` Addr = localhost ; AAD User ID = ${expectedUser} ; Pwd =${expectedPassword}`),
new KustoConnectionStringBuilder(`Network Address = localhost; AAD User iD = ${expectedUser} ; Pwd = ${expectedPassword} `),
KustoConnectionStringBuilder.withAadUserPasswordAuthentication("localhost", expectedUser, expectedPassword),
];
const kcsb1 = new KustoConnectionStringBuilder("Server=localhost");
kcsb1.aadUserId = expectedUser;
kcsb1.password = expectedPassword;
kcsbs.push(kcsb1);
for (const kcsb of kcsbs) {
assert.strictEqual(kcsb.dataSource, "localhost");
assert.strictEqual(kcsb.aadUserId, expectedUser);
assert.strictEqual(kcsb.password, expectedPassword);
assert.strictEqual(kcsb.authorityId, "common");
const emptyFields = ["applicationClientId", "applicationKey"];
for (const field of emptyFields) {
assert.strictEqual(kcsb[field], undefined);
}
}
it("without authority id", () => {
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;AAD User ID=${expectedUser};password=${expectedPassword};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`Data Source=localhost ; AaD User ID=${expectedUser}; Password =${expectedPassword};AAD Federated Security=True`),
new KustoConnectionStringBuilder(` Addr = localhost ; AAD User ID = ${expectedUser} ; Pwd =${expectedPassword};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`Network Address = localhost; AAD User iD = ${expectedUser} ; Pwd = ${expectedPassword};AAD Federated Security=True `),
KustoConnectionStringBuilder.withAadUserPasswordAuthentication("localhost", expectedUser, expectedPassword),
];
const kcsb1 = new KustoConnectionStringBuilder("Server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.aadUserId = expectedUser;
kcsb1.password = expectedPassword;
kcsbs.push(kcsb1);
doComparsion(
kcsbs,
{
dataSource: "localhost",
authorityId: "common",
aadUserId: expectedUser,
password: expectedPassword,
aadFederatedSecurity: true
},
`Data Source=localhost;AAD Federated Security=true;AAD User ID=${expectedUser};Password=****;Authority Id=common`,
`Data Source=localhost;AAD Federated Security=true;AAD User ID=${expectedUser};Password=${expectedPassword};Authority Id=common`
);
});
it("with authority id", () => {
const expectedAuthorityId = "test-authority";
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;AAD User ID=${expectedUser};password=${expectedPassword};Authority Id=${expectedAuthorityId};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`Data Source=localhost ; AaD User ID=${expectedUser}; Password =${expectedPassword};authority=${expectedAuthorityId};AAD Federated Security=True`),
new KustoConnectionStringBuilder(` Addr = localhost ; AAD User ID = ${expectedUser} ; Pwd =${expectedPassword};tenantid=${expectedAuthorityId};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`Network Address = localhost; AAD User iD = ${expectedUser} ; Pwd = ${expectedPassword};tid=${expectedAuthorityId};AAD Federated Security=True `),
KustoConnectionStringBuilder.withAadUserPasswordAuthentication("localhost", expectedUser, expectedPassword, expectedAuthorityId),
];
const kcsb1 = new KustoConnectionStringBuilder("Server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.aadUserId = expectedUser;
kcsb1.password = expectedPassword;
kcsb1.authorityId = expectedAuthorityId;
kcsbs.push(kcsb1);
doComparsion(
kcsbs,
{
dataSource: "localhost",
authorityId: expectedAuthorityId,
aadUserId: expectedUser,
password: expectedPassword,
aadFederatedSecurity: true
},
`Data Source=localhost;AAD Federated Security=true;AAD User ID=${expectedUser};Password=****;Authority Id=${expectedAuthorityId}`,
`Data Source=localhost;AAD Federated Security=true;AAD User ID=${expectedUser};Password=${expectedPassword};Authority Id=${expectedAuthorityId}`
);
})
});
it("from string with app auth", () => {
const uuid = uuidv4();
const key = "key of application";
describe("from app key auth", () => {
const expectedUuid = uuidv4();
const expectedKey = "key of application";
it("without authority id", () => {
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;Application client Id=${expectedUuid};application Key=${expectedKey};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`Data Source=localhost ; Application Client Id=${expectedUuid}; Appkey =${expectedKey};AAD Federated Security=True`),
new KustoConnectionStringBuilder(` Addr = localhost ; AppClientId = ${expectedUuid} ; AppKey =${expectedKey};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`Network Address = localhost; AppClientId = ${expectedUuid} ; AppKey =${expectedKey};AAD Federated Security=True`),
KustoConnectionStringBuilder.withAadApplicationKeyAuthentication("localhost", expectedUuid, expectedKey)
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.applicationClientId = expectedUuid;
kcsb1.applicationKey = expectedKey;
kcsbs.push(kcsb1);
doComparsion(
kcsbs,
{
dataSource: "localhost",
applicationClientId: expectedUuid,
applicationKey: expectedKey,
aadFederatedSecurity: true
},
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${expectedUuid};Application Key=****;Authority Id=common`,
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${expectedUuid};Application Key=${expectedKey};Authority Id=common`
);
})
it("with authority id", () => {
const expectedAuthorityId = "test-authority";
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;Application client Id=${expectedUuid};application Key=${expectedKey};Authority Id=${expectedAuthorityId};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`Data Source=localhost ; Application Client Id=${expectedUuid}; Appkey =${expectedKey};authority=${expectedAuthorityId};AAD Federated Security=True`),
new KustoConnectionStringBuilder(` Addr = localhost ; AppClientId = ${expectedUuid} ; AppKey =${expectedKey};tenantid=${expectedAuthorityId};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`Network Address = localhost; AppClientId = ${expectedUuid} ; AppKey =${expectedKey};tid=${expectedAuthorityId};AAD Federated Security=True `),
KustoConnectionStringBuilder.withAadApplicationKeyAuthentication("localhost", expectedUuid, expectedKey, expectedAuthorityId)
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.applicationClientId = expectedUuid;
kcsb1.applicationKey = expectedKey;
kcsb1.authorityId = expectedAuthorityId;
kcsbs.push(kcsb1);
doComparsion(
kcsbs,
{
dataSource: "localhost",
applicationClientId: expectedUuid,
applicationKey: expectedKey,
aadFederatedSecurity: true
},
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${expectedUuid};Application Key=****;Authority Id=${expectedAuthorityId}`,
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${expectedUuid};Application Key=${expectedKey};Authority Id=${expectedAuthorityId}`
);
})
});
describe("from certificate auth", () => {
const appId = uuidv4();
const privateKey = "some private key";
const thumbPrint = "thumbprint";
const expectedAuthorityId = "test-authority";
const cert5xc = "5xc";
it("with authority id", () => {
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;Application client Id=${appId};application Certificate PrivateKey=${privateKey};application certificate thumbprint=${thumbPrint};Authority Id=${expectedAuthorityId};application certificate x5c=${cert5xc};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`localhost;AppClientId=${appId};Application Certificate PrivateKey=${privateKey};appcert=${thumbPrint};Authority Id=${expectedAuthorityId};SendX5c=${cert5xc};AAD Federated Security=True`),
KustoConnectionStringBuilder.withAadApplicationCertificateAuthentication(
"localhost",
appId,
privateKey,
thumbPrint,
expectedAuthorityId,
cert5xc,
)
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.applicationClientId = appId;
kcsb1.authorityId = expectedAuthorityId;
kcsb1.applicationCertificatePrivateKey = privateKey;
kcsb1.applicationCertificateThumbprint = thumbPrint;
kcsb1.applicationCertificateX5c = cert5xc;
kcsbs.push(kcsb1);
doComparsion(
kcsbs,
{
dataSource: "localhost",
applicationClientId: appId,
applicationCertificatePrivateKey: privateKey,
applicationCertificateThumbprint: thumbPrint,
authorityId: expectedAuthorityId,
applicationCertificateX5c: cert5xc,
aadFederatedSecurity: true
},
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${appId};Application Certificate PrivateKey=****;Application Certificate Thumbprint=${thumbPrint};Application Certificate x5c=${cert5xc};Authority Id=${expectedAuthorityId}`,
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${appId};Application Certificate PrivateKey=${privateKey};Application Certificate Thumbprint=${thumbPrint};Application Certificate x5c=${cert5xc};Authority Id=${expectedAuthorityId}`
);
})
it("without authority id", () => {
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;Application client Id=${appId};application Certificate PrivateKey=${privateKey};application certificate thumbprint=${thumbPrint};application certificate x5c=${cert5xc};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`localhost;AppClientId=${appId};Application Certificate PrivateKey=${privateKey};appcert=${thumbPrint};SendX5c=${cert5xc};AAD Federated Security=True`),
KustoConnectionStringBuilder.withAadApplicationCertificateAuthentication(
"localhost",
appId,
privateKey,
thumbPrint,
"common",
cert5xc,
)
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.applicationClientId = appId;
kcsb1.applicationCertificatePrivateKey = privateKey;
kcsb1.applicationCertificateThumbprint = thumbPrint;
kcsb1.applicationCertificateX5c = cert5xc;
kcsbs.push(kcsb1);
doComparsion(
kcsbs,
{
dataSource: "localhost",
applicationClientId: appId,
applicationCertificatePrivateKey: privateKey,
applicationCertificateThumbprint: thumbPrint,
authorityId: "common",
applicationCertificateX5c: cert5xc,
aadFederatedSecurity: true
},
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${appId};Application Certificate PrivateKey=****;Application Certificate Thumbprint=${thumbPrint};Application Certificate x5c=${cert5xc};Authority Id=common`,
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${appId};Application Certificate PrivateKey=${privateKey};Application Certificate Thumbprint=${thumbPrint};Application Certificate x5c=${cert5xc};Authority Id=common`
);
})
it("without 3xc", () => {
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;Application client Id=${appId};application Certificate PrivateKey=${privateKey};application certificate thumbprint=${thumbPrint};AAD Federated Security=True`),
new KustoConnectionStringBuilder(`localhost;AppClientId=${appId};Application Certificate PrivateKey=${privateKey};appcert=${thumbPrint};AAD Federated Security=True`),
KustoConnectionStringBuilder.withAadApplicationCertificateAuthentication(
"localhost",
appId,
privateKey,
thumbPrint,
"common",
)
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.applicationClientId = appId;
kcsb1.applicationCertificatePrivateKey = privateKey;
kcsb1.applicationCertificateThumbprint = thumbPrint;
kcsbs.push(kcsb1);
doComparsion(
kcsbs,
{
dataSource: "localhost",
applicationClientId: appId,
applicationCertificatePrivateKey: privateKey,
applicationCertificateThumbprint: thumbPrint,
authorityId: "common",
aadFederatedSecurity: true
},
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${appId};Application Certificate PrivateKey=****;Application Certificate Thumbprint=${thumbPrint};Authority Id=common`,
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${appId};Application Certificate PrivateKey=${privateKey};Application Certificate Thumbprint=${thumbPrint};Authority Id=common`
);
})
});
it("from aad device auth", () => {
const kcsbs = [
new KustoConnectionStringBuilder(`localhost;Application client Id=${uuid};application Key=${key}`),
new KustoConnectionStringBuilder(`Data Source=localhost ; Application Client Id=${uuid}; Appkey =${key}`),
new KustoConnectionStringBuilder(` Addr = localhost ; AppClientId = ${uuid} ; AppKey =${key}`),
new KustoConnectionStringBuilder(`Network Address = localhost; AppClientId = ${uuid} ; AppKey =${key}`),
KustoConnectionStringBuilder.withAadApplicationKeyAuthentication("localhost", uuid, key)
KustoConnectionStringBuilder.withAadDeviceAuthentication("localhost", "common"),
KustoConnectionStringBuilder.withAadDeviceAuthentication("localhost", "common", (res) => res),
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.applicationClientId = uuid;
kcsb1.applicationKey = key;
kcsbs.push(kcsb1);
kcsb1.aadFederatedSecurity = true;
kcsb1.useDeviceCodeAuth = true;
kcsb1.deviceCodeCallback = (res) => res;
for (const kcsb of kcsbs) {
assert.strictEqual(kcsb.dataSource, "localhost");
assert.strictEqual(kcsb.applicationClientId, uuid);
assert.strictEqual(kcsb.applicationKey, key);
assert.strictEqual(kcsb.authorityId, "common");
const emptyFields = ["aadUserId", "password"];
for (const field of emptyFields) {
assert.strictEqual(kcsb[field], undefined);
}
}
doComparsion(
kcsbs,
{
dataSource: "localhost",
authorityId: "common",
useDeviceCodeAuth: true,
aadFederatedSecurity: true,
deviceCodeCallback: (res: DeviceCodeResponse) => res
},
"Data Source=localhost;AAD Federated Security=true;Authority Id=common",
"Data Source=localhost;AAD Federated Security=true;Authority Id=common"
);
});
describe("from msi auth", () => {
it("without clientId and timeout", () => {
const kcsbs = [
KustoConnectionStringBuilder.withAadManagedIdentities("localhost"),
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.useManagedIdentityAuth = true;
doComparsion(
kcsbs,
{
dataSource: "localhost",
authorityId: "common",
useManagedIdentityAuth: true,
aadFederatedSecurity: true,
},
"Data Source=localhost;AAD Federated Security=true;Authority Id=common",
"Data Source=localhost;AAD Federated Security=true;Authority Id=common"
)
});
it("with clientId and timeout", () => {
const msiClientId = "clientId";
const timeoutMs = 10;
const kcsbs = [
KustoConnectionStringBuilder.withAadManagedIdentities("localhost", msiClientId, "common", timeoutMs),
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.useManagedIdentityAuth = true;
kcsb1.msiClientId = msiClientId;
kcsb1.timeoutMs = timeoutMs;
doComparsion(
kcsbs,
{
dataSource: "localhost",
authorityId: "common",
useManagedIdentityAuth: true,
aadFederatedSecurity: true,
msiClientId,
timeoutMs
},
"Data Source=localhost;AAD Federated Security=true;Authority Id=common",
"Data Source=localhost;AAD Federated Security=true;Authority Id=common"
)
});
});
describe("from az cli", () => {
const timeout = 10000;
const authorityId = "common";
const kcsbs = [
KustoConnectionStringBuilder.withAzLoginIdentity("localhost", authorityId, timeout),
KustoConnectionStringBuilder.withAzLoginIdentity("localhost", undefined, timeout),
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.useAzLoginAuth = true;
kcsb1.authorityId = authorityId;
kcsb1.timeoutMs = timeout;
doComparsion(
kcsbs,
{
dataSource: "localhost",
aadFederatedSecurity: true,
authorityId,
timeoutMs: timeout,
useAzLoginAuth: true,
},
"Data Source=localhost;AAD Federated Security=true;Authority Id=common",
"Data Source=localhost;AAD Federated Security=true;Authority Id=common"
);
});
describe("from access token", () => {
const token = "some_token";
const kcsbs = [
KustoConnectionStringBuilder.withAccessToken("localhost", token),
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.accessToken = token;
doComparsion(
kcsbs,
{
dataSource: "localhost",
aadFederatedSecurity: true,
accessToken: token,
},
"Data Source=localhost;AAD Federated Security=true;Authority Id=common",
"Data Source=localhost;AAD Federated Security=true;Authority Id=common"
);
});
describe("from token provider", () => {
const tokenProvider = () => Promise.resolve("some_token");
const kcsbs = [
KustoConnectionStringBuilder.withTokenProvider("localhost", tokenProvider),
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.tokenProvider = tokenProvider;
doComparsion(
kcsbs,
{
dataSource: "localhost",
aadFederatedSecurity: true,
tokenProvider,
},
"Data Source=localhost;AAD Federated Security=true;Authority Id=common",
"Data Source=localhost;AAD Federated Security=true;Authority Id=common"
);
});
describe("interactive login", () => {
it("without optional params", () => {
const kcsbs = [
KustoConnectionStringBuilder.withUserPrompt("localhost", "common"),
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.useUserPromptAuth = true;
doComparsion(
kcsbs,
{
dataSource: "localhost",
authorityId: "common",
useUserPromptAuth: true,
aadFederatedSecurity: true,
},
"Data Source=localhost;AAD Federated Security=true;Authority Id=common",
"Data Source=localhost;AAD Federated Security=true;Authority Id=common"
)
});
it("with optional params", () => {
const clientId = "clientId";
const loginHint = "myUser";
const timeoutMs = 10;
const kcsbs = [
KustoConnectionStringBuilder.withUserPrompt("localhost", "common", clientId, timeoutMs, loginHint),
];
const kcsb1 = new KustoConnectionStringBuilder("server=localhost");
kcsb1.aadFederatedSecurity = true;
kcsb1.useUserPromptAuth = true;
kcsb1.applicationClientId = clientId;
kcsb1.timeoutMs = timeoutMs;
kcsb1.loginHint = loginHint;
doComparsion(
kcsbs,
{
dataSource: "localhost",
authorityId: "common",
useUserPromptAuth: true,
aadFederatedSecurity: true,
applicationClientId: clientId,
timeoutMs,
loginHint
},
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${clientId};Authority Id=common`,
`Data Source=localhost;AAD Federated Security=true;Application Client Id=${clientId};Authority Id=common`
)
});
});
});
});

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

@ -0,0 +1,13 @@
const loginTestEnv = "LOGIN_TEST";
const autoTestEnv = "AUTO_TEST";
export const testIfEnv = (definedEnv: string[], undefinedEnv: string[]) => {
if (definedEnv.filter(x => process.env[x]).length === definedEnv.length && undefinedEnv.filter(x => !process.env[x]).length === undefinedEnv.length) {
return it;
} else {
return it.skip;
}
};
export const manualLoginTest = (...definedEnv: string[]) => testIfEnv([loginTestEnv, ...definedEnv], [autoTestEnv]);
export const loginTest = (...definedEnv: string[]) => testIfEnv([loginTestEnv, ...definedEnv], []);

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

@ -0,0 +1,231 @@
import { KustoConnectionStringBuilder } from "../index";
import AadHelper from "../source/security";
import { CloudSettings } from "../source/cloudSettings";
import assert from "assert";
import { ServerError } from "@azure/msal-node";
import { KustoAuthenticationError } from "../source/errors";
import { CredentialUnavailableError } from "@azure/identity";
import { loginTest, manualLoginTest } from "./data/testUtils";
describe("test errors", () => {
before(() => {
CloudSettings.getInstance().cloudCache["https://somecluster.kusto.windows.net"] = CloudSettings.getInstance().defaultCloudInfo;
});
it("no data source", async () => {
const kcsb = new KustoConnectionStringBuilder("test");
kcsb.dataSource = "";
assert.throws(() => new AadHelper(kcsb), Error, "Invalid string builder - missing dataSource");
});
it("test user pass", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const username = "username@microsoft.com";
const kcsb = KustoConnectionStringBuilder.withAadUserPasswordAuthentication(cluster, username, "password", "organizations");
const helper = new AadHelper(kcsb);
try {
await helper.getAuthHeader();
assert.fail("should throw unauthorized exception");
} catch (e: unknown) {
assert.ok(e instanceof KustoAuthenticationError);
assert.ok(e.inner instanceof ServerError);
assert.strictEqual(e.tokenProviderName, "UserPassTokenProvider")
assert.strictEqual(e.context.userName, username);
}
}).timeout(10000);
it("test app key", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const appId = "86f7361f-15b7-4f10-aef5-3ce66ac73766";
const key = "private_key";
const kcsb = KustoConnectionStringBuilder.withAadApplicationKeyAuthentication(cluster, appId, key, "organizations");
const helper = new AadHelper(kcsb);
try {
await helper.getAuthHeader();
assert.fail("should throw unauthorized exception");
} catch (e: unknown) {
assert.ok(e instanceof KustoAuthenticationError);
assert.ok(e.inner instanceof ServerError);
assert.strictEqual(e.tokenProviderName, "ApplicationKeyTokenProvider")
assert.strictEqual(e.context.clientId, appId);
}
}).timeout(10000);
it("test app certificate", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const appId = "86f7361f-15b7-4f10-aef5-3ce66ac73766";
const thumb = "thumb";
const privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp\n" +
"wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5\n" +
"1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh\n" +
"3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2\n" +
"pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX\n" +
"GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il\n" +
"AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF\n" +
"L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k\n" +
"X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl\n" +
"U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ\n" +
"37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=\n" +
"-----END RSA PRIVATE KEY-----";
const kcsb = KustoConnectionStringBuilder.withAadApplicationCertificateAuthentication(cluster, appId, privateKey, thumb, "organizations");
const helper = new AadHelper(kcsb);
try {
await helper.getAuthHeader();
assert.fail("should throw unauthorized exception");
} catch (e: unknown) {
assert.ok(e instanceof KustoAuthenticationError);
assert.ok(e.inner instanceof ServerError);
assert.strictEqual(e.tokenProviderName, "ApplicationCertificateTokenProvider")
assert.strictEqual(e.context.clientId, appId);
assert.strictEqual(e.context.thumbprint, thumb);
}
}).timeout(10000);
it("device code without function", () => {
const kcsb = new KustoConnectionStringBuilder("https://somecluster.kusto.windows.net");
kcsb.aadFederatedSecurity = true;
kcsb.authorityId = "common";
kcsb.useDeviceCodeAuth = true
assert.throws(() => new AadHelper(kcsb), KustoAuthenticationError, "Device code authentication is not supported without a function");
});
it("test msi", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const clientId = "86f7361f-15b7-4f10-aef5-3ce66ac73766";
const kcsb = KustoConnectionStringBuilder.withAadManagedIdentities(cluster, clientId, "organizations", 1);
const helper = new AadHelper(kcsb);
try {
await helper.getAuthHeader();
assert.fail("should throw unauthorized exception");
} catch (e: unknown) {
assert.ok(e instanceof KustoAuthenticationError);
assert.ok(e.inner instanceof CredentialUnavailableError);
assert.strictEqual(e.tokenProviderName, "MsiTokenProvider")
assert.strictEqual(e.context.clientId, clientId);
}
}).timeout(10000);
});
describe("Test providers", () => {
before(() => {
CloudSettings.getInstance().cloudCache["https://somecluster.kusto.windows.net"] = CloudSettings.getInstance().defaultCloudInfo;
});
it("test null", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = new KustoConnectionStringBuilder(cluster);
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.strictEqual(token, null);
});
it("test access token", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withAccessToken(cluster, "somekey");
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.strictEqual(token, "Bearer somekey");
});
it("test callback token provider", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withTokenProvider(cluster, () => Promise.resolve("somekey"));
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.strictEqual(token, "Bearer somekey");
});
loginTest("APP_ID", "TENANT_ID", "APP_KEY")("test app key token provider", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withAadApplicationKeyAuthentication(
cluster,
process.env.APP_ID!,
process.env.APP_KEY!,
process.env.TENANT_ID!,
);
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.notStrictEqual(token, null);
}).timeout(10000);
manualLoginTest("APP_ID", "TENANT_ID", "CERT_THUMBPRINT", "CERT_PUBLIC", "CERT_PEM")("test app certificate token provider", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withAadApplicationCertificateAuthentication(
cluster,
process.env.APP_KEY!,
process.env.CERT_PEM!,
process.env.CERT_THUMBPRINT!,
process.env.CERT_PUBLIC!
);
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.notStrictEqual(token, null);
}).timeout(10000);
manualLoginTest("APP_ID", "USER_NAME", "USER_PASS")("test user pass provider", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withAadUserPasswordAuthentication(
cluster,
process.env.USER_NAME!,
process.env.USER_PASS!,
process.env.TENANT_ID!,
);
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.notStrictEqual(token, null);
}).timeout(10000);
manualLoginTest()("test az login", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withAzLoginIdentity(cluster, "organizations");
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.notStrictEqual(token, null);
}).timeout(30000);
manualLoginTest()("test device code", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withAadDeviceAuthentication(cluster, "organizations");
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.notStrictEqual(token, null);
}).timeout(30000);
manualLoginTest()("test user prompt", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withUserPrompt(cluster, "organizations");
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.notStrictEqual(token, null);
}).timeout(30000);
manualLoginTest("TEST_MSI")("test msi", async () => {
const cluster = "https://somecluster.kusto.windows.net";
const kcsb = KustoConnectionStringBuilder.withAadManagedIdentities(cluster, undefined,"organizations");
const helper = new AadHelper(kcsb);
const token = await helper.getAuthHeader();
assert.notStrictEqual(token, null);
}).timeout(30000);
});

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

@ -27,7 +27,7 @@ describe("CloudInfo", () => {
}
assert.strictEqual(provider.scopes[0], "https://fakeurl.kusto.windows.net/.default");
});
}).timeout(5000);
it("mfa off", async () => {
const fakeUri2 = "https://fakeurl2.kusto.windows.net"
@ -49,6 +49,6 @@ describe("CloudInfo", () => {
}
assert.strictEqual(provider.scopes[0], "https://fakeurl.kustomfa.windows.net/.default");
});
}).timeout(5000);
});
});

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

@ -195,7 +195,7 @@ describe("ManagedStreamingIngestClient", () => {
describe("helper methods", () => {
it("should be able to create a ManagedStreamingIngestClient from a DM URI", () => {
const client = KustoManagedStreamingIngestClient.fromDmConnectionString(KustoConnectionStringBuilder.withAccessToken("https://ingest-dummy.kusto.windows.net"));
const client = KustoManagedStreamingIngestClient.fromDmConnectionString(new KustoConnectionStringBuilder("https://ingest-dummy.kusto.windows.net"));
assert.strictEqual((client as any).queuedIngestClient.resourceManager.kustoClient.connectionString.dataSource,
"https://ingest-dummy.kusto.windows.net");
@ -203,10 +203,10 @@ describe("ManagedStreamingIngestClient", () => {
"https://dummy.kusto.windows.net");
});
it("should fail when trying to create a ManagedStreamingIngestClient from an invalid DM URI", () => {
assert.throws(() => KustoManagedStreamingIngestClient.fromDmConnectionString(KustoConnectionStringBuilder.withAccessToken("https://dummy.kusto.windows.net")));
assert.throws(() => KustoManagedStreamingIngestClient.fromDmConnectionString(new KustoConnectionStringBuilder("https://dummy.kusto.windows.net")));
});
it("should be able to create a ManagedStreamingIngestClient from an Engine URI", () => {
const client = KustoManagedStreamingIngestClient.fromEngineConnectionString(KustoConnectionStringBuilder.withAccessToken("https://dummy.kusto.windows.net"));
const client = KustoManagedStreamingIngestClient.fromEngineConnectionString(new KustoConnectionStringBuilder("https://dummy.kusto.windows.net"));
assert.strictEqual((client as any).queuedIngestClient.resourceManager.kustoClient.connectionString.dataSource,
"https://ingest-dummy.kusto.windows.net");
@ -214,7 +214,7 @@ describe("ManagedStreamingIngestClient", () => {
"https://dummy.kusto.windows.net");
});
it("should fail when trying to create a ManagedStreamingIngestClient from an invalid Engine URI", () => {
assert.throws(() => KustoManagedStreamingIngestClient.fromEngineConnectionString(KustoConnectionStringBuilder.withAccessToken("https://ingest-dummy.kusto.windows.net")));
assert.throws(() => KustoManagedStreamingIngestClient.fromEngineConnectionString(new KustoConnectionStringBuilder("https://ingest-dummy.kusto.windows.net")));
});
});
});