14720: Better login button, tell you what cluster you're logging in to.
authorPeter Amstutz <pamstutz@veritasgenetics.com>
Mon, 11 Feb 2019 22:49:44 +0000 (17:49 -0500)
committerPeter Amstutz <pamstutz@veritasgenetics.com>
Tue, 12 Feb 2019 21:40:25 +0000 (16:40 -0500)
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz@veritasgenetics.com>

src/index.tsx
src/store/auth/auth-action.ts
src/store/auth/auth-reducer.ts
src/views/login-panel/login-panel.tsx
src/views/main-panel/main-panel.tsx

index f5eedc73c7e3c8ff1032864670537b564c15a7dc..d0eb7d3b5c472ab66bd7e6287d4b3b5fbe174b38 100644 (file)
@@ -60,7 +60,6 @@ import { groupActionSet } from '~/views-components/context-menu/action-sets/grou
 import { groupMemberActionSet } from '~/views-components/context-menu/action-sets/group-member-action-set';
 import { linkActionSet } from '~/views-components/context-menu/action-sets/link-action-set';
 import { loadFileViewersConfig } from '~/store/file-viewers/file-viewers-actions';
-import { setRemoteHosts } from '~/views/login-panel/login-panel';
 
 console.log(`Starting arvados [${getBuildInfo()}]`);
 
@@ -106,8 +105,6 @@ fetchConfig()
         store.dispatch(initAuth(config));
         store.dispatch(setBuildInfo());
         store.dispatch(setCurrentTokenDialogApiHost(apiHost));
-        store.dispatch(setUuidPrefix(config.uuidPrefix));
-        store.dispatch(setRemoteHosts(config.remoteHosts));
         store.dispatch(loadVocabulary);
         store.dispatch(loadFileViewersConfig);
 
index 43d1be8233d70fe3540b7d791a7d457beab076b5..cc1a5a0a4b4da99eda49d727fa0284211c7d9e54 100644 (file)
@@ -17,12 +17,14 @@ export const authActions = unionize({
     SAVE_API_TOKEN: ofType<string>(),
     LOGIN: {},
     LOGOUT: {},
+    CONFIG: ofType<{ uuidPrefix: string, remoteHosts: { [key: string]: string } }>(),
     INIT: ofType<{ user: User, token: string }>(),
     USER_DETAILS_REQUEST: {},
     USER_DETAILS_SUCCESS: ofType<User>(),
     SET_SSH_KEYS: ofType<SshKeyResource[]>(),
     ADD_SSH_KEY: ofType<SshKeyResource>(),
     REMOVE_SSH_KEY: ofType<string>(),
+    SET_HOME_CLUSTER: ofType<string>(),
     SET_SESSIONS: ofType<Session[]>(),
     ADD_SESSION: ofType<Session>(),
     REMOVE_SESSION: ofType<string>(),
@@ -48,6 +50,7 @@ export const initAuth = (config: Config) => (dispatch: Dispatch, getState: () =>
     if (token) {
         setAuthorizationHeader(services, token);
     }
+    dispatch(authActions.CONFIG({ uuidPrefix: config.uuidPrefix, remoteHosts: config.remoteHosts }));
     if (token && user) {
         dispatch(authActions.INIT({ user, token }));
         dispatch<any>(initSessions(services.authService, config, user));
index a2822f100c37b6421b465f3b7a2312d2c5b00362..0504dd480086dde66b56ca606070686f6583948a 100644 (file)
@@ -13,40 +13,54 @@ export interface AuthState {
     apiToken?: string;
     sshKeys: SshKeyResource[];
     sessions: Session[];
+    localCluster: string;
+    homeCluster: string;
+    remoteHosts: { [key: string]: string };
 }
 
 const initialState: AuthState = {
     user: undefined,
     apiToken: undefined,
     sshKeys: [],
-    sessions: []
+    sessions: [],
+    localCluster: "",
+    homeCluster: "",
+    remoteHosts: {}
 };
 
 export const authReducer = (services: ServiceRepository) => (state = initialState, action: AuthAction) => {
     return authActions.match(action, {
         SAVE_API_TOKEN: (token: string) => {
-            return {...state, apiToken: token};
+            return { ...state, apiToken: token };
+        },
+        CONFIG: ({ uuidPrefix, remoteHosts }) => {
+            return {
+                ...state, localCluster: uuidPrefix, remoteHosts, homeCluster: uuidPrefix
+            };
         },
         INIT: ({ user, token }) => {
-            return { ...state, user, apiToken: token };
+            return { ...state, user, apiToken: token, homeCluster: user.uuid.substr(0, 5) };
         },
         LOGIN: () => {
             return state;
         },
         LOGOUT: () => {
-            return {...state, apiToken: undefined};
+            return { ...state, apiToken: undefined };
         },
         USER_DETAILS_SUCCESS: (user: User) => {
-            return {...state, user};
+            return { ...state, user };
         },
         SET_SSH_KEYS: (sshKeys: SshKeyResource[]) => {
-            return {...state, sshKeys};
+            return { ...state, sshKeys };
         },
         ADD_SSH_KEY: (sshKey: SshKeyResource) => {
             return { ...state, sshKeys: state.sshKeys.concat(sshKey) };
         },
         REMOVE_SSH_KEY: (uuid: string) => {
-            return { ...state, sshKeys: state.sshKeys.filter((sshKey) => sshKey.uuid !== uuid )};
+            return { ...state, sshKeys: state.sshKeys.filter((sshKey) => sshKey.uuid !== uuid) };
+        },
+        SET_HOME_CLUSTER: (homeCluster: string) => {
+            return { ...state, homeCluster };
         },
         SET_SESSIONS: (sessions: Session[]) => {
             return { ...state, sessions };
@@ -59,14 +73,16 @@ export const authReducer = (services: ServiceRepository) => (state = initialStat
                 ...state,
                 sessions: state.sessions.filter(
                     session => session.clusterId !== clusterId
-                )};
+                )
+            };
         },
         UPDATE_SESSION: (session: Session) => {
             return {
                 ...state,
                 sessions: state.sessions.map(
                     s => s.clusterId === session.clusterId ? session : s
-                )};
+                )
+            };
         },
         default: () => state
     });
index 48c6e163ff3f110dcdb093e316eb38642446e1cc..12c7d6c79de9ccdc0ccad399250ce6c752ed4e59 100644 (file)
@@ -4,13 +4,11 @@
 
 import * as React from 'react';
 import { connect, DispatchProp } from 'react-redux';
-import { Grid, Typography, Button } from '@material-ui/core';
+import { Grid, Typography, Button, Select, FormControl } from '@material-ui/core';
 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
-import { login } from '~/store/auth/auth-action';
+import { login, authActions } from '~/store/auth/auth-action';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { RootState } from '~/store/store';
-import { getProperty } from '~/store/properties/properties';
-import { propertiesActions } from '~/store/properties/properties-actions';
 import * as classNames from 'classnames';
 
 type CssRules = 'root' | 'container' | 'title' | 'content' | 'content__bolder' | 'button';
@@ -52,24 +50,16 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 });
 
 type LoginPanelProps = DispatchProp<any> & WithStyles<CssRules> & {
-    remoteHosts: any,
+    remoteHosts: { [key: string]: string },
     homeCluster: string,
     uuidPrefix: string
 };
 
-export const REMOTE_HOSTS_NAME = 'remoteHosts';
-export const HOME_CLUSTER_NAME = 'homeCluster';
-export const setRemoteHosts = (remoteHosts: any) =>
-    propertiesActions.SET_PROPERTY({ key: REMOTE_HOSTS_NAME, value: remoteHosts });
-
-export const setHomeCluster = (homeCluster: string) =>
-    propertiesActions.SET_PROPERTY({ key: HOME_CLUSTER_NAME, value: homeCluster });
-
 export const LoginPanel = withStyles(styles)(
     connect((state: RootState) => ({
-        remoteHosts: state.properties.remoteHosts,
-        homeCluster: state.properties.homeCluster,
-        uuidPrefix: state.properties.uuidPrefix
+        remoteHosts: state.auth.remoteHosts,
+        homeCluster: state.auth.homeCluster,
+        uuidPrefix: state.auth.localCluster
     }))(({ classes, dispatch, remoteHosts, homeCluster, uuidPrefix }: LoginPanelProps) =>
         <Grid container direction="column" item xs alignItems="center" justify="center" className={classes.root}>
             <Grid item className={classes.container}>
@@ -90,20 +80,22 @@ export const LoginPanel = withStyles(styles)(
                     Arvados Workbench uses your name and email address only for identification, and does not retrieve any other personal information from Google.
                </Typography>
 
-                <Typography className={classes.content}>
-                    <form>
-                        <label>
-                            Choose your home cluster:
-                           <select value={homeCluster} onChange={(event) => dispatch(setHomeCluster(event.target.value))}>
-                                {Object.keys(remoteHosts).map((k) => <option key={k} value={k}>{k}</option>)}
-                            </select>
-                        </label>
-                    </form>
-                </Typography>
+                {Object.keys(remoteHosts).length > 1 &&
+                    <Typography component="div" align="right">
+                        <label>Please select the cluster that hosts your user account:</label>
+                        <Select native value={homeCluster} style={{ margin: "1em" }}
+                            onChange={(event) => dispatch(authActions.SET_HOME_CLUSTER(event.target.value))}>
+                            {Object.keys(remoteHosts).map((k) => <option key={k} value={k}>{k}</option>)}
+                        </Select>
+                    </Typography>}
+
                 <Typography component="div" align="right">
-                    <Button variant="contained" color="primary" className={classes.button} onClick={() => dispatch(login(uuidPrefix, remoteHosts[homeCluster]))}>
-                        Log in
-                   </Button>
+                    <Button variant="contained" color="primary" style={{ margin: "1em" }} className={classes.button}
+                        onClick={() => dispatch(login(uuidPrefix, remoteHosts[homeCluster]))}>
+                        Log in to {uuidPrefix}
+                        {uuidPrefix !== homeCluster &&
+                            <span>&nbsp;with user from {homeCluster}</span>}
+                    </Button>
                 </Typography>
             </Grid>
         </Grid>
index 087163dcd25870014557a975953a87af69de96c6..5009f12987ab59d215a0f697124319ef7bfa5efa 100644 (file)
@@ -14,7 +14,7 @@ const mapStateToProps = (state: RootState): MainPanelRootDataProps => {
         working: isSystemWorking(state.progressIndicator),
         loading: isWorkbenchLoading(state),
         buildInfo: state.appInfo.buildInfo,
-        uuidPrefix: state.properties.uuidPrefix
+        uuidPrefix: state.auth.localCluster
     };
 };