From 540c2164a050f7a805565c9f7d0e157a125e0707 Mon Sep 17 00:00:00 2001 From: Rishichandra Wawhal Date: Wed, 7 Aug 2019 17:56:21 +0530 Subject: [PATCH 01/28] change login flow to handle admin secret persistence --- console/src/components/Common/Common.scss | 4 + console/src/components/Login/Actions.js | 150 ++++++++++++++++ console/src/components/Login/Login.js | 170 ++++++++++-------- console/src/components/Main/Actions.js | 81 --------- .../Services/ApiExplorer/Actions.js | 37 ++-- .../ApiExplorer/ApiRequest/ApiRequest.js | 38 ++-- .../components/Services/Data/DataActions.js | 8 +- 7 files changed, 291 insertions(+), 197 deletions(-) create mode 100644 console/src/components/Login/Actions.js diff --git a/console/src/components/Common/Common.scss b/console/src/components/Common/Common.scss index d8760d274ea88..e33d020258da6 100644 --- a/console/src/components/Common/Common.scss +++ b/console/src/components/Common/Common.scss @@ -412,6 +412,10 @@ input { margin: 0 !important; } +.remove_margin_top { + margin-top: 0 !important; +} + .display_inline { display: inline-block; } diff --git a/console/src/components/Login/Actions.js b/console/src/components/Login/Actions.js new file mode 100644 index 0000000000000..37c91ae226ccb --- /dev/null +++ b/console/src/components/Login/Actions.js @@ -0,0 +1,150 @@ +import Endpoints, { globalCookiePolicy } from '../../Endpoints'; +import { push } from 'react-router-redux'; +import globals from '../../Globals'; +import { updateDataHeaders } from '../Services/Data/DataActions'; +import { saveAdminSecretState, loadAdminSecretState } from '../AppState'; +import { + getGraphiQLHeadersFromLocalStorage, + setGraphiQLHeadersInLocalStorage, +} from '../Services/ApiExplorer/ApiRequest/utils'; +import { setHeadersBulk } from '../Services/ApiExplorer/Actions'; + +const adminSecretKeyString = `x-hasura-${globals.adminSecretLabel}`; + +export const verifyLogin = ({ + adminSecret, + shouldPersist, + successCallback, + errorCallback, + dispatch, +}) => { + console.log(adminSecret); + const url = Endpoints.getSchema; + const requestOptions = { + credentials: globalCookiePolicy, + method: 'POST', + headers: { + [adminSecretKeyString]: adminSecret, + 'content-type': 'application/json', + }, + body: JSON.stringify({ + type: 'select', + args: { + table: { + name: 'hdb_table', + schema: 'hdb_catalog', + }, + columns: ['table_schema'], + where: { table_schema: 'public' }, + limit: 1, + }, + }), + }; + fetch(url, requestOptions) + .then(response => { + if (response.status === 200) { + successCallback(); + if (shouldPersist) { + saveAdminSecretState(adminSecret); + } + dispatch( + updateDataHeaders({ + 'content-type': 'application/json', + [adminSecretKeyString]: adminSecret, + }) + ); + dispatch(push(globals.urlPrefix)); + } else { + errorCallback(true); + } + }) + .catch(error => { + console.error(error); + errorCallback(error); + }); +}; + +export const handleHeaderInit = () => (dispatch, getState) => { + // get headers from local storage and parse them, set default headers if they are not parseable + const graphiqlHeadersFromLocalStorage = getGraphiQLHeadersFromLocalStorage(); + const defaultGraphiqlHeaders = [ + { + key: 'content-type', + value: 'application/json', + isActive: true, + isNewHeader: false, + isDisabled: false, + }, + ]; + let graphiqlHeaders; + try { + graphiqlHeaders = graphiqlHeadersFromLocalStorage + ? JSON.parse(graphiqlHeadersFromLocalStorage) + : defaultGraphiqlHeaders; + } catch (e) { + graphiqlHeaders = defaultGraphiqlHeaders; + } + + // add an empty placeholder header if not present + const lastHeader = graphiqlHeaders[graphiqlHeaders.length - 1]; + if (lastHeader.key) { + graphiqlHeaders.push({ + key: '', + value: '', + isActive: true, + isNewHeader: true, + isDisabled: false, + }); + } + + // persist these headers back to local storage + setGraphiQLHeadersInLocalStorage(JSON.stringify(graphiqlHeaders)); + + //append admin secret to headers + if (globals.consoleMode === 'server' && globals.isAdminSecretSet) { + const adminSecretFromLs = loadAdminSecretState(); + if (adminSecretFromLs) { + graphiqlHeaders = [ + ...graphiqlHeaders.slice(0, graphiqlHeaders.length - 1), + { + key: adminSecretKeyString, + value: adminSecretFromLs, + isActive: true, + isNewHeader: false, + isDisabled: true, + }, + graphiqlHeaders[graphiqlHeaders.length - 1], + ]; + } else { + const adminSecretInRedux = getState().tables.dataHeaders[ + adminSecretKeyString + ]; + graphiqlHeaders = [ + ...graphiqlHeaders.slice(0, graphiqlHeaders.length - 1), + { + key: adminSecretKeyString, + value: adminSecretInRedux, + isActive: true, + isNewHeader: false, + isDisabled: true, + }, + graphiqlHeaders[graphiqlHeaders.length - 1], + ]; + } + } else if (globals.adminSecret) { + graphiqlHeaders = [ + ...graphiqlHeaders.slice(0, graphiqlHeaders.length - 1), + { + key: adminSecretKeyString, + value: globals.adminSecret, + isActive: true, + isNewHeader: false, + isDisabled: true, + }, + graphiqlHeaders[graphiqlHeaders.length - 1], + ]; + } + + // set headers in redux + dispatch(setHeadersBulk(graphiqlHeaders)); +}; diff --git a/console/src/components/Login/Login.js b/console/src/components/Login/Login.js index 530d439b5bf1c..953cb20df522c 100644 --- a/console/src/components/Login/Login.js +++ b/console/src/components/Login/Login.js @@ -1,96 +1,112 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import React, { useState } from 'react'; import Helmet from 'react-helmet'; import Button from '../Common/Button/Button'; import globals from '../../Globals'; -import { loginClicked, UPDATE_ADMIN_SECRET_INPUT } from '../Main/Actions'; +import { verifyLogin } from './Actions'; -class Login extends Component { - handleAdminSecret = e => { - this.props.dispatch({ - type: UPDATE_ADMIN_SECRET_INPUT, - data: e.target.value, - }); - }; +const styles = require('./Login.scss'); +const hasuraLogo = require('./blue-logo.svg'); - loginClicked = () => { - this.props.dispatch(loginClicked()); - }; +const Login = ({ dispatch }) => { + // request state + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); - render() { - const { loginInProgress, loginError, dispatch } = this.props; + // should persist admin secret + const [shouldPersist, setShouldPersist] = useState(true); + const toggleShouldPersist = () => setShouldPersist(!shouldPersist); - const styles = require('./Login.scss'); - const hasuraLogo = require('./blue-logo.svg'); + // input handler + const [adminSecretInput, setAdminSecretInput] = useState(''); + const onAdminSecretChange = e => setAdminSecretInput(e.target.value); - let loginText = 'Enter'; - if (loginInProgress) { - loginText = ( - - Verifying... -