16212: Loads remote clusters configs at app boot.
authorLucas Di Pentima <lucas@di-pentima.com.ar>
Tue, 31 Mar 2020 18:35:29 +0000 (15:35 -0300)
committerLucas Di Pentima <lucas@di-pentima.com.ar>
Wed, 29 Apr 2020 20:24:36 +0000 (17:24 -0300)
When using a federation without LoginCluster, the user is given the option
to log in using any cluster as the 'home cluster'. If any of those remote
clusters has Login.PAM enabled, the login form is displayed.

Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas@di-pentima.com.ar>

src/index.tsx
src/store/auth/auth-action-session.ts
src/store/auth/auth-action.ts
src/views-components/login-form/login-form.tsx
src/views/login-panel/login-panel.tsx

index bf810fb7583695bce32279269a11a3ce388615fc..d428b1c361143928feddfcd3529dd497d4643fa7 100644 (file)
@@ -166,5 +166,3 @@ const initListener = (history: History, store: RootStore, services: ServiceRepos
         }
     };
 };
-
-// force build comment #1
index a63878286a94dc407d1060c98a8e8c7c5e5cceda..52a1e23abf780dfbd2fab211eec79dacfa2a6e6c 100644 (file)
@@ -200,6 +200,19 @@ export const validateSessions = () =>
         }
     };
 
+export const addRemoteConfig = (remoteHost: string) =>
+    async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+        const config = await getRemoteHostConfig(remoteHost);
+        if (!config) {
+            dispatch(snackbarActions.OPEN_SNACKBAR({
+                message: `Could not get config for ${remoteHost}`,
+                kind: SnackbarKind.ERROR
+            }));
+            return;
+        }
+        dispatch(authActions.REMOTE_CLUSTER_CONFIG({ config }));
+    };
+
 export const addSession = (remoteHost: string, token?: string, sendToLogin?: boolean) =>
     async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
         const sessions = getState().auth.sessions;
index 923d3c9ea693427c344fda6f5344758af153c83b..1060ec708dcaab8cd9a0e39235bc769e4c1ed841 100644 (file)
@@ -15,6 +15,7 @@ import { createServices, setAuthorizationHeader } from "~/services/services";
 import { cancelLinking } from '~/store/link-account-panel/link-account-panel-actions';
 import { progressIndicatorActions } from "~/store/progress-indicator/progress-indicator-actions";
 import { WORKBENCH_LOADING_SCREEN } from '~/store/workbench/workbench-actions';
+import { addRemoteConfig } from './auth-action-session';
 
 export const authActions = unionize({
     LOGIN: {},
@@ -38,23 +39,30 @@ export const initAuth = (config: Config) => (dispatch: Dispatch, getState: () =>
     // Cancel any link account ops in progress unless the user has
     // just logged in or there has been a successful link operation
     const data = services.linkAccountService.getLinkOpStatus();
-    if (!matchTokenRoute(location.pathname) && (!matchFedTokenRoute(location.pathname)) && data === undefined) {
+    if (!matchTokenRoute(location.pathname) &&
+        (!matchFedTokenRoute(location.pathname)) && data === undefined) {
         dispatch<any>(cancelLinking()).then(() => {
             dispatch<any>(init(config));
         });
-    }
-    else {
+    } else {
         dispatch<any>(init(config));
     }
 };
 
 const init = (config: Config) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+    const remoteHosts = () => getState().auth.remoteHosts;
     const token = services.authService.getApiToken();
     let homeCluster = services.authService.getHomeCluster();
     if (homeCluster && !config.remoteHosts[homeCluster]) {
         homeCluster = undefined;
     }
     dispatch(authActions.SET_CONFIG({ config }));
+    Object.keys(remoteHosts()).forEach((remoteUuid: string) => {
+        const remoteHost = remoteHosts()[remoteUuid];
+        if (remoteUuid !== config.uuidPrefix) {
+            dispatch<any>(addRemoteConfig(remoteHost));
+        }
+    });
     dispatch(authActions.SET_HOME_CLUSTER(config.loginCluster || homeCluster || config.uuidPrefix));
 
     if (token && token !== "undefined") {
index 2d4451c6a30925836e37658aaf2c9143e579010e..8ec856f9dcbdfe4afba3d304e4dfa5665db9b35b 100644 (file)
@@ -46,10 +46,11 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
 
 type LoginFormProps = DispatchProp<any> & WithStyles<CssRules> & {
     handleSubmit: (username: string, password: string) => AxiosPromise;
+    loginLabel?: string,
 };
 
 export const LoginForm = withStyles(styles)(
-    ({ handleSubmit, dispatch, classes }: LoginFormProps) => {
+    ({ handleSubmit, loginLabel, dispatch, classes }: LoginFormProps) => {
         const userInput = useRef<HTMLInputElement>(null);
         const [username, setUsername] = useState('');
         const [password, setPassword] = useState('');
@@ -137,7 +138,7 @@ export const LoginForm = withStyles(styles)(
                         <Button variant="contained" size="large" color="primary"
                             className={classes.loginBtn} onClick={() => handleLogin()}
                             disabled={isSubmitting || isButtonDisabled}>
-                            Log in
+                            {loginLabel || 'Log in'}
                         </Button>
                     </CardActions>
                     { isSubmitting && <CircularProgress color='secondary' className={classes.progress} />}
index 203a2f192fbe9e86d00b18df7cf90b1264b1f961..57e7a0dc0c65137c188d92e752cba8e6441575e4 100644 (file)
@@ -61,7 +61,7 @@ const doPAMLogin = (url: string) => (username: string, password: string) => {
 type LoginPanelProps = DispatchProp<any> & WithStyles<CssRules> & {
     remoteHosts: { [key: string]: string },
     homeCluster: string,
-    uuidPrefix: string,
+    localCluster: string,
     loginCluster: string,
     welcomePage: string,
     pamLogin: boolean,
@@ -71,12 +71,15 @@ export const LoginPanel = withStyles(styles)(
     connect((state: RootState) => ({
         remoteHosts: state.auth.remoteHosts,
         homeCluster: state.auth.homeCluster,
-        uuidPrefix: state.auth.localCluster,
+        localCluster: state.auth.localCluster,
         loginCluster: state.auth.loginCluster,
         welcomePage: state.auth.config.clusterConfig.Workbench.WelcomePageHTML,
-        pamLogin: state.auth.config.clusterConfig.Login.PAM,
-    }))(({ classes, dispatch, remoteHosts, homeCluster, uuidPrefix, loginCluster, welcomePage, pamLogin }: LoginPanelProps) =>
-        <Grid container justify="center" alignItems="center"
+        pamLogin: state.auth.remoteHostsConfig[state.auth.homeCluster] &&
+            state.auth.remoteHostsConfig[state.auth.homeCluster].clusterConfig.Login.PAM || false,
+    }))(({ classes, dispatch, remoteHosts, homeCluster, localCluster, loginCluster, welcomePage, pamLogin }: LoginPanelProps) => {
+        const loginBtnLabel = `Log in${(localCluster !== homeCluster && loginCluster !== homeCluster) ? " to "+localCluster+" with user from "+homeCluster : ''}`;
+
+        return (<Grid container justify="center" alignItems="center"
             className={classes.root}
             style={{ marginTop: 56, overflowY: "auto", height: "100%" }}>
             <Grid item className={classes.container}>
@@ -95,17 +98,17 @@ export const LoginPanel = withStyles(styles)(
 
                 {pamLogin
                 ? <Typography component="div">
-                    <LoginForm dispatch={dispatch} handleSubmit={
-                        doPAMLogin(`https://${remoteHosts[homeCluster]}`)}/>
+                    <LoginForm dispatch={dispatch}
+                        loginLabel={loginBtnLabel}
+                        handleSubmit={doPAMLogin(`https://${remoteHosts[homeCluster]}`)}/>
                 </Typography>
                 : <Typography component="div" align="right">
                     <Button variant="contained" color="primary" style={{ margin: "1em" }}
                         className={classes.button}
-                        onClick={() => dispatch(login(uuidPrefix, homeCluster, loginCluster, remoteHosts))}>
-                        Log in {uuidPrefix !== homeCluster && loginCluster !== homeCluster &&
-                            <span>&nbsp;to {uuidPrefix} with user from {homeCluster}</span>}
+                        onClick={() => dispatch(login(localCluster, homeCluster, loginCluster, remoteHosts))}>
+                        {loginBtnLabel}
                     </Button>
                 </Typography>}
             </Grid>
-        </Grid >
+        </Grid >);}
     ));