Checkpoint

This commit is contained in:
Steve Faulkner
2020-12-30 19:12:22 -06:00
parent 7116f25ce4
commit 585f75bc91
9 changed files with 156 additions and 208 deletions

View File

@@ -1,12 +1,12 @@
import * as DataModels from "../../../Contracts/DataModels";
import { ConnectionStringParser } from "./ConnectionStringParser";
import { parseConnectionString } from "./ConnectionStringParser";
describe("ConnectionStringParser", () => {
const mockAccountName: string = "Test";
const mockMasterKey: string = "some-key";
it("should parse a valid sql account connection string", () => {
const metadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString(
const metadata = parseConnectionString(
`AccountEndpoint=https://${mockAccountName}.documents.azure.com:443/;AccountKey=${mockMasterKey};`
);
@@ -15,7 +15,7 @@ describe("ConnectionStringParser", () => {
});
it("should parse a valid mongo account connection string", () => {
const metadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString(
const metadata = parseConnectionString(
`mongodb://${mockAccountName}:${mockMasterKey}@${mockAccountName}.documents.azure.com:10255`
);
@@ -24,7 +24,7 @@ describe("ConnectionStringParser", () => {
});
it("should parse a valid compute mongo account connection string", () => {
const metadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString(
const metadata = parseConnectionString(
`mongodb://${mockAccountName}:${mockMasterKey}@${mockAccountName}.mongo.cosmos.azure.com:10255`
);
@@ -33,7 +33,7 @@ describe("ConnectionStringParser", () => {
});
it("should parse a valid graph account connection string", () => {
const metadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString(
const metadata = parseConnectionString(
`AccountEndpoint=https://${mockAccountName}.documents.azure.com:443/;AccountKey=${mockMasterKey};ApiKind=Gremlin;`
);
@@ -42,7 +42,7 @@ describe("ConnectionStringParser", () => {
});
it("should parse a valid table account connection string", () => {
const metadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString(
const metadata = parseConnectionString(
`DefaultEndpointsProtocol=https;AccountName=${mockAccountName};AccountKey=${mockMasterKey};TableEndpoint=https://${mockAccountName}.table.cosmosdb.azure.com:443/;`
);
@@ -51,7 +51,7 @@ describe("ConnectionStringParser", () => {
});
it("should parse a valid cassandra account connection string", () => {
const metadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString(
const metadata = parseConnectionString(
`AccountEndpoint=${mockAccountName}.cassandra.cosmosdb.azure.com;AccountKey=${mockMasterKey};`
);
@@ -60,15 +60,13 @@ describe("ConnectionStringParser", () => {
});
it("should fail to parse an invalid connection string", () => {
const metadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString(
"some-rogue-connection-string"
);
const metadata = parseConnectionString("some-rogue-connection-string");
expect(metadata).toBe(undefined);
});
it("should fail to parse an empty connection string", () => {
const metadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString("");
const metadata = parseConnectionString("");
expect(metadata).toBe(undefined);
});

View File

@@ -1,50 +1,48 @@
import * as Constants from "../../../Common/Constants";
import * as DataModels from "../../../Contracts/DataModels";
import { AccessInputMetadata, ApiKind } from "../../../Contracts/DataModels";
export class ConnectionStringParser {
public static parseConnectionString(connectionString: string): DataModels.AccessInputMetadata {
if (!!connectionString) {
try {
const accessInput: DataModels.AccessInputMetadata = {} as DataModels.AccessInputMetadata;
const connectionStringParts = connectionString.split(";");
export function parseConnectionString(connectionString: string): AccessInputMetadata {
if (connectionString) {
try {
const accessInput = {} as AccessInputMetadata;
const connectionStringParts = connectionString.split(";");
connectionStringParts.forEach((connectionStringPart: string) => {
if (RegExp(Constants.EndpointsRegex.sql).test(connectionStringPart)) {
accessInput.accountName = connectionStringPart.match(Constants.EndpointsRegex.sql)[1];
accessInput.apiKind = DataModels.ApiKind.SQL;
} else if (RegExp(Constants.EndpointsRegex.mongo).test(connectionStringPart)) {
const matches: string[] = connectionStringPart.match(Constants.EndpointsRegex.mongo);
accessInput.accountName = matches && matches.length > 1 && matches[2];
accessInput.apiKind = DataModels.ApiKind.MongoDB;
} else if (RegExp(Constants.EndpointsRegex.mongoCompute).test(connectionStringPart)) {
const matches: string[] = connectionStringPart.match(Constants.EndpointsRegex.mongoCompute);
accessInput.accountName = matches && matches.length > 1 && matches[2];
accessInput.apiKind = DataModels.ApiKind.MongoDBCompute;
} else if (Constants.EndpointsRegex.cassandra.some(regex => RegExp(regex).test(connectionStringPart))) {
Constants.EndpointsRegex.cassandra.forEach(regex => {
if (RegExp(regex).test(connectionStringPart)) {
accessInput.accountName = connectionStringPart.match(regex)[1];
accessInput.apiKind = DataModels.ApiKind.Cassandra;
}
});
} else if (RegExp(Constants.EndpointsRegex.table).test(connectionStringPart)) {
accessInput.accountName = connectionStringPart.match(Constants.EndpointsRegex.table)[1];
accessInput.apiKind = DataModels.ApiKind.Table;
} else if (connectionStringPart.indexOf("ApiKind=Gremlin") >= 0) {
accessInput.apiKind = DataModels.ApiKind.Graph;
}
});
if (Object.keys(accessInput).length === 0) {
return undefined;
connectionStringParts.forEach((connectionStringPart: string) => {
if (RegExp(Constants.EndpointsRegex.sql).test(connectionStringPart)) {
accessInput.accountName = connectionStringPart.match(Constants.EndpointsRegex.sql)[1];
accessInput.apiKind = ApiKind.SQL;
} else if (RegExp(Constants.EndpointsRegex.mongo).test(connectionStringPart)) {
const matches: string[] = connectionStringPart.match(Constants.EndpointsRegex.mongo);
accessInput.accountName = matches && matches.length > 1 && matches[2];
accessInput.apiKind = ApiKind.MongoDB;
} else if (RegExp(Constants.EndpointsRegex.mongoCompute).test(connectionStringPart)) {
const matches: string[] = connectionStringPart.match(Constants.EndpointsRegex.mongoCompute);
accessInput.accountName = matches && matches.length > 1 && matches[2];
accessInput.apiKind = ApiKind.MongoDBCompute;
} else if (Constants.EndpointsRegex.cassandra.some(regex => RegExp(regex).test(connectionStringPart))) {
Constants.EndpointsRegex.cassandra.forEach(regex => {
if (RegExp(regex).test(connectionStringPart)) {
accessInput.accountName = connectionStringPart.match(regex)[1];
accessInput.apiKind = ApiKind.Cassandra;
}
});
} else if (RegExp(Constants.EndpointsRegex.table).test(connectionStringPart)) {
accessInput.accountName = connectionStringPart.match(Constants.EndpointsRegex.table)[1];
accessInput.apiKind = ApiKind.Table;
} else if (connectionStringPart.indexOf("ApiKind=Gremlin") >= 0) {
accessInput.apiKind = ApiKind.Graph;
}
});
return accessInput;
} catch (error) {
if (Object.keys(accessInput).length === 0) {
return undefined;
}
}
return undefined;
return accessInput;
} catch (error) {
return undefined;
}
}
return undefined;
}

View File

@@ -43,84 +43,40 @@ export default class Main {
return false;
}
public static initializeExplorer(): Q.Promise<Explorer> {
public static initializeExplorer(): 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;
}
const params = new URLSearchParams(window.location.search);
let authType: string = params && params.get("authType");
if (params) {
this._features = Main.extractFeatures(params);
}
// Encrypted token flow
if (params && params.has("key")) {
Main._encryptedToken = encodeURIComponent(params.get("key"));
Main._accessInputMetadata = JSON.parse(params.get("metadata"));
authType = AuthType.EncryptedToken;
}
(<any>window).authType = authType;
if (!authType) {
return Q.reject("Sign in needed");
throw new Error("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);
}
);
Main._initDataExplorerFrameInputs(explorer);
} 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;
return explorer;
}
public static extractFeatures(params: URLSearchParams): { [key: string]: string } {
@@ -137,21 +93,6 @@ export default class Main {
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;
@@ -234,7 +175,6 @@ export default class Main {
}
const masterKey: string = Main._getMasterKeyFromConnectionString(connectionString);
Main.configureTokenValidationDisplayPrompt(explorer);
Main._setExplorerReady(explorer, masterKey);
deferred.resolve();
@@ -398,14 +338,6 @@ export default class Main {
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);
}