From 15cb4a8fc4b14db5d5e80d39475822e22d8eeb12 Mon Sep 17 00:00:00 2001 From: Steve Faulkner <471400+southpolesteve@users.noreply.github.com> Date: Fri, 1 Jan 2021 00:09:38 -0600 Subject: [PATCH] Checkpoint --- .../AccountSwitchComponent.test.tsx | 159 ---------- .../AccountSwitch/AccountSwitchComponent.tsx | 7 +- .../AccountSwitchComponentAdapter.tsx | 11 - .../AccountSwitchComponent.test.tsx.snap | 71 ----- src/Explorer/Panes/RenewAdHocAccessPane.ts | 6 +- src/HostedExplorer.ts | 94 +----- src/HostedExplorer.tsx | 277 +++++++++--------- src/Main.tsx | 5 - src/Platform/Hosted/Authorization.ts | 8 - src/Utils/UserUtils.test.ts | 24 -- src/Utils/UserUtils.ts | 15 +- src/contexts/authContext.tsx | 73 ----- src/hooks/useDatabaseAccounts.tsx | 6 +- src/hooks/useDirectories.tsx | 6 +- src/hooks/useGraphPhoto.tsx | 6 +- src/hooks/useSubscriptions.tsx | 6 +- 16 files changed, 155 insertions(+), 619 deletions(-) delete mode 100644 src/Explorer/Controls/AccountSwitch/AccountSwitchComponent.test.tsx delete mode 100644 src/Explorer/Controls/AccountSwitch/AccountSwitchComponentAdapter.tsx delete mode 100644 src/Explorer/Controls/AccountSwitch/__snapshots__/AccountSwitchComponent.test.tsx.snap delete mode 100644 src/Utils/UserUtils.test.ts delete mode 100644 src/contexts/authContext.tsx diff --git a/src/Explorer/Controls/AccountSwitch/AccountSwitchComponent.test.tsx b/src/Explorer/Controls/AccountSwitch/AccountSwitchComponent.test.tsx deleted file mode 100644 index cb512ad85..000000000 --- a/src/Explorer/Controls/AccountSwitch/AccountSwitchComponent.test.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import React from "react"; -import { shallow, mount } from "enzyme"; -import { AccountSwitchComponent, AccountSwitchComponentProps } from "./AccountSwitchComponent"; -import { AuthType } from "../../../AuthType"; -import { DatabaseAccount, Subscription } from "../../../Contracts/DataModels"; -import { AccountKind } from "../../../Common/Constants"; - -const createBlankProps = (): AccountSwitchComponentProps => { - return { - authType: null, - displayText: "", - accounts: [], - selectedAccountName: null, - isLoadingAccounts: false, - onAccountChange: jest.fn(), - subscriptions: [], - selectedSubscriptionId: null, - isLoadingSubscriptions: false, - onSubscriptionChange: jest.fn() - }; -}; - -const createBlankAccount = (): DatabaseAccount => { - return { - id: "", - kind: AccountKind.Default, - name: "", - properties: null, - location: "", - tags: null, - type: "" - }; -}; - -const createBlankSubscription = (): Subscription => { - return { - subscriptionId: "", - displayName: "", - authorizationSource: "", - state: "", - subscriptionPolicies: null, - tenantId: "", - uniqueDisplayName: "" - }; -}; - -const createFullProps = (): AccountSwitchComponentProps => { - const props = createBlankProps(); - props.authType = AuthType.AAD; - const account1 = createBlankAccount(); - account1.name = "account1"; - const account2 = createBlankAccount(); - account2.name = "account2"; - const account3 = createBlankAccount(); - account3.name = "superlongaccountnamestringtest"; - props.accounts = [account1, account2, account3]; - props.selectedAccountName = "account2"; - - const sub1 = createBlankSubscription(); - sub1.displayName = "sub1"; - sub1.subscriptionId = "a6062a74-5d53-4b20-9545-000b95f22297"; - const sub2 = createBlankSubscription(); - sub2.displayName = "subsubsubsubsubsubsub2"; - sub2.subscriptionId = "b20b3e93-0185-4326-8a9c-d44bac276b6b"; - props.subscriptions = [sub1, sub2]; - props.selectedSubscriptionId = "a6062a74-5d53-4b20-9545-000b95f22297"; - - return props; -}; - -describe("test render", () => { - it("renders no auth type -> handle error in code", () => { - const props = createBlankProps(); - - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - - // Encrypted Token - it("renders auth security token, with selected account name", () => { - const props = createBlankProps(); - props.authType = AuthType.EncryptedToken; - props.selectedAccountName = "testaccount"; - - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - - // AAD - it("renders auth aad, with all information", () => { - const props = createFullProps(); - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - - it("renders auth aad all dropdown menus", () => { - const props = createFullProps(); - const wrapper = mount(); - - expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(false); - wrapper.find("button.accountSwitchButton").simulate("click"); - expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(true); - - expect(wrapper.exists("div.accountSwitchSubscriptionDropdown")).toBe(true); - wrapper.find("DropdownBase.accountSwitchSubscriptionDropdown").simulate("click"); - // Click will dismiss the first contextual menu in enzyme. Need to dig deeper to achieve below test - - // expect(wrapper.exists("div.accountSwitchSubscriptionDropdownMenu")).toBe(true); - // expect(wrapper.find("button.ms-Dropdown-item").length).toBe(2); - // wrapper.find("div.accountSwitchSubscriptionDropdown").simulate("click"); - // expect(wrapper.exists("div.accountSwitchSubscriptionDropdownMenu")).toBe(false); - - // expect(wrapper.exists("div.accountSwitchAccountDropdown")).toBe(true); - // wrapper.find("div.accountSwitchAccountDropdown").simulate("click"); - // expect(wrapper.exists("div.accountSwitchAccountDropdownMenu")).toBe(true); - // expect(wrapper.find("button.ms-Dropdown-item").length).toBe(3); - // wrapper.find("div.accountSwitchAccountDropdown").simulate("click"); - // expect(wrapper.exists("div.accountSwitchAccountDropdownMenu")).toBe(false); - - // wrapper.find("button.accountSwitchButton").simulate("click"); - // expect(wrapper.exists("div.accountSwitchContextualMenu")).toBe(false); - - wrapper.unmount(); - }); -}); - -// describe("test function", () => { -// it("switch subscription function", () => { -// const props = createFullProps(); -// const wrapper = mount(); - -// wrapper.find("button.accountSwitchButton").simulate("click"); -// wrapper.find("div.accountSwitchSubscriptionDropdown").simulate("click"); -// wrapper -// .find("button.ms-Dropdown-item") -// .at(1) -// .simulate("click"); -// expect(props.onSubscriptionChange).toBeCalled(); -// expect(props.onSubscriptionChange).toHaveBeenCalled(); - -// wrapper.unmount(); -// }); - -// it("switch account", () => { -// const props = createFullProps(); -// const wrapper = mount(); - -// wrapper.find("button.accountSwitchButton").simulate("click"); -// wrapper.find("div.accountSwitchAccountDropdown").simulate("click"); -// wrapper -// .find("button.ms-Dropdown-item") -// .at(0) -// .simulate("click"); -// expect(props.onAccountChange).toBeCalled(); -// expect(props.onAccountChange).toHaveBeenCalled(); - -// wrapper.unmount(); -// }); -// }); diff --git a/src/Explorer/Controls/AccountSwitch/AccountSwitchComponent.tsx b/src/Explorer/Controls/AccountSwitch/AccountSwitchComponent.tsx index b2ff5be6e..c4ef3197f 100644 --- a/src/Explorer/Controls/AccountSwitch/AccountSwitchComponent.tsx +++ b/src/Explorer/Controls/AccountSwitch/AccountSwitchComponent.tsx @@ -1,5 +1,4 @@ import { StyleConstants } from "../../../Common/Constants"; - import * as React from "react"; import { DefaultButton, IButtonStyles, IButtonProps } from "office-ui-fabric-react/lib/Button"; import { IContextualMenuProps } from "office-ui-fabric-react/lib/ContextualMenu"; @@ -38,10 +37,10 @@ const buttonStyles: IButtonStyles = { } }; -export const AccountSwitchComponent: React.FunctionComponent = () => { - const subscriptions = useSubscriptions(); +export const AccountSwitchComponent: React.FunctionComponent<{ armToken: string }> = ({ armToken }) => { + const subscriptions = useSubscriptions(armToken); const [selectedSubscriptionId, setSelectedSubscriptionId] = React.useState(); - const accounts = useDatabaseAccounts(selectedSubscriptionId); + const accounts = useDatabaseAccounts(selectedSubscriptionId, armToken); const [selectedAccountName, setSelectedAccoutName] = React.useState(); const menuProps: IContextualMenuProps = { diff --git a/src/Explorer/Controls/AccountSwitch/AccountSwitchComponentAdapter.tsx b/src/Explorer/Controls/AccountSwitch/AccountSwitchComponentAdapter.tsx deleted file mode 100644 index 09acf6af7..000000000 --- a/src/Explorer/Controls/AccountSwitch/AccountSwitchComponentAdapter.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import * as React from "react"; -import { ReactAdapter } from "../../../Bindings/ReactBindingHandler"; -import { AccountSwitchComponent, AccountSwitchComponentProps } from "./AccountSwitchComponent"; - -export class AccountSwitchComponentAdapter implements ReactAdapter { - public parameters: ko.Observable; - - public renderComponent(): JSX.Element { - return ; - } -} diff --git a/src/Explorer/Controls/AccountSwitch/__snapshots__/AccountSwitchComponent.test.tsx.snap b/src/Explorer/Controls/AccountSwitch/__snapshots__/AccountSwitchComponent.test.tsx.snap deleted file mode 100644 index 3a40ab7a3..000000000 --- a/src/Explorer/Controls/AccountSwitch/__snapshots__/AccountSwitchComponent.test.tsx.snap +++ /dev/null @@ -1,71 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`test render renders auth aad, with all information 1`] = ` - -`; - -exports[`test render renders auth security token, with selected account name 1`] = ` - - testaccount - -`; - -exports[`test render renders no auth type -> handle error in code 1`] = ` - -`; diff --git a/src/Explorer/Panes/RenewAdHocAccessPane.ts b/src/Explorer/Panes/RenewAdHocAccessPane.ts index f9476c341..d4ef767df 100644 --- a/src/Explorer/Panes/RenewAdHocAccessPane.ts +++ b/src/Explorer/Panes/RenewAdHocAccessPane.ts @@ -2,7 +2,7 @@ import * as ko from "knockout"; import * as Constants from "../../Common/Constants"; import * as DataModels from "../../Contracts/DataModels"; import * as ViewModels from "../../Contracts/ViewModels"; -import { ConnectionStringParser } from "../../Platform/Hosted/Helpers/ConnectionStringParser"; +import { parseConnectionString } from "../../Platform/Hosted/Helpers/ConnectionStringParser"; import { ContextualPaneBase } from "./ContextualPaneBase"; import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent"; import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility"; @@ -48,9 +48,7 @@ export class RenewAdHocAccessPane extends ContextualPaneBase { }; private _shouldShowContextSwitchPrompt(): boolean { - const inputMetadata: DataModels.AccessInputMetadata = ConnectionStringParser.parseConnectionString( - this.accessKey() - ); + const inputMetadata: DataModels.AccessInputMetadata = parseConnectionString(this.accessKey()); const apiKind: DataModels.ApiKind = this.container && DefaultExperienceUtility.getApiKindFromDefaultExperience(this.container.defaultExperience()); const hasOpenedTabs: boolean = diff --git a/src/HostedExplorer.ts b/src/HostedExplorer.ts index 30e97cf74..0d314fe27 100644 --- a/src/HostedExplorer.ts +++ b/src/HostedExplorer.ts @@ -2,8 +2,6 @@ import "./Shared/appInsights"; import * as _ from "underscore"; import * as ko from "knockout"; import hasher from "hasher"; -import { AccountSwitchComponentProps } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent"; -import { AccountSwitchComponentAdapter } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponentAdapter"; import { Action } from "./Shared/Telemetry/TelemetryConstants"; import { ArmResourceUtils } from "./Platform/Hosted/ArmResourceUtils"; import AuthHeadersUtil from "./Platform/Hosted/Authorization"; @@ -50,13 +48,11 @@ class HostedExplorer { public firewallWarningComponentAdapter: DialogComponentAdapter; public dialogComponentAdapter: DialogComponentAdapter; public meControlComponentAdapter: MeControlComponentAdapter; - public accountSwitchComponentAdapter: AccountSwitchComponentAdapter; public switchDirectoryPane: SwitchDirectoryPane; private _firewallWarningDialogProps: ko.Observable; private _dialogProps: ko.Observable; private _meControlProps: ko.Observable; - private _accountSwitchProps: ko.Observable; private _controlbarCommands: ko.ObservableArray; private _directoryDropdownProps: ko.Observable; private _directoryListProps: ko.Observable; @@ -161,35 +157,10 @@ class HostedExplorer { this.meControlComponentAdapter = new MeControlComponentAdapter(); this.meControlComponentAdapter.parameters = this._meControlProps; - this._accountSwitchProps = ko.observable({ - authType: AuthType.EncryptedToken, - selectedAccountName: "", - accounts: [], - isLoadingAccounts: false, - onAccountChange: this._onAccountChange, - selectedSubscriptionId: undefined, - subscriptions: [], - isLoadingSubscriptions: false, - onSubscriptionChange: this._onSubscriptionChange - }); - this.accountSwitchComponentAdapter = new AccountSwitchComponentAdapter(); - this.accountSwitchComponentAdapter.parameters = this._accountSwitchProps; - - this.isAccountActive = ko.computed(() => { - if ( - this._accountSwitchProps() && - (this._accountSwitchProps().displayText || this._accountSwitchProps().selectedAccountName) - ) { - return true; - } - return false; - }); - hasher.initialized.add(updateExplorerHash); hasher.changed.add(updateExplorerHash); hasher.init(); window.addEventListener("message", this._handleMessage.bind(this), false); - this._handleAadLogin(); } public explorer_click() { @@ -348,43 +319,10 @@ class HostedExplorer { this._meControlProps(propsToUpdate); } - private _updateAccountSwitchProps(props: Partial) { + private _updateAccountSwitchProps(props: any) { if (!props) { return; } - - const propsToUpdate = this._accountSwitchProps(); - if (props.authType) { - if (props.selectedAccountName != null) { - propsToUpdate.selectedAccountName = props.selectedAccountName; - } - if (props.authType === AuthType.EncryptedToken) { - propsToUpdate.authType = AuthType.EncryptedToken; - } else if (props.authType === AuthType.AAD) { - propsToUpdate.authType = AuthType.AAD; - if (props.displayText != null) { - propsToUpdate.displayText = props.displayText; - } - if (props.isLoadingAccounts != null) { - propsToUpdate.isLoadingAccounts = props.isLoadingAccounts; - } - if (props.accounts) { - propsToUpdate.accounts = props.accounts.sort((a, b) => (a.name < b.name ? -1 : 1)); - } - - if (props.isLoadingSubscriptions != null) { - propsToUpdate.isLoadingSubscriptions = props.isLoadingSubscriptions; - } - if (props.subscriptions) { - propsToUpdate.subscriptions = props.subscriptions.sort((a, b) => (a.displayName < b.displayName ? -1 : 1)); - } - if (props.selectedSubscriptionId != null) { - propsToUpdate.selectedSubscriptionId = props.selectedSubscriptionId; - } - } - } - - this._accountSwitchProps(propsToUpdate); } private _onAccountChange = (newAccount: DatabaseAccount) => { @@ -522,36 +460,6 @@ class HostedExplorer { } } - private _handleAadLogin() { - AuthHeadersUtil.processTokenResponse(); - if (AuthHeadersUtil.isUserSignedIn()) { - window.authType = AuthType.AAD; - const user = AuthHeadersUtil.getCachedUser(); - this._updateMeControlProps({ - isUserSignedIn: true, - user: { - name: user.profile.name, - email: user.userName, - tenantName: undefined, - imageUrl: undefined - } - }); - - AuthHeadersUtil.getPhotoFromGraphAPI().then(blob => { - const imageUrl = URL.createObjectURL(blob); - this._updateMeControlProps({ - isUserSignedIn: true, - user: { - name: undefined, - email: undefined, - tenantName: undefined, - imageUrl: imageUrl - } - }); - }); - } - } - private _handleGetAccessAadRequest() { this._getAccessAad().then( response => { diff --git a/src/HostedExplorer.tsx b/src/HostedExplorer.tsx index 77cef5a1c..774093f31 100644 --- a/src/HostedExplorer.tsx +++ b/src/HostedExplorer.tsx @@ -2,23 +2,19 @@ import "./Platform/Hosted/ConnectScreen.less"; import { useBoolean } from "@uifabric/react-hooks"; import { DefaultButton, - DetailsList, DirectionalHint, FocusZone, - IContextualMenuProps, initializeIcons, Panel, PanelType, Persona, PersonaInitialsColor, PersonaSize, - SelectionMode, - Selection + ChoiceGroup } from "office-ui-fabric-react"; import * as React from "react"; import { render } from "react-dom"; import FeedbackIcon from "../images/Feedback.svg"; -import ConnectIcon from "../images/HostedConnectwhite.svg"; import ChevronRight from "../images/chevron-right.svg"; import "../less/hostedexplorer.less"; import { CommandButtonComponent } from "./Explorer/Controls/CommandButton/CommandButtonComponent"; @@ -26,111 +22,74 @@ import "./Explorer/Menus/NavBar/MeControlComponent.less"; import { useGraphPhoto } from "./hooks/useGraphPhoto"; import "./Shared/appInsights"; import { AccountSwitchComponent } from "./Explorer/Controls/AccountSwitch/AccountSwitchComponent"; -import { AuthContext, AuthProvider } from "./contexts/authContext"; import { usePortalAccessToken } from "./hooks/usePortalAccessToken"; import { useDirectories } from "./hooks/useDirectories"; +import * as Msal from "msal"; +import { configContext } from "./ConfigContext"; +import { HttpHeaders } from "./Common/Constants"; +import { GenerateTokenResponse } from "./Contracts/DataModels"; import { AuthType } from "./AuthType"; initializeIcons(); +const msal = new Msal.UserAgentApplication({ + auth: { + authority: "https://login.microsoft.com/common", + clientId: "203f1145-856a-4232-83d4-a43568fba23d", + redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development + } +}); + const App: React.FunctionComponent = () => { + // Hooks for handling encrypted portal tokens const params = new URLSearchParams(window.location.search); - const encryptedToken = params && params.get("key"); + const [encryptedToken, setEncryptedToken] = React.useState(params && params.get("key")); const encryptedTokenMetadata = usePortalAccessToken(encryptedToken); + + // Hooks for showing/hiding UI const [isOpen, { setTrue: openPanel, setFalse: dismissPanel }] = useBoolean(false); - const { isLoggedIn, aadlogin: login, account, aadlogout: logout, tenantId } = React.useContext(AuthContext); const [isConnectionStringVisible, { setTrue: showConnectionString }] = useBoolean(false); - const photo = useGraphPhoto(); - const directories = useDirectories(); - // const [selectedItem, setSelectedItem] = React.useState(undefined); - const selection = new Selection({ - getKey: item => item.tenantId, - items: directories, - onSelectionChanged: () => { - const selected = selection.getSelection()[0]; - if (selected.tenantId !== tenantId) { - console.log("new Tenant", selected.tenantId); - } - }, - selectionMode: SelectionMode.single - }); - selection.setKeySelected(tenantId, true, false); - // private _renderPersonaComponent = (): JSX.Element => { - // const { user } = this.props; - // const personaProps: IPersonaSharedProps = { - // imageUrl: user.imageUrl, - // text: user.name, - // secondaryText: user.email, - // showSecondaryText: true, - // showInitialsUntilImageLoads: true, - // initialsColor: PersonaInitialsColor.teal, - // size: PersonaSize.size72, - // className: "mecontrolContextualMenuPersona" - // }; + // Hooks for AAD authentication + const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(false); + const [account, setAccount] = React.useState(); + const [graphToken, setGraphToken] = React.useState(); + const [armToken, setArmToken] = React.useState(); + const [tenantId, setTenantId] = React.useState(); + const [connectionString, setConnectionString] = React.useState(""); - // return ; - // }; + const login = React.useCallback(async () => { + const response = await msal.loginPopup(); + setLoggedIn(); + setAccount(response.account); + setTenantId(response.tenantId); + }, []); - const menuProps: IContextualMenuProps = { - className: "mecontrolContextualMenu", - isBeakVisible: false, - directionalHintFixed: true, - directionalHint: DirectionalHint.bottomRightEdge, - calloutProps: { - minPagePadding: 0 - }, - items: [ - { - key: "SwitchDirectory", - text: "Switch Directory", - onClick: openPanel - }, - { - key: "SignOut", - text: "Sign Out", - onClick: logout - } - ] - }; + const logout = React.useCallback(() => { + msal.logout(); + setLoggedOut(); + }, []); - // { - // id: "commandbutton-settings", - // iconSrc: SettingsIcon, - // iconAlt: "setting button", - // onCommandClick: () => {}, - // commandButtonLabel: undefined, - // ariaLabel: "setting button", - // tooltipText: "Global settings", - // hasPopup: true, - // disabled: false - // }, - // { - // id: "commandbutton-feedback", - // iconSrc: FeedbackIcon, - // iconAlt: "feeback button", - // onCommandClick: () => - // window.open( - // "https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20Hosted%20Data%20Explorer%20Feedback" - // ), - // commandButtonLabel: undefined, - // ariaLabel: "feeback button", - // tooltipText: "Send feedback", - // hasPopup: true, - // disabled: false - // } - - const buttonProps = { - id: "mecontrolHeader", - className: "mecontrolHeaderButton", - menuProps, - styles: { - rootHovered: { backgroundColor: "#393939" }, - rootFocused: { backgroundColor: "#393939" }, - rootPressed: { backgroundColor: "#393939" }, - rootExpanded: { backgroundColor: "#393939" } + React.useEffect(() => { + if (account && tenantId) { + console.log(msal.authority); + console.log("Getting tokens for", tenantId); + Promise.all([ + msal.acquireTokenSilent({ + scopes: ["https://graph.windows.net//.default"] + }), + msal.acquireTokenSilent({ + scopes: ["https://management.azure.com//.default"] + }) + ]).then(([graphTokenResponse, armTokenResponse]) => { + setGraphToken(graphTokenResponse.accessToken); + setArmToken(armTokenResponse.accessToken); + }); } - }; + }, [account, tenantId]); + + const photo = useGraphPhoto(graphToken); + const directories = useDirectories(armToken); return ( @@ -151,7 +110,7 @@ const App: React.FunctionComponent = () => { )} {isLoggedIn && ( - + )} {!isLoggedIn && encryptedTokenMetadata?.accountName && ( @@ -161,18 +120,6 @@ const App: React.FunctionComponent = () => { )} - {isLoggedIn && ( - {}} - ariaLabel="connect button" - tooltipText="Connect to a Cosmos DB account" - hasPopup={true} - disabled={false} - /> - )} { {isLoggedIn ? ( - + { )}&metadata=${JSON.stringify(encryptedTokenMetadata)}`} > )} - {!encryptedTokenMetadata && isLoggedIn && ( + {/* {!encryptedTokenMetadata && isLoggedIn && ( { title="explorer" src={`explorer.html?v=1.0.1&platform=Hosted&authType=${AuthType.AAD}`} > - )} + )} */} {!isLoggedIn && !encryptedTokenMetadata && ( @@ -246,15 +223,42 @@ const App: React.FunctionComponent = () => { Welcome to Azure Cosmos DB {isConnectionStringVisible ? ( - + { + event.preventDefault(); + // const foo = parseConnectionString(connectionString); + const headers = new Headers(); + headers.append(HttpHeaders.connectionString, connectionString); + const url = configContext.BACKEND_ENDPOINT + "/api/guest/tokens/generateToken"; + const response = await fetch(url, { headers, method: "POST" }); + if (!response.ok) { + throw response; + } + // This API has a quirk where it must be parsed twice + const result: GenerateTokenResponse = JSON.parse(await response.json()); + console.log(result.readWrite || result.read); + setEncryptedToken(decodeURIComponent(result.readWrite || result.read)); + event.preventDefault(); + }} + > Connect to your account with connection string - + { + setConnectionString(event.target.value); + }} + /> - + @@ -285,35 +289,30 @@ const App: React.FunctionComponent = () => { onDismiss={dismissPanel} closeButtonAriaLabel="Close" > - ({ key: dir.tenantId, text: `${dir.displayName} (${dir.tenantId})` }))} + selectedKey={tenantId} + onChange={async () => { + dismissPanel(); + // TODO!!! This does not work. Still not sure why. Tried lots of stuff. + // const response = await msal.loginPopup({ + // authority: `https://login.microsoftonline.com/${option.key}` + // }); + // // msal = new Msal.UserAgentApplication({ + // // auth: { + // // authority: `https://login.microsoftonline.com/${option.key}`, + // // clientId: "203f1145-856a-4232-83d4-a43568fba23d", + // // redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development + // // } + // // }); + // setTenantId(option.key); + // setAccount(response.account); + // console.log(account); + }} /> ); }; -render( - - - , - document.body -); +render(, document.body); diff --git a/src/Main.tsx b/src/Main.tsx index 9c20b4671..7bc28ec60 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -55,11 +55,6 @@ import "url-polyfill/url-polyfill.min"; initializeIcons(); -import * as ko from "knockout"; -import * as TelemetryProcessor from "./Shared/Telemetry/TelemetryProcessor"; -import { Action, ActionModifiers } from "./Shared/Telemetry/TelemetryConstants"; - -import { BindingHandlersRegisterer } from "./Bindings/BindingHandlersRegisterer"; import * as Emulator from "./Platform/Emulator/Main"; import Hosted from "./Platform/Hosted/Main"; import * as Portal from "./Platform/Portal/Main"; diff --git a/src/Platform/Hosted/Authorization.ts b/src/Platform/Hosted/Authorization.ts index 28104c723..8941a03e9 100644 --- a/src/Platform/Hosted/Authorization.ts +++ b/src/Platform/Hosted/Authorization.ts @@ -107,14 +107,6 @@ export default class AuthHeadersUtil { const user = AuthHeadersUtil._authContext.getCachedUser(); return !!user; } - - public static getCachedUser(): AuthenticationContext.UserInfo { - if (this.isUserSignedIn()) { - return AuthHeadersUtil._authContext.getCachedUser(); - } - return undefined; - } - public static signIn() { if (!AuthHeadersUtil.isUserSignedIn()) { AuthHeadersUtil._authContext.login(); diff --git a/src/Utils/UserUtils.test.ts b/src/Utils/UserUtils.test.ts deleted file mode 100644 index 8a87d8bcf..000000000 --- a/src/Utils/UserUtils.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import AuthHeadersUtil from "../Platform/Hosted/Authorization"; -import * as UserUtils from "./UserUtils"; - -describe("UserUtils", () => { - it("getFullName works in regular data explorer (inside portal)", () => { - const user: AuthenticationContext.UserInfo = { - userName: "userName", - profile: { - name: "name" - } - }; - AuthHeadersUtil.getCachedUser = jest.fn().mockReturnValue(user); - - expect(UserUtils.getFullName()).toBe("name"); - }); - - it("getFullName works in fullscreen data explorer (outside portal)", () => { - jest.mock("./AuthorizationUtils", () => { - (): { name: string } => ({ name: "name" }); - }); - - expect(UserUtils.getFullName()).toBe("name"); - }); -}); diff --git a/src/Utils/UserUtils.ts b/src/Utils/UserUtils.ts index 33015e83c..5275b2702 100644 --- a/src/Utils/UserUtils.ts +++ b/src/Utils/UserUtils.ts @@ -1,17 +1,8 @@ -import AuthHeadersUtil from "../Platform/Hosted/Authorization"; import { decryptJWTToken } from "./AuthorizationUtils"; import { userContext } from "../UserContext"; export function getFullName(): string { - let fullName: string; - const user = AuthHeadersUtil.getCachedUser(); - if (user) { - fullName = user.profile.name; - } else { - const authToken = userContext.authorizationToken; - const props = decryptJWTToken(authToken); - fullName = props.name; - } - - return fullName; + const authToken = userContext.authorizationToken; + const props = decryptJWTToken(authToken); + return props.name; } diff --git a/src/contexts/authContext.tsx b/src/contexts/authContext.tsx deleted file mode 100644 index 922d12230..000000000 --- a/src/contexts/authContext.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import * as React from "react"; -import * as Msal from "msal"; -import { createContext, useState, useCallback } from "react"; -import { useBoolean } from "@uifabric/react-hooks"; - -const defaultError = "Auth context method was called witout a AuthProvider component in the component tree"; - -const msal = new Msal.UserAgentApplication({ - auth: { - authority: "https://login.microsoft.com/common", - clientId: "203f1145-856a-4232-83d4-a43568fba23d", - redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set development - } -}); - -interface AuthContext { - isLoggedIn: boolean; - account?: Msal.Account; - graphToken?: string; - armToken?: string; - tenantId?: string; - aadlogout: () => unknown; - aadlogin: () => unknown; -} - -export const AuthContext = createContext({ - isLoggedIn: false, - aadlogin: () => { - throw Error(defaultError); - }, - aadlogout: () => { - throw Error(defaultError); - } -}); - -export const AuthProvider: React.FunctionComponent = ({ children }) => { - const [isLoggedIn, { setTrue: setLoggedIn, setFalse: setLoggedOut }] = useBoolean(false); - const [account, setAccount] = useState(); - const [graphToken, setGraphToken] = useState(); - const [armToken, setArmToken] = useState(); - const [tenantId, setTenantId] = useState(); - - const aadlogin = useCallback(async () => { - const response = await msal.loginPopup(); - setLoggedIn(); - setAccount(response.account); - setTenantId(response.tenantId); - msal.authority = "https://login.microsoftonline.com/481f23b0-3fb3-4e76-812d-15513d11dbfc"; - - const [graphTokenResponse, armTokenResponse] = await Promise.all([ - msal.acquireTokenSilent({ - scopes: ["https://graph.windows.net//.default"] - }), - msal.acquireTokenSilent({ - scopes: ["https://management.azure.com//.default"] - }) - ]); - - setGraphToken(graphTokenResponse.accessToken); - setArmToken(armTokenResponse.accessToken); - }, []); - - const aadlogout = useCallback(() => { - msal.logout(); - setLoggedOut(); - }, []); - - return ( - - {children} - - ); -}; diff --git a/src/hooks/useDatabaseAccounts.tsx b/src/hooks/useDatabaseAccounts.tsx index 837f9331f..86f618116 100644 --- a/src/hooks/useDatabaseAccounts.tsx +++ b/src/hooks/useDatabaseAccounts.tsx @@ -1,5 +1,4 @@ -import { useContext, useEffect, useState } from "react"; -import { AuthContext } from "../contexts/authContext"; +import { useEffect, useState } from "react"; import { DatabaseAccount } from "../Contracts/DataModels"; interface AccountListResult { @@ -39,8 +38,7 @@ export async function fetchDatabaseAccounts( return accounts; } -export function useDatabaseAccounts(subscriptionId: string): DatabaseAccount[] { - const { armToken } = useContext(AuthContext); +export function useDatabaseAccounts(subscriptionId: string, armToken: string): DatabaseAccount[] { const [state, setState] = useState(); useEffect(() => { diff --git a/src/hooks/useDirectories.tsx b/src/hooks/useDirectories.tsx index 76be8a91e..70b36e18e 100644 --- a/src/hooks/useDirectories.tsx +++ b/src/hooks/useDirectories.tsx @@ -1,5 +1,4 @@ -import { useContext, useEffect, useState } from "react"; -import { AuthContext } from "../contexts/authContext"; +import { useEffect, useState } from "react"; import { Tenant } from "../Contracts/DataModels"; interface TenantListResult { @@ -29,8 +28,7 @@ export async function fetchDirectories(accessToken: string): Promise { return tenents; } -export function useDirectories(): Tenant[] { - const { armToken } = useContext(AuthContext); +export function useDirectories(armToken: string): Tenant[] { const [state, setState] = useState(); useEffect(() => { diff --git a/src/hooks/useGraphPhoto.tsx b/src/hooks/useGraphPhoto.tsx index efebc11de..703245dbe 100644 --- a/src/hooks/useGraphPhoto.tsx +++ b/src/hooks/useGraphPhoto.tsx @@ -1,5 +1,4 @@ -import { useContext, useEffect, useState } from "react"; -import { AuthContext } from "../contexts/authContext"; +import { useEffect, useState } from "react"; export async function fetchPhoto(accessToken: string): Promise { const headers = new Headers(); @@ -18,9 +17,8 @@ export async function fetchPhoto(accessToken: string): Promise { .catch(error => console.log(error)); } -export function useGraphPhoto(): string { +export function useGraphPhoto(graphToken: string): string { const [photo, setPhoto] = useState(); - const { graphToken } = useContext(AuthContext); useEffect(() => { if (graphToken) { diff --git a/src/hooks/useSubscriptions.tsx b/src/hooks/useSubscriptions.tsx index c6e8a5242..a5f459f2b 100644 --- a/src/hooks/useSubscriptions.tsx +++ b/src/hooks/useSubscriptions.tsx @@ -1,5 +1,4 @@ -import { useContext, useEffect, useState } from "react"; -import { AuthContext } from "../contexts/authContext"; +import { useEffect, useState } from "react"; import { Subscription } from "../Contracts/DataModels"; interface SubscriptionListResult { @@ -32,8 +31,7 @@ export async function fetchSubscriptions(accessToken: string): Promise(); useEffect(() => {
Welcome to Azure Cosmos DB
Connect to your account with connection string
- + { + setConnectionString(event.target.value); + }} + /> - +
@@ -285,35 +289,30 @@ const App: React.FunctionComponent = () => { onDismiss={dismissPanel} closeButtonAriaLabel="Close" > - ({ key: dir.tenantId, text: `${dir.displayName} (${dir.tenantId})` }))} + selectedKey={tenantId} + onChange={async () => { + dismissPanel(); + // TODO!!! This does not work. Still not sure why. Tried lots of stuff. + // const response = await msal.loginPopup({ + // authority: `https://login.microsoftonline.com/${option.key}` + // }); + // // msal = new Msal.UserAgentApplication({ + // // auth: { + // // authority: `https://login.microsoftonline.com/${option.key}`, + // // clientId: "203f1145-856a-4232-83d4-a43568fba23d", + // // redirectUri: "https://dataexplorer-dev.azurewebsites.net" // TODO! This should only be set in development + // // } + // // }); + // setTenantId(option.key); + // setAccount(response.account); + // console.log(account); + }} />