mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-27 21:01:57 +00:00
599 lines
21 KiB
TypeScript
599 lines
21 KiB
TypeScript
import * as Constants from "../../Common/Constants";
|
|
import AuthHeadersUtil from "./Authorization";
|
|
import Q from "q";
|
|
import {
|
|
AccessInputMetadata,
|
|
AccountKeys,
|
|
ApiKind,
|
|
DatabaseAccount,
|
|
GenerateTokenResponse,
|
|
resourceTokenConnectionStringProperties
|
|
} from "../../Contracts/DataModels";
|
|
import { AuthType } from "../../AuthType";
|
|
import { CollectionCreation } from "../../Shared/Constants";
|
|
import { isInvalidParentFrameOrigin } from "../../Utils/MessageValidation";
|
|
import { DataExplorerInputsFrame } from "../../Contracts/ViewModels";
|
|
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
|
|
import { HostedUtils } from "./HostedUtils";
|
|
import { sendMessage } from "../../Common/MessageHandler";
|
|
import { MessageTypes } from "../../Contracts/ExplorerContracts";
|
|
import { SessionStorageUtility, StorageKey } from "../../Shared/StorageUtility";
|
|
import { SubscriptionUtilMappings } from "../../Shared/Constants";
|
|
import "../../Explorer/Tables/DataTable/DataTableBindingManager";
|
|
import Explorer from "../../Explorer/Explorer";
|
|
import { updateUserContext } from "../../UserContext";
|
|
import { configContext } from "../../ConfigContext";
|
|
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
|
|
|
export default class Main {
|
|
private static _databaseAccountId: string;
|
|
private static _encryptedToken: string;
|
|
private static _accessInputMetadata: AccessInputMetadata;
|
|
private static _features: { [key: string]: string };
|
|
// For AAD, Need to post message to hosted frame to do the auth
|
|
// Use local deferred variable as work around until we find better solution
|
|
private static _getAadAccessDeferred: Q.Deferred<Explorer>;
|
|
private static _explorer: Explorer;
|
|
|
|
public static isUsingEncryptionToken(): boolean {
|
|
const params = new URLSearchParams(window.parent.location.search);
|
|
if ((!!params && params.has("key")) || Main._hasCachedEncryptedKey()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static initializeExplorer(): Q.Promise<Explorer> {
|
|
window.addEventListener("message", this._handleMessage.bind(this), false);
|
|
this._features = {};
|
|
const params = new URLSearchParams(window.parent.location.search);
|
|
const deferred: Q.Deferred<Explorer> = Q.defer<Explorer>();
|
|
let authType: string = null;
|
|
|
|
// Encrypted token flow
|
|
if (!!params && params.has("key")) {
|
|
Main._encryptedToken = encodeURIComponent(params.get("key"));
|
|
SessionStorageUtility.setEntryString(StorageKey.EncryptedKeyToken, Main._encryptedToken);
|
|
authType = AuthType.EncryptedToken;
|
|
} else if (Main._hasCachedEncryptedKey()) {
|
|
Main._encryptedToken = SessionStorageUtility.getEntryString(StorageKey.EncryptedKeyToken);
|
|
authType = AuthType.EncryptedToken;
|
|
}
|
|
|
|
// Aad flow
|
|
if (AuthHeadersUtil.isUserSignedIn()) {
|
|
authType = AuthType.AAD;
|
|
}
|
|
|
|
if (params) {
|
|
this._features = Main.extractFeatures(params);
|
|
}
|
|
|
|
(<any>window).authType = authType;
|
|
if (!authType) {
|
|
return Q.reject("Sign in needed");
|
|
}
|
|
|
|
const explorer: Explorer = this._instantiateExplorer();
|
|
if (authType === AuthType.EncryptedToken) {
|
|
sendMessage({
|
|
type: MessageTypes.UpdateAccountSwitch,
|
|
props: {
|
|
authType: AuthType.EncryptedToken,
|
|
displayText: "Loading..."
|
|
}
|
|
});
|
|
updateUserContext({
|
|
accessToken: Main._encryptedToken
|
|
});
|
|
Main._getAccessInputMetadata(Main._encryptedToken).then(
|
|
() => {
|
|
const expiryTimestamp: number =
|
|
Main._accessInputMetadata && parseInt(Main._accessInputMetadata.expiryTimestamp);
|
|
if (authType === AuthType.EncryptedToken && (isNaN(expiryTimestamp) || expiryTimestamp <= 0)) {
|
|
return deferred.reject("Token expired");
|
|
}
|
|
|
|
Main._initDataExplorerFrameInputs(explorer);
|
|
deferred.resolve(explorer);
|
|
},
|
|
(error: any) => {
|
|
console.error(error);
|
|
deferred.reject(error);
|
|
}
|
|
);
|
|
} else if (authType === AuthType.AAD) {
|
|
sendMessage({
|
|
type: MessageTypes.GetAccessAadRequest
|
|
});
|
|
if (this._getAadAccessDeferred != null) {
|
|
// already request aad access, don't duplicate
|
|
return Q(null);
|
|
}
|
|
this._explorer = explorer;
|
|
this._getAadAccessDeferred = Q.defer<Explorer>();
|
|
return this._getAadAccessDeferred.promise.finally(() => {
|
|
this._getAadAccessDeferred = null;
|
|
});
|
|
} else {
|
|
Main._initDataExplorerFrameInputs(explorer);
|
|
deferred.resolve(explorer);
|
|
}
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
public static extractFeatures(params: URLSearchParams): { [key: string]: string } {
|
|
const featureParamRegex = /feature.(.*)/i;
|
|
const features: { [key: string]: string } = {};
|
|
params.forEach((value: string, param: string) => {
|
|
if (featureParamRegex.test(param)) {
|
|
const matches: string[] = param.match(featureParamRegex);
|
|
if (matches.length > 0) {
|
|
features[matches[1].toLowerCase()] = value;
|
|
}
|
|
}
|
|
});
|
|
return features;
|
|
}
|
|
|
|
public static configureTokenValidationDisplayPrompt(explorer: Explorer): void {
|
|
const authType: AuthType = (<any>window).authType;
|
|
if (
|
|
!explorer ||
|
|
!Main._encryptedToken ||
|
|
!Main._accessInputMetadata ||
|
|
Main._accessInputMetadata.expiryTimestamp == null ||
|
|
authType !== AuthType.EncryptedToken
|
|
) {
|
|
return;
|
|
}
|
|
|
|
Main._showGuestAccessTokenRenewalPromptInMs(explorer, parseInt(Main._accessInputMetadata.expiryTimestamp));
|
|
}
|
|
|
|
public static parseResourceTokenConnectionString(connectionString: string): resourceTokenConnectionStringProperties {
|
|
let accountEndpoint: string;
|
|
let collectionId: string;
|
|
let databaseId: string;
|
|
let partitionKey: string;
|
|
let resourceToken: string;
|
|
const connectionStringParts = connectionString.split(";");
|
|
connectionStringParts.forEach((part: string) => {
|
|
if (part.startsWith("type=resource")) {
|
|
resourceToken = part + ";";
|
|
} else if (part.startsWith("AccountEndpoint=")) {
|
|
accountEndpoint = part.substring(16);
|
|
} else if (part.startsWith("DatabaseId=")) {
|
|
databaseId = part.substring(11);
|
|
} else if (part.startsWith("CollectionId=")) {
|
|
collectionId = part.substring(13);
|
|
} else if (part.startsWith("PartitionKey=")) {
|
|
partitionKey = part.substring(13);
|
|
} else if (part !== "") {
|
|
resourceToken += part + ";";
|
|
}
|
|
});
|
|
|
|
return {
|
|
accountEndpoint,
|
|
collectionId,
|
|
databaseId,
|
|
partitionKey,
|
|
resourceToken
|
|
};
|
|
}
|
|
|
|
public static renewExplorerAccess = (explorer: Explorer, connectionString: string): Q.Promise<void> => {
|
|
if (!connectionString) {
|
|
console.error("Missing or invalid connection string input");
|
|
Q.reject("Missing or invalid connection string input");
|
|
}
|
|
|
|
if (Main._isResourceToken(connectionString)) {
|
|
return Main._renewExplorerAccessWithResourceToken(explorer, connectionString);
|
|
}
|
|
|
|
const deferred: Q.Deferred<void> = Q.defer<void>();
|
|
AuthHeadersUtil.generateUnauthenticatedEncryptedTokenForConnectionString(connectionString).then(
|
|
(encryptedToken: GenerateTokenResponse) => {
|
|
if (!encryptedToken || !encryptedToken.readWrite) {
|
|
deferred.reject("Encrypted token is empty or undefined");
|
|
}
|
|
|
|
Main._encryptedToken = encryptedToken.readWrite;
|
|
window.authType = AuthType.EncryptedToken;
|
|
|
|
updateUserContext({
|
|
accessToken: Main._encryptedToken
|
|
});
|
|
Main._getAccessInputMetadata(Main._encryptedToken).then(
|
|
() => {
|
|
if (explorer.isConnectExplorerVisible()) {
|
|
explorer.notificationConsoleData([]);
|
|
explorer.hideConnectExplorerForm();
|
|
}
|
|
|
|
if (Main._accessInputMetadata.apiKind != ApiKind.Graph) {
|
|
// do not save encrypted token for graphs because we cannot extract master key in the client
|
|
SessionStorageUtility.setEntryString(StorageKey.EncryptedKeyToken, Main._encryptedToken);
|
|
window.parent &&
|
|
window.parent.history.replaceState(
|
|
{ encryptedToken: encryptedToken },
|
|
"",
|
|
`?key=${Main._encryptedToken}${(window.parent && window.parent.location.hash) || ""}`
|
|
); // replace query params if any
|
|
} else {
|
|
SessionStorageUtility.removeEntry(StorageKey.EncryptedKeyToken);
|
|
window.parent &&
|
|
window.parent.history.replaceState(
|
|
{ encryptedToken: encryptedToken },
|
|
"",
|
|
`?${(window.parent && window.parent.location.hash) || ""}`
|
|
); // replace query params if any
|
|
}
|
|
|
|
const masterKey: string = Main._getMasterKeyFromConnectionString(connectionString);
|
|
Main.configureTokenValidationDisplayPrompt(explorer);
|
|
Main._setExplorerReady(explorer, masterKey);
|
|
|
|
deferred.resolve();
|
|
},
|
|
(error: any) => {
|
|
console.error(error);
|
|
deferred.reject(error);
|
|
}
|
|
);
|
|
},
|
|
(error: any) => {
|
|
deferred.reject(`Failed to generate encrypted token: ${getErrorMessage(error)}`);
|
|
}
|
|
);
|
|
|
|
return deferred.promise.timeout(Constants.ClientDefaults.requestTimeoutMs);
|
|
};
|
|
|
|
public static getUninitializedExplorerForGuestAccess(): Explorer {
|
|
const explorer = Main._instantiateExplorer();
|
|
if (window.authType === AuthType.AAD) {
|
|
this._explorer = explorer;
|
|
}
|
|
(<any>window).dataExplorer = explorer;
|
|
|
|
return explorer;
|
|
}
|
|
|
|
private static _initDataExplorerFrameInputs(
|
|
explorer: Explorer,
|
|
masterKey?: string /* master key extracted from connection string if available */,
|
|
account?: DatabaseAccount,
|
|
authorizationToken?: string /* access key */
|
|
): void {
|
|
const serverId: string = AuthHeadersUtil.serverId;
|
|
const authType: string = (<any>window).authType;
|
|
const accountResourceId =
|
|
authType === AuthType.EncryptedToken
|
|
? Main._databaseAccountId
|
|
: authType === AuthType.AAD && account
|
|
? account.id
|
|
: "";
|
|
const subscriptionId: string = accountResourceId && accountResourceId.split("subscriptions/")[1].split("/")[0];
|
|
const resourceGroup: string = accountResourceId && accountResourceId.split("resourceGroups/")[1].split("/")[0];
|
|
|
|
explorer.isTryCosmosDBSubscription(SubscriptionUtilMappings.FreeTierSubscriptionIds.indexOf(subscriptionId) >= 0);
|
|
if (authorizationToken && authorizationToken.indexOf("Bearer") !== 0) {
|
|
// Portal sends the auth token with bearer suffix, so we prepend the same to be consistent
|
|
authorizationToken = `Bearer ${authorizationToken}`;
|
|
}
|
|
|
|
if (authType === AuthType.EncryptedToken) {
|
|
const apiExperience: string = DefaultExperienceUtility.getDefaultExperienceFromApiKind(
|
|
Main._accessInputMetadata.apiKind
|
|
);
|
|
sendMessage({
|
|
type: MessageTypes.UpdateAccountSwitch,
|
|
props: {
|
|
authType: AuthType.EncryptedToken,
|
|
selectedAccountName: Main._accessInputMetadata.accountName
|
|
}
|
|
});
|
|
return explorer.initDataExplorerWithFrameInputs({
|
|
databaseAccount: {
|
|
id: Main._databaseAccountId,
|
|
name: Main._accessInputMetadata.accountName,
|
|
kind: this._getDatabaseAccountKindFromExperience(apiExperience),
|
|
properties: HostedUtils.getDatabaseAccountPropertiesFromMetadata(Main._accessInputMetadata),
|
|
tags: { defaultExperience: apiExperience }
|
|
},
|
|
subscriptionId,
|
|
resourceGroup,
|
|
masterKey,
|
|
hasWriteAccess: true, // TODO: we should embed this information in the token ideally
|
|
authorizationToken: undefined,
|
|
features: this._features,
|
|
csmEndpoint: undefined,
|
|
dnsSuffix: null,
|
|
serverId: serverId,
|
|
extensionEndpoint: configContext.BACKEND_ENDPOINT,
|
|
subscriptionType: CollectionCreation.DefaultSubscriptionType,
|
|
quotaId: undefined,
|
|
addCollectionDefaultFlight: explorer.flight(),
|
|
isTryCosmosDBSubscription: explorer.isTryCosmosDBSubscription()
|
|
});
|
|
}
|
|
|
|
if (authType === AuthType.AAD) {
|
|
const inputs: DataExplorerInputsFrame = {
|
|
databaseAccount: account,
|
|
subscriptionId,
|
|
resourceGroup,
|
|
masterKey,
|
|
hasWriteAccess: true, //TODO: 425017 - support read access
|
|
authorizationToken,
|
|
features: this._features,
|
|
csmEndpoint: undefined,
|
|
dnsSuffix: null,
|
|
serverId: serverId,
|
|
extensionEndpoint: configContext.BACKEND_ENDPOINT,
|
|
subscriptionType: CollectionCreation.DefaultSubscriptionType,
|
|
quotaId: undefined,
|
|
addCollectionDefaultFlight: explorer.flight(),
|
|
isTryCosmosDBSubscription: explorer.isTryCosmosDBSubscription()
|
|
};
|
|
return explorer.initDataExplorerWithFrameInputs(inputs);
|
|
}
|
|
|
|
if (authType === AuthType.ResourceToken) {
|
|
const apiExperience: string = DefaultExperienceUtility.getDefaultExperienceFromApiKind(
|
|
Main._accessInputMetadata.apiKind
|
|
);
|
|
return explorer.initDataExplorerWithFrameInputs({
|
|
databaseAccount: {
|
|
id: Main._databaseAccountId,
|
|
name: Main._accessInputMetadata.accountName,
|
|
kind: this._getDatabaseAccountKindFromExperience(apiExperience),
|
|
properties: HostedUtils.getDatabaseAccountPropertiesFromMetadata(Main._accessInputMetadata),
|
|
tags: { defaultExperience: apiExperience }
|
|
},
|
|
subscriptionId,
|
|
resourceGroup,
|
|
masterKey,
|
|
hasWriteAccess: true, // TODO: we should embed this information in the token ideally
|
|
authorizationToken: undefined,
|
|
features: this._features,
|
|
csmEndpoint: undefined,
|
|
dnsSuffix: null,
|
|
serverId: serverId,
|
|
extensionEndpoint: configContext.BACKEND_ENDPOINT,
|
|
subscriptionType: CollectionCreation.DefaultSubscriptionType,
|
|
quotaId: undefined,
|
|
addCollectionDefaultFlight: explorer.flight(),
|
|
isTryCosmosDBSubscription: explorer.isTryCosmosDBSubscription(),
|
|
isAuthWithresourceToken: true
|
|
});
|
|
}
|
|
|
|
throw new Error(`Unsupported AuthType ${authType}`);
|
|
}
|
|
|
|
private static _instantiateExplorer(): Explorer {
|
|
const explorer = new Explorer();
|
|
// workaround to resolve cyclic refs with view
|
|
explorer.renewExplorerShareAccess = Main.renewExplorerAccess;
|
|
window.addEventListener("message", explorer.handleMessage.bind(explorer), false);
|
|
|
|
// Hosted needs click to dismiss any menu
|
|
if (window.authType === AuthType.AAD) {
|
|
window.addEventListener(
|
|
"click",
|
|
() => {
|
|
sendMessage({
|
|
type: MessageTypes.ExplorerClickEvent
|
|
});
|
|
},
|
|
true
|
|
);
|
|
}
|
|
|
|
return explorer;
|
|
}
|
|
|
|
private static _showGuestAccessTokenRenewalPromptInMs(explorer: Explorer, interval: number): void {
|
|
if (interval != null && !isNaN(interval)) {
|
|
setTimeout(() => {
|
|
explorer.displayGuestAccessTokenRenewalPrompt();
|
|
}, interval);
|
|
}
|
|
}
|
|
|
|
private static _hasCachedEncryptedKey(): boolean {
|
|
return SessionStorageUtility.hasItem(StorageKey.EncryptedKeyToken);
|
|
}
|
|
|
|
private static _getDatabaseAccountKindFromExperience(apiExperience: string): string {
|
|
if (apiExperience === Constants.DefaultAccountExperience.MongoDB) {
|
|
return Constants.AccountKind.MongoDB;
|
|
}
|
|
|
|
if (apiExperience === Constants.DefaultAccountExperience.ApiForMongoDB) {
|
|
return Constants.AccountKind.MongoDB;
|
|
}
|
|
|
|
return Constants.AccountKind.GlobalDocumentDB;
|
|
}
|
|
|
|
private static _getAccessInputMetadata(accessInput: string): Q.Promise<void> {
|
|
const deferred: Q.Deferred<void> = Q.defer<void>();
|
|
AuthHeadersUtil.getAccessInputMetadata(accessInput).then(
|
|
(metadata: any) => {
|
|
Main._accessInputMetadata = metadata;
|
|
deferred.resolve();
|
|
},
|
|
(error: any) => {
|
|
deferred.reject(error);
|
|
}
|
|
);
|
|
|
|
return deferred.promise.timeout(Constants.ClientDefaults.requestTimeoutMs);
|
|
}
|
|
|
|
private static _getMasterKeyFromConnectionString(connectionString: string): string {
|
|
if (!connectionString || Main._accessInputMetadata == null || Main._accessInputMetadata.apiKind !== ApiKind.Graph) {
|
|
// client only needs master key for Graph API
|
|
return undefined;
|
|
}
|
|
|
|
const matchedParts: string[] = connectionString.match("AccountKey=(.*);ApiKind=Gremlin;$");
|
|
return (matchedParts.length > 1 && matchedParts[1]) || undefined;
|
|
}
|
|
|
|
private static _isResourceToken(connectionString: string): boolean {
|
|
return connectionString && connectionString.includes("type=resource");
|
|
}
|
|
|
|
private static _renewExplorerAccessWithResourceToken = (
|
|
explorer: Explorer,
|
|
connectionString: string
|
|
): Q.Promise<void> => {
|
|
window.authType = AuthType.ResourceToken;
|
|
|
|
const properties: resourceTokenConnectionStringProperties = Main.parseResourceTokenConnectionString(
|
|
connectionString
|
|
);
|
|
if (
|
|
!properties.accountEndpoint ||
|
|
!properties.resourceToken ||
|
|
!properties.databaseId ||
|
|
!properties.collectionId
|
|
) {
|
|
console.error("Invalid connection string input");
|
|
Q.reject("Invalid connection string input");
|
|
}
|
|
updateUserContext({
|
|
resourceToken: properties.resourceToken,
|
|
endpoint: properties.accountEndpoint
|
|
});
|
|
explorer.resourceTokenDatabaseId(properties.databaseId);
|
|
explorer.resourceTokenCollectionId(properties.collectionId);
|
|
if (properties.partitionKey) {
|
|
explorer.resourceTokenPartitionKey(properties.partitionKey);
|
|
}
|
|
Main._accessInputMetadata = Main._getAccessInputMetadataFromAccountEndpoint(properties.accountEndpoint);
|
|
|
|
if (explorer.isConnectExplorerVisible()) {
|
|
explorer.notificationConsoleData([]);
|
|
explorer.hideConnectExplorerForm();
|
|
}
|
|
|
|
Main._setExplorerReady(explorer);
|
|
return Q.resolve();
|
|
};
|
|
|
|
private static _getAccessInputMetadataFromAccountEndpoint = (accountEndpoint: string): AccessInputMetadata => {
|
|
const documentEndpoint: string = accountEndpoint;
|
|
const result: RegExpMatchArray = accountEndpoint.match("https://([^\\.]+)\\..+");
|
|
const accountName: string = result && result[1];
|
|
const apiEndpoint: string = accountEndpoint.substring(8);
|
|
const apiKind: number = ApiKind.SQL;
|
|
|
|
return {
|
|
accountName,
|
|
apiEndpoint,
|
|
apiKind,
|
|
documentEndpoint,
|
|
expiryTimestamp: ""
|
|
};
|
|
};
|
|
|
|
private static _setExplorerReady(
|
|
explorer: Explorer,
|
|
masterKey?: string,
|
|
account?: DatabaseAccount,
|
|
authorizationToken?: string
|
|
) {
|
|
Main._initDataExplorerFrameInputs(explorer, masterKey, account, authorizationToken);
|
|
explorer.isAccountReady.valueHasMutated();
|
|
sendMessage("ready");
|
|
}
|
|
|
|
private static _shouldProcessMessage(event: MessageEvent): boolean {
|
|
if (typeof event.data !== "object") {
|
|
return false;
|
|
}
|
|
if (event.data["signature"] !== "pcIframe") {
|
|
return false;
|
|
}
|
|
if (!("data" in event.data)) {
|
|
return false;
|
|
}
|
|
if (typeof event.data["data"] !== "object") {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static _handleMessage(event: MessageEvent) {
|
|
if (isInvalidParentFrameOrigin(event)) {
|
|
return;
|
|
}
|
|
|
|
if (!this._shouldProcessMessage(event)) {
|
|
return;
|
|
}
|
|
|
|
const message: any = event.data.data;
|
|
if (message.type) {
|
|
if (message.type === MessageTypes.GetAccessAadResponse && (message.response || message.error)) {
|
|
if (message.response) {
|
|
Main._handleGetAccessAadSucceed(message.response);
|
|
}
|
|
if (message.error) {
|
|
Main._handleGetAccessAadFailed(message.error);
|
|
}
|
|
return;
|
|
}
|
|
if (message.type === MessageTypes.SwitchAccount && message.account && message.keys) {
|
|
Main._handleSwitchAccountSucceed(message.account, message.keys, message.authorizationToken);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static _handleSwitchAccountSucceed(account: DatabaseAccount, keys: AccountKeys, authorizationToken: string) {
|
|
if (!this._explorer) {
|
|
console.error("no explorer found");
|
|
return;
|
|
}
|
|
|
|
this._explorer.hideConnectExplorerForm();
|
|
|
|
const masterKey = Main._getMasterKey(keys);
|
|
this._explorer.notificationConsoleData([]);
|
|
Main._setExplorerReady(this._explorer, masterKey, account, authorizationToken);
|
|
}
|
|
|
|
private static _handleGetAccessAadSucceed(response: [DatabaseAccount, AccountKeys, string]) {
|
|
if (!response || response.length < 1) {
|
|
return;
|
|
}
|
|
const account = response[0];
|
|
const masterKey = Main._getMasterKey(response[1]);
|
|
const authorizationToken = response[2];
|
|
Main._setExplorerReady(this._explorer, masterKey, account, authorizationToken);
|
|
this._getAadAccessDeferred.resolve(this._explorer);
|
|
}
|
|
|
|
private static _getMasterKey(keys: AccountKeys): string {
|
|
return (
|
|
keys?.primaryMasterKey ??
|
|
keys?.secondaryMasterKey ??
|
|
keys?.primaryReadonlyMasterKey ??
|
|
keys?.secondaryReadonlyMasterKey
|
|
);
|
|
}
|
|
|
|
private static _handleGetAccessAadFailed(error: any) {
|
|
this._getAadAccessDeferred.reject(error);
|
|
}
|
|
}
|