Merge branch 'master' into 15088-merge-account
authorEric Biagiotti <ebiagiotti@veritasgenetics.com>
Wed, 15 May 2019 15:04:57 +0000 (11:04 -0400)
committerEric Biagiotti <ebiagiotti@veritasgenetics.com>
Wed, 15 May 2019 15:04:57 +0000 (11:04 -0400)
1  2 
src/routes/route-change-handlers.ts
src/routes/routes.ts
src/store/auth/auth-action.ts
src/store/auth/auth-reducer.ts
src/store/navigation/navigation-action.ts
src/store/store.ts
src/store/workbench/workbench-actions.ts
src/views-components/api-token/api-token.tsx
src/views/workbench/workbench.tsx

index e0a51550f745eeb2fd9ab125165a3ad8885b6aad,50ba319ea18315e4e13770cf9f3d36852b5db58f..b43e84bb72147cc240a81a12cffbd223d17b32f0
@@@ -23,7 -23,7 +23,7 @@@ const handleLocationChange = (store: Ro
      const projectMatch = Routes.matchProjectRoute(pathname);
      const collectionMatch = Routes.matchCollectionRoute(pathname);
      const favoriteMatch = Routes.matchFavoritesRoute(pathname);
-     const publicFavoritesMatch = Routes.matchPublicFavorites(pathname);
+     const publicFavoritesMatch = Routes.matchPublicFavoritesRoute(pathname);
      const trashMatch = Routes.matchTrashRoute(pathname);
      const processMatch = Routes.matchProcessRoute(pathname);
      const processLogMatch = Routes.matchProcessLogRoute(pathname);
      const computeNodesMatch = Routes.matchComputeNodesRoute(pathname);
      const apiClientAuthorizationsMatch = Routes.matchApiClientAuthorizationsRoute(pathname);
      const myAccountMatch = Routes.matchMyAccountRoute(pathname);
 +    const linkAccountMatch = Routes.matchLinkAccountRoute(pathname);
      const userMatch = Routes.matchUsersRoute(pathname);
      const groupsMatch = Routes.matchGroupsRoute(pathname);
      const groupDetailsMatch = Routes.matchGroupDetailsRoute(pathname);
      const linksMatch = Routes.matchLinksRoute(pathname);
+     const collectionsContentAddressMatch = Routes.matchCollectionsContentAddressRoute(pathname);
  
      store.dispatch(dialogActions.CLOSE_ALL_DIALOGS());
      store.dispatch(contextMenuActions.CLOSE_CONTEXT_MENU());
@@@ -95,8 -95,6 +96,8 @@@
          store.dispatch(WorkbenchActions.loadApiClientAuthorizations);
      } else if (myAccountMatch) {
          store.dispatch(WorkbenchActions.loadMyAccount);
 +    } else if (linkAccountMatch) {
 +        store.dispatch(WorkbenchActions.loadLinkAccount);
      } else if (userMatch) {
          store.dispatch(WorkbenchActions.loadUsers);
      } else if (groupsMatch) {
          store.dispatch(WorkbenchActions.loadGroupDetailsPanel(groupDetailsMatch.params.id));
      } else if (linksMatch) {
          store.dispatch(WorkbenchActions.loadLinks);
+     } else if (collectionsContentAddressMatch) {
+         store.dispatch(WorkbenchActions.loadCollectionContentAddress);
      }
  };
diff --combined src/routes/routes.ts
index ba7e2a45aff16946ad61b8a2316eba6e84dd632e,831c69e58cc62fcd15b09f135b2de66896cf0089..76f5c32dc192f9205674f5e52e6c88a1979cdd9a
@@@ -10,6 -10,7 +10,7 @@@ import { getCollectionUrl } from '~/mod
  export const Routes = {
      ROOT: '/',
      TOKEN: '/token',
+     FED_LOGIN: '/fedtoken',
      PROJECTS: `/projects/:id(${RESOURCE_UUID_PATTERN})`,
      COLLECTIONS: `/collections/:id(${RESOURCE_UUID_PATTERN})`,
      PROCESSES: `/processes/:id(${RESOURCE_UUID_PATTERN})`,
@@@ -27,7 -28,6 +28,7 @@@
      SSH_KEYS_USER: `/ssh-keys-user`,
      SITE_MANAGER: `/site-manager`,
      MY_ACCOUNT: '/my-account',
 +    LINK_ACCOUNT: '/link_account',
      KEEP_SERVICES: `/keep-services`,
      COMPUTE_NODES: `/nodes`,
      USERS: '/users',
@@@ -35,7 -35,8 +36,8 @@@
      GROUPS: '/groups',
      GROUP_DETAILS: `/group/:id(${RESOURCE_UUID_PATTERN})`,
      LINKS: '/links',
-     PUBLIC_FAVORITES: '/public-favorites'
+     PUBLIC_FAVORITES: '/public-favorites',
+     COLLECTIONS_CONTENT_ADDRESS: '/collections/:id',
  };
  
  export const getResourceUrl = (uuid: string) => {
@@@ -116,15 -117,9 +118,15 @@@ export const matchSiteManagerRoute = (r
  export const matchMyAccountRoute = (route: string) =>
      matchPath(route, { path: Routes.MY_ACCOUNT });
  
 +export const matchLinkAccountRoute = (route: string) =>
 +    matchPath(route, { path: Routes.LINK_ACCOUNT });
 +
  export const matchKeepServicesRoute = (route: string) =>
      matchPath(route, { path: Routes.KEEP_SERVICES });
  
 +export const matchTokenRoute = (route: string) =>
 +    matchPath(route, { path: Routes.TOKEN });
 +
  export const matchUsersRoute = (route: string) =>
      matchPath(route, { path: Routes.USERS });
  
@@@ -143,5 -138,8 +145,8 @@@ export const matchGroupDetailsRoute = (
  export const matchLinksRoute = (route: string) =>
      matchPath(route, { path: Routes.LINKS });
  
- export const matchPublicFavorites = (route: string) =>
+ export const matchPublicFavoritesRoute = (route: string) =>
      matchPath(route, { path: Routes.PUBLIC_FAVORITES });
+ export const matchCollectionsContentAddressRoute = (route: string) =>
+     matchPath(route, { path: Routes.COLLECTIONS_CONTENT_ADDRESS });
index 7ee8399286c381287fb60b236f15ed87ea0df90b,c088418a61eaa4b3b4a4c8fdc7b7110fa6ebfa58..6ca7140339f86542632e01c7616b6cec0e92073a
@@@ -8,16 -8,15 +8,18 @@@ import { AxiosInstance } from "axios"
  import { RootState } from "../store";
  import { ServiceRepository } from "~/services/services";
  import { SshKeyResource } from '~/models/ssh-key';
 -import { User } from "~/models/user";
 +import { User, UserResource } from "~/models/user";
  import { Session } from "~/models/session";
- import { Config } from '~/common/config';
+ import { getDiscoveryURL, Config } from '~/common/config';
  import { initSessions } from "~/store/auth/auth-action-session";
 +import { cancelLinking } from '~/store/link-account-panel/link-account-panel-actions';
 +import { matchTokenRoute } from '~/routes/routes';
+ import Axios from "axios";
+ import { AxiosError } from "axios";
  
  export const authActions = unionize({
      SAVE_API_TOKEN: ofType<string>(),
 +    SAVE_USER: ofType<UserResource>(),
      LOGIN: {},
      LOGOUT: {},
      CONFIG: ofType<{ config: Config }>(),
@@@ -31,7 -30,8 +33,8 @@@
      SET_SESSIONS: ofType<Session[]>(),
      ADD_SESSION: ofType<Session>(),
      REMOVE_SESSION: ofType<string>(),
-     UPDATE_SESSION: ofType<Session>()
+     UPDATE_SESSION: ofType<Session>(),
+     REMOTE_CLUSTER_CONFIG: ofType<{ config: Config }>(),
  });
  
  function setAuthorizationHeader(services: ServiceRepository, token: string) {
@@@ -48,26 -48,32 +51,39 @@@ function removeAuthorizationHeader(clie
  }
  
  export const initAuth = (config: Config) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
 +    // Cancel any link account ops in progess unless the user has
 +    // just logged in or there has been a successful link operation
 +    const data = services.linkAccountService.getLinkOpStatus();
 +    if (!matchTokenRoute(location.pathname) && data === undefined) {
 +        dispatch<any>(cancelLinking());
 +    }
 +
      const user = services.authService.getUser();
      const token = services.authService.getApiToken();
+     const homeCluster = services.authService.getHomeCluster();
      if (token) {
          setAuthorizationHeader(services, token);
      }
      dispatch(authActions.CONFIG({ config }));
+     dispatch(authActions.SET_HOME_CLUSTER(homeCluster || config.uuidPrefix));
      if (token && user) {
          dispatch(authActions.INIT({ user, token }));
          dispatch<any>(initSessions(services.authService, config, user));
          dispatch<any>(getUserDetails()).then((user: User) => {
              dispatch(authActions.INIT({ user, token }));
+         }).catch((err: AxiosError) => {
+             if (err.response) {
+                 // Bad token
+                 if (err.response.status === 401) {
+                     logout()(dispatch, getState, services);
+                 }
+             }
          });
      }
+     Object.keys(config.remoteHosts).map((k) => {
+         Axios.get<Config>(getDiscoveryURL(config.remoteHosts[k]))
+             .then(response => dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config: response.data })));
+     });
  };
  
  export const saveApiToken = (token: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
      dispatch(authActions.SAVE_API_TOKEN(token));
  };
  
- export const login = (uuidPrefix: string, homeCluster: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-     services.authService.login(uuidPrefix, homeCluster);
 +export const saveUser = (user: UserResource) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
 +    services.authService.saveUser(user);
 +    dispatch(authActions.SAVE_USER(user));
 +};
 +
+ export const login = (uuidPrefix: string, homeCluster: string, remoteHosts: { [key: string]: string }) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+     services.authService.login(uuidPrefix, homeCluster, remoteHosts);
      dispatch(authActions.LOGIN());
  };
  
 -export const logout = () => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
 +export const logout = (deleteLinkData: boolean = false) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
 +    if (deleteLinkData) {
 +        services.linkAccountService.removeAccountToLink();
 +    }
      services.authService.removeApiToken();
      services.authService.removeUser();
      removeAuthorizationHeader(services.apiClient);
index c87fc5ddb15320eb89357022870c7a649b87ab0f,e44c81e323297fdfcd8884a4efe08236d911a9f2..cded9f0e71816636ce1876d51c83d3976abb589c
@@@ -3,10 -3,11 +3,11 @@@
  // SPDX-License-Identifier: AGPL-3.0
  
  import { authActions, AuthAction } from "./auth-action";
 -import { User } from "~/models/user";
 +import { User, UserResource } from "~/models/user";
  import { ServiceRepository } from "~/services/services";
  import { SshKeyResource } from '~/models/ssh-key';
  import { Session } from "~/models/session";
+ import { Config } from '~/common/config';
  
  export interface AuthState {
      user?: User;
@@@ -16,6 -17,7 +17,7 @@@
      localCluster: string;
      homeCluster: string;
      remoteHosts: { [key: string]: string };
+     remoteHostsConfig: { [key: string]: Config };
  }
  
  const initialState: AuthState = {
@@@ -25,7 -27,8 +27,8 @@@
      sessions: [],
      localCluster: "",
      homeCluster: "",
-     remoteHosts: {}
+     remoteHosts: {},
+     remoteHostsConfig: {}
  };
  
  export const authReducer = (services: ServiceRepository) => (state = initialState, action: AuthAction) => {
@@@ -33,9 -36,6 +36,9 @@@
          SAVE_API_TOKEN: (token: string) => {
              return { ...state, apiToken: token };
          },
 +        SAVE_USER: (user: UserResource) => {
 +            return { ...state, user};
 +        },
          CONFIG: ({ config }) => {
              return {
                  ...state,
                  homeCluster: config.uuidPrefix
              };
          },
+         REMOTE_CLUSTER_CONFIG: ({ config }) => {
+             return {
+                 ...state,
+                 remoteHostsConfig: { ...state.remoteHostsConfig, [config.uuidPrefix]: config },
+             };
+         },
          INIT: ({ user, token }) => {
              return { ...state, user, apiToken: token, homeCluster: user.uuid.substr(0, 5) };
          },
index 3bec160992a92ae4342e3789321e57cf7ab2471c,d7ad017878894ff0156333b9d3c290c0637276b3..6d393f03ce8538a44670bfe3f65ca7f73e9d71d9
@@@ -85,8 -85,6 +85,8 @@@ export const navigateToSiteManager= pus
  
  export const navigateToMyAccount = push(Routes.MY_ACCOUNT);
  
 +export const navigateToLinkAccount = push(Routes.LINK_ACCOUNT);
 +
  export const navigateToKeepServices = push(Routes.KEEP_SERVICES);
  
  export const navigateToComputeNodes = push(Routes.COMPUTE_NODES);
@@@ -100,3 -98,5 +100,5 @@@ export const navigateToGroups = push(Ro
  export const navigateToGroupDetails = compose(push, getGroupUrl);
  
  export const navigateToLinks = push(Routes.LINKS);
+ export const navigateToCollectionsContentAddress = push(Routes.COLLECTIONS_CONTENT_ADDRESS);
diff --combined src/store/store.ts
index 6a37572bc553254eb9c6e1446f6f2d204c658223,ff9a495e804b9e30c2f6693008d42e5a002943e5..8a2ca2400cb1cbd3f1229cce96294291e91c8d66
@@@ -62,7 -62,9 +62,10 @@@ import { ApiClientAuthorizationMiddlewa
  import { PublicFavoritesMiddlewareService } from '~/store/public-favorites-panel/public-favorites-middleware-service';
  import { PUBLIC_FAVORITE_PANEL_ID } from '~/store/public-favorites-panel/public-favorites-action';
  import { publicFavoritesReducer } from '~/store/public-favorites/public-favorites-reducer';
 +import { linkAccountPanelReducer } from './link-account-panel/link-account-panel-reducer';
+ import { CollectionsWithSameContentAddressMiddlewareService } from '~/store/collections-content-address-panel/collections-content-address-middleware-service';
+ import { COLLECTIONS_CONTENT_ADDRESS_PANEL_ID } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
+ import { ownerNameReducer } from '~/store/owner-name/owner-name-reducer';
  
  const composeEnhancers =
      (process.env.NODE_ENV === 'development' &&
@@@ -116,6 -118,10 +119,10 @@@ export function configureStore(history
      const publicFavoritesMiddleware = dataExplorerMiddleware(
          new PublicFavoritesMiddlewareService(services, PUBLIC_FAVORITE_PANEL_ID)
      );
+     const collectionsContentAddress = dataExplorerMiddleware(
+         new CollectionsWithSameContentAddressMiddlewareService(services, COLLECTIONS_CONTENT_ADDRESS_PANEL_ID)
+     );
      const middlewares: Middleware[] = [
          routerMiddleware(history),
          thunkMiddleware.withExtraArgument(services),
          linkPanelMiddleware,
          computeNodeMiddleware,
          apiClientAuthorizationMiddlewareService,
-         publicFavoritesMiddleware
+         publicFavoritesMiddleware,
+         collectionsContentAddress
      ];
      const enhancer = composeEnhancers(applyMiddleware(...middlewares));
      return createStore(rootReducer, enhancer);
@@@ -147,6 -154,7 +155,7 @@@ const createRootReducer = (services: Se
      detailsPanel: detailsPanelReducer,
      dialog: dialogReducer,
      favorites: favoritesReducer,
+     ownerName: ownerNameReducer,
      publicFavorites: publicFavoritesReducer,
      form: formReducer,
      processLogsPanel: processLogsPanelReducer,
      searchBar: searchBarReducer,
      virtualMachines: virtualMachinesReducer,
      repositories: repositoriesReducer,
 -    keepServices: keepServicesReducer
 +    keepServices: keepServicesReducer,
 +    linkAccountPanel: linkAccountPanelReducer
  });
index 0cffcaceaeaaef03af2c5b1e942146bd81ba832b,2363b5795a8b9c2c0d0743524fb8e82f460f0caf..27ac76f335198e75bc37bcbc30df330d39226cbd
@@@ -59,7 -59,6 +59,7 @@@ import { CopyFormDialogData } from '~/s
  import { loadWorkflowPanel, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions';
  import { loadSshKeysPanel } from '~/store/auth/auth-action-ssh';
  import { loadMyAccountPanel } from '~/store/my-account/my-account-panel-actions';
 +import { loadLinkAccountPanel, linkAccountPanelActions } from '~/store/link-account-panel/link-account-panel-actions';
  import { loadSiteManagerPanel } from '~/store/auth/auth-action-session';
  import { workflowPanelColumns } from '~/views/workflow-panel/workflow-panel-view';
  import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
@@@ -69,8 -68,7 +69,7 @@@ import { FilterBuilder } from '~/servic
  import { GroupContentsResource } from '~/services/groups-service/groups-service';
  import { MatchCases, ofType, unionize, UnionOf } from '~/common/unionize';
  import { loadRunProcessPanel } from '~/store/run-process-panel/run-process-panel-actions';
- import { loadCollectionFiles } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
- import { collectionPanelActions } from "~/store/collection-panel/collection-panel-action";
+ import { collectionPanelActions, loadCollectionPanel } from "~/store/collection-panel/collection-panel-action";
  import { CollectionResource } from "~/models/collection";
  import {
      loadSearchResultsPanel,
@@@ -95,7 -93,8 +94,9 @@@ import { groupDetailsPanelColumns } fro
  import { DataTableFetchMode } from "~/components/data-table/data-table";
  import { loadPublicFavoritePanel, publicFavoritePanelActions } from '~/store/public-favorites-panel/public-favorites-action';
  import { publicFavoritePanelColumns } from '~/views/public-favorites-panel/public-favorites-panel';
 +import { USER_LINK_ACCOUNT_KEY } from '~/services/link-account-service/link-account-service';
+ import { loadCollectionsContentAddressPanel, collectionsContentAddressActions } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
+ import { collectionContentAddressPanelColumns } from '~/views/collection-content-address-panel/collection-content-address-panel';
  
  export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
  
@@@ -135,11 -134,8 +136,12 @@@ export const loadWorkbench = () =
              dispatch(linkPanelActions.SET_COLUMNS({ columns: linkPanelColumns }));
              dispatch(computeNodesActions.SET_COLUMNS({ columns: computeNodePanelColumns }));
              dispatch(apiClientAuthorizationsActions.SET_COLUMNS({ columns: apiClientAuthorizationPanelColumns }));
+             dispatch(collectionsContentAddressActions.SET_COLUMNS({ columns: collectionContentAddressPanelColumns }));
  
 +            if (sessionStorage.getItem(USER_LINK_ACCOUNT_KEY)) {
 +                dispatch(linkAccountPanelActions.HAS_SESSION_DATA());
 +            }
 +
              dispatch<any>(initSidePanelTree());
              if (router.location) {
                  const match = matchRootRoute(router.location.pathname);
@@@ -160,6 -156,11 +162,11 @@@ export const loadFavorites = () =
              dispatch<any>(setSidePanelBreadcrumbs(SidePanelTreeCategory.FAVORITES));
          });
  
+ export const loadCollectionContentAddress = handleFirstTimeLoad(
+     async (dispatch: Dispatch<any>) => {
+         await dispatch(loadCollectionsContentAddressPanel());
+     });
  export const loadTrash = () =>
      handleFirstTimeLoad(
          (dispatch: Dispatch) => {
@@@ -263,21 -264,21 +270,21 @@@ export const loadCollection = (uuid: st
                          dispatch(updateResources([collection]));
                          await dispatch(activateSidePanelTreeItem(collection.ownerUuid));
                          dispatch(setSidePanelBreadcrumbs(collection.ownerUuid));
-                         dispatch(loadCollectionFiles(collection.uuid));
+                         dispatch(loadCollectionPanel(collection.uuid));
                      },
                      SHARED: collection => {
                          dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                          dispatch(updateResources([collection]));
                          dispatch<any>(setSharedWithMeBreadcrumbs(collection.ownerUuid));
                          dispatch(activateSidePanelTreeItem(collection.ownerUuid));
-                         dispatch(loadCollectionFiles(collection.uuid));
+                         dispatch(loadCollectionPanel(collection.uuid));
                      },
                      TRASHED: collection => {
                          dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                          dispatch(updateResources([collection]));
                          dispatch(setTrashBreadcrumbs(''));
                          dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.TRASH));
-                         dispatch(loadCollectionFiles(collection.uuid));
+                         dispatch(loadCollectionPanel(collection.uuid));
                      },
  
                  });
@@@ -491,11 -492,6 +498,11 @@@ export const loadMyAccount = handleFirs
          dispatch(loadMyAccountPanel());
      });
  
 +export const loadLinkAccount = handleFirstTimeLoad(
 +    (dispatch: Dispatch<any>) => {
 +        dispatch(loadLinkAccountPanel());
 +    });
 +
  export const loadKeepServices = handleFirstTimeLoad(
      async (dispatch: Dispatch<any>) => {
          await dispatch(loadKeepServicesPanel());
index b0fd03134c613fff4a3be001fd88cd8c02d5e73c,b78e7192dc0bd24f5c1ef14ed1ab6c0629aea564..2f8d87e1ede0d1b95fe688738583605136254904
@@@ -8,15 -8,15 +8,16 @@@ import { connect, DispatchProp } from "
  import { authActions, getUserDetails, saveApiToken } from "~/store/auth/auth-action";
  import { getUrlParameter } from "~/common/url";
  import { AuthService } from "~/services/auth-service/auth-service";
 -import { navigateToRootProject } from "~/store/navigation/navigation-action";
 +import { navigateToRootProject, navigateToLinkAccount } from "~/store/navigation/navigation-action";
  import { User } from "~/models/user";
  import { Config } from "~/common/config";
  import { initSessions } from "~/store/auth/auth-action-session";
 +import { getAccountLinkData } from "~/store/link-account-panel/link-account-panel-actions";
  
  interface ApiTokenProps {
      authService: AuthService;
      config: Config;
+     loadMainApp: boolean;
  }
  
  export const ApiToken = connect()(
          componentDidMount() {
              const search = this.props.location ? this.props.location.search : "";
              const apiToken = getUrlParameter(search, 'api_token');
+             const loadMainApp = this.props.loadMainApp;
              this.props.dispatch(saveApiToken(apiToken));
              this.props.dispatch<any>(getUserDetails()).then((user: User) => {
                  this.props.dispatch(initSessions(this.props.authService, this.props.config, user));
              }).finally(() => {
+                 if (loadMainApp) {
 +                if (this.props.dispatch(getAccountLinkData())) {
 +                    this.props.dispatch(navigateToLinkAccount);
 +                }
 +                else {
                      this.props.dispatch(navigateToRootProject);
                  }
++                }
              });
          }
          render() {
-             return <div/>;
+             return <div />;
          }
      }
  );
index e852150c2721001476ee0eb07399a0505bd02770,20cbbdea0c9a0b262f976889d5e097243b777c6f..d015d4ec363255c5982ce3a8bb12af2393d1b2ec
@@@ -3,7 -3,6 +3,7 @@@
  // SPDX-License-Identifier: AGPL-3.0
  
  import * as React from 'react';
 +import { connect } from 'react-redux';
  import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
  import { Route, Switch } from "react-router";
  import { ProjectPanel } from "~/views/project-panel/project-panel";
@@@ -93,7 -92,8 +93,9 @@@ import { GroupMemberAttributesDialog } 
  import { AddGroupMembersDialog } from '~/views-components/dialog-forms/add-group-member-dialog';
  import { PartialCopyToCollectionDialog } from '~/views-components/dialog-forms/partial-copy-to-collection-dialog';
  import { PublicFavoritePanel } from '~/views/public-favorites-panel/public-favorites-panel';
 +import { LinkAccountPanel } from '~/views/link-account-panel/link-account-panel';
+ import { FedLogin } from './fed-login';
+ import { CollectionsContentAddressPanel } from '~/views/collection-content-address-panel/collection-content-address-panel';
  
  type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
  
@@@ -125,12 -125,7 +127,12 @@@ const styles: StyleRulesCallback<CssRul
      }
  });
  
 -type WorkbenchPanelProps = WithStyles<CssRules>;
 +interface WorkbenchDataProps {
 +    isUserActive: boolean;
 +    isNotLinking: boolean;
 +}
 +
 +type WorkbenchPanelProps = WithStyles<CssRules> & WorkbenchDataProps;
  
  const defaultSplitterSize = 90;
  
@@@ -142,21 -137,21 +144,21 @@@ const getSplitterInitialSize = () => 
  const saveSplitterSize = (size: number) => localStorage.setItem('splitterSize', size.toString());
  
  export const WorkbenchPanel =
 -    withStyles(styles)(({ classes }: WorkbenchPanelProps) =>
 -        <Grid container item xs className={classes.root}>
 -            <Grid container item xs className={classes.container}>
 -                <SplitterLayout customClassName={classes.splitter} percentage={true}
 +    withStyles(styles)((props: WorkbenchPanelProps) =>
 +        <Grid container item xs className={props.classes.root}>
 +            <Grid container item xs className={props.classes.container}>
 +                <SplitterLayout customClassName={props.classes.splitter} percentage={true}
                      primaryIndex={0} primaryMinSize={10}
                      secondaryInitialSize={getSplitterInitialSize()} secondaryMinSize={40}
                      onSecondaryPaneSizeChange={saveSplitterSize}>
 -                    <Grid container item xs component='aside' direction='column' className={classes.asidePanel}>
 +                    { props.isUserActive && props.isNotLinking && <Grid container item xs component='aside' direction='column' className={props.classes.asidePanel}>
                          <SidePanel />
 -                    </Grid>
 -                    <Grid container item xs component="main" direction="column" className={classes.contentWrapper}>
 +                    </Grid> }
 +                    <Grid container item xs component="main" direction="column" className={props.classes.contentWrapper}>
                          <Grid item xs>
 -                            <MainContentBar />
 +                            { props.isNotLinking && <MainContentBar /> }
                          </Grid>
 -                        <Grid item xs className={classes.content}>
 +                        <Grid item xs className={props.classes.content}>
                              <Switch>
                                  <Route path={Routes.PROJECTS} component={ProjectPanel} />
                                  <Route path={Routes.COLLECTIONS} component={CollectionPanel} />
                                  <Route path={Routes.GROUP_DETAILS} component={GroupDetailsPanel} />
                                  <Route path={Routes.LINKS} component={LinkPanel} />
                                  <Route path={Routes.PUBLIC_FAVORITES} component={PublicFavoritePanel} />
 +                                <Route path={Routes.LINK_ACCOUNT} component={LinkAccountPanel} />
+                                 <Route path={Routes.COLLECTIONS_CONTENT_ADDRESS} component={CollectionsContentAddressPanel} />
                              </Switch>
                          </Grid>
                      </Grid>
              <UserAttributesDialog />
              <UserManageDialog />
              <VirtualMachineAttributesDialog />
+             <FedLogin />
          </Grid>
      );