airavata-local-agent/renderer/pages/login.jsx (234 lines of code) (raw):

/***************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * *****************************************************************/ import { Box, Center, Flex, Select, Img, Text, Button, Alert, AlertIcon, Link, Heading, Spinner } from "@chakra-ui/react"; import { useEffect, useState } from "react"; import { HeaderBox } from "../components/HeaderBox"; import { useAuth, useBackendUrls } from "../lib/Contexts"; import { useRouter } from "next/router"; import { TOKEN_FILE } from "../lib/constants"; const Login = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); const [verifyURL, setVerifyURL] = useState(""); const [isProd, setIsProd] = useState(false); const [gatewayOptions, setGatewayOptions] = useState([]); const [selectedGateway, setSelectedGateway] = useState(""); const [authInfo, setAuthInfo] = useAuth(); const router = useRouter(); const { apiUrl, authUrl, loginUrl, setGatewayId } = useBackendUrls(); // general a random string const randomString = (length) => { const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; let result = ''; for (let i = length; i > 0; --i) { result += chars[Math.floor(Math.random() * chars.length)]; } return result; }; const handleLogin = async () => { setError("Molecular Dynamics Gateway login is not yet implemented, please use the organizational login."); return; // TODO: implement this section if (username === "") { setError("Username cannot be blank."); } else if (password === "") { setError("Password cannot be blank."); } else { // handle login // setError("Invalid username or password."); window.location.href = "/tabs-view"; } }; const handleCiLogin = async () => { setLoading(true); window.auth.ciLogonLogin(); }; useEffect(() => { window.ipc.send('is-prod'); window.ipc.send('get-all-gateways'); window.ipc.send('get-gateway'); if (localStorage.getItem("ciLoginAuto") === "true") { setLoading(true); localStorage.removeItem("ciLoginAuto"); window.auth.ciLogonLogin(); } window.ipc.on('file-written', (data) => { console.log("File written", data); window.ipc.send('read-file', TOKEN_FILE); }); window.ipc.on('file-read', (data) => { console.log("File read", data); }); window.auth.ciLogonSuccess((event, data) => { const accessToken = data.access_token; const refreshToken = data.refresh_token; if (!accessToken || !refreshToken) { console.log("Error logging in with CI logon"); const numTriesAlready = localStorage.getItem("numTries"); if (numTriesAlready == null) { localStorage.setItem('numTries', 1); } else if (parseInt(numTriesAlready) < 4) { localStorage.setItem('numTries', parseInt(numTriesAlready) + 1); } else { localStorage.setItem('numTries', 0); localStorage.setItem('ciLoginAuto', "false"); setError("Refresh the page and try logging in again."); setLoading(false); return; } localStorage.setItem("ciLoginAuto", "true"); location.reload(); } else { window.localStorage.setItem("accessToken", data.access_token); window.localStorage.setItem("refreshToken", data.refresh_token); localStorage.removeItem("ciLoginAuto"); localStorage.setItem('numTries', 0); setLoading(false); window.ipc.send('write-file', TOKEN_FILE, JSON.stringify(data)); router.push('/docker-home'); } }); window.ipc.on('is-prod-reply', (isProdResult) => { setIsProd(isProdResult); }); window.ipc.on('got-gateways', (data) => { setGatewayOptions(data); }); window.ipc.on('gateway-got', (gateway) => { setSelectedGateway(gateway); }); // TODO: ask background process for what gateway we are currently configured on return () => { window.ipc.removeAllListeners('file-written'); window.ipc.removeAllListeners('file-read'); window.ipc.removeAllListeners('is-prod-reply'); window.ipc.removeAllListeners('got-gateways'); window.ipc.removeAllListeners('gateway-got'); }; }, []); return ( <> <HeaderBox /> <Center mt={16} maxW='500px' mx='auto'> <Box> <Flex alignItems='center' gap={2}> <Img src='/images/cs-logo.png' maxH='50px' /> <Text color='blue.600' fontWeight='bold' fontSize='3xl'>Cybershuttle Local Agent</Text> </Flex> { error !== "" && ( <Alert status='error' rounded='md' mt={2}> <AlertIcon /> <Text> <Text as='span' color='red.800' fontWeight='bold'>Login Failed</Text>. {error} </Text> </Alert> ) } { verifyURL !== "" && ( <Alert status='info' rounded='md' mt={2}> <AlertIcon /> <Text> <Text as='span' color='blue.800' fontWeight='bold'>Please verify your login</Text>. Open the following URL in your browser if it has not automatically opened: <Link color='blue.500' href={verifyURL} target="_blank">{verifyURL}</Link> </Text> </Alert> ) } <Box shadow='md' rounded='md' p={4} mt={4}> <Heading size='md' textAlign="left" color='blue.500'> Gateway </Heading> <Select mt={4} onChange={(e) => { const gateway = e.target.value; if (gateway) { window.ipc.send('set-gateway', gateway); setSelectedGateway(gateway); setGatewayId(gateway); } }} value={selectedGateway} > { gatewayOptions.map((item) => { return ( <option key={item.gateway} value={item.id}> {item.name} ({item.gateway}) </option> ); }) } </Select> </Box> <Box shadow='md' rounded='md' p={4} mt={4}> <Heading size='md' textAlign="left" color='blue.500'> Log in with your existing organizational login</Heading> { isProd ? ( <Button colorScheme='blue' w='full' mt={4} onClick={() => { setLoading(true); window.open(loginUrl, '_blank'); }} isDisabled={loading} > { loading && <Spinner mr={2} /> }Login with CILogon</Button> ) : ( <Button colorScheme='blue' w='full' mt={4} onClick={handleCiLogin} isDisabled={loading} > { loading && <Spinner mr={2} /> }Login with CILogon</Button> ) } </Box> {/* <Box shadow='md' rounded='md' p={4} mt={8}> <Heading size='md' textAlign="left" color='blue.500'>Log in with Molecular Dynamics Gateway</Heading> <Text mt={2}>If you need to create an account, <Link color='blue.500' href={SIGN_UP_URL} target="_blank">you can sign up here</Link>. You can close the pop-up window after you see "Account request processed successfully...".</Text> <VStack mt={4} w='500px' spacing={4}> <FormControl> <FormLabel>Username</FormLabel> <Input type='text' value={username} onChange={(e) => { setUsername(e.target.value); }} placeholder='Username' /> </FormControl> <FormControl> <FormLabel>Password</FormLabel> <Input type='password' value={password} onChange={(e) => { setPassword(e.target.value); }} placeholder='Password' /> </FormControl> <Button colorScheme='blue' onClick={handleLogin} w='full' isDisabled>Login with Molecular Dynamics Gateway</Button> </VStack> </Box> */} <Text mt={4} textAlign='center'><Link color='blue.500' href='/home'>Back to home</Link></Text> </Box> </Center > </> ); }; export default Login;