From 89a628e91a78cca0dfca4d65d4de94fc963c8ab8 Mon Sep 17 00:00:00 2001 From: Hoang Pham Date: Fri, 17 Dec 2021 18:20:20 -0600 Subject: [PATCH] Added Featurebase UI code for authentication --- http/handler.go | 2 +- lattice/src/App.tsx | 77 +++++--------- lattice/src/App/AuthFlow/AuthFlow.module.scss | 56 +++++++++++ lattice/src/App/AuthFlow/Login.tsx | 34 +++++++ lattice/src/App/AuthFlow/SignInButton.tsx | 19 ++++ lattice/src/App/AuthFlow/SignOutButton.tsx | 19 ++++ lattice/src/App/AuthFlow/index.ts | 1 + lattice/src/App/Login/Login.tsx | 19 ---- lattice/src/App/Login/LoginButton.tsx | 11 -- lattice/src/App/Login/index.ts | 1 - lattice/src/Main.tsx | 67 ++++++++++++ lattice/src/assets/bg-pattern.png | Bin 0 -> 67342 bytes lattice/src/assets/m-bug-alt.svg | 16 +++ lattice/src/index.tsx | 22 ++-- lattice/src/services/eventServices.tsx | 38 +++---- lattice/src/services/useAuth.tsx | 95 ++++++++++++++++++ lattice/src/shared/Header/Header.tsx | 42 ++++++-- lattice/src/shared/Nav/Nav.tsx | 7 -- .../src/shared/PrivateRoute/PrivateRoute.tsx | 33 ++++++ lattice/src/theme/darkTheme.tsx | 4 +- lattice/src/theme/lightTheme.tsx | 4 +- 21 files changed, 438 insertions(+), 129 deletions(-) create mode 100644 lattice/src/App/AuthFlow/AuthFlow.module.scss create mode 100644 lattice/src/App/AuthFlow/Login.tsx create mode 100644 lattice/src/App/AuthFlow/SignInButton.tsx create mode 100644 lattice/src/App/AuthFlow/SignOutButton.tsx create mode 100644 lattice/src/App/AuthFlow/index.ts delete mode 100644 lattice/src/App/Login/Login.tsx delete mode 100644 lattice/src/App/Login/LoginButton.tsx delete mode 100644 lattice/src/App/Login/index.ts create mode 100644 lattice/src/Main.tsx create mode 100644 lattice/src/assets/bg-pattern.png create mode 100644 lattice/src/assets/m-bug-alt.svg create mode 100644 lattice/src/services/useAuth.tsx create mode 100644 lattice/src/shared/PrivateRoute/PrivateRoute.tsx diff --git a/http/handler.go b/http/handler.go index 0852242aa..9ebdadc9b 100644 --- a/http/handler.go +++ b/http/handler.go @@ -363,7 +363,7 @@ func (h *Handler) collectStats(next http.Handler) http.Handler { // latticeRoutes lists the frontend routes that do not directly correspond to // backend routes, and require special handling. -var latticeRoutes = []string{"/tables", "/query", "/querybuilder", "/login"} // TODO somehow pull this from some metadata in the lattice directory +var latticeRoutes = []string{"/tables", "/query", "/querybuilder", "/signin"} // TODO somehow pull this from some metadata in the lattice directory // newRouter creates a new mux http router. func newRouter(handler *Handler) http.Handler { diff --git a/lattice/src/App.tsx b/lattice/src/App.tsx index bf55e0ace..8481007b9 100644 --- a/lattice/src/App.tsx +++ b/lattice/src/App.tsx @@ -1,60 +1,37 @@ -import React, { useEffect, useState } from 'react'; -import CssBaseline from '@material-ui/core/CssBaseline'; -import { Route, Switch } from 'react-router-dom'; -import { darkTheme, lightTheme } from 'theme/'; -import { Home } from 'App/Home'; -import { Header } from 'shared/Header'; +import Login from 'App/AuthFlow/Login'; +import Main from 'Main'; +import { BrowserRouter, Route, Switch } from 'react-router-dom'; +import { useAuth } from 'services/useAuth'; +import PrivateRoute from 'shared/PrivateRoute/PrivateRoute'; +import { lightTheme } from 'theme/'; + import { MuiThemeProvider } from '@material-ui/core/styles'; -import { Nav } from 'shared/Nav'; -import { NotFound } from 'App/NotFound'; -import { MoleculaTablesContainer } from 'App/MoleculaTables'; -import { QueryContainer } from 'App/Query'; -import { QueryBuilderContainer } from 'App/QueryBuilder'; -import css from './App.module.scss'; -import Login from 'App/Login/Login'; const App = () => { - const [theme, setTheme] = useState( - localStorage.getItem('theme') || 'light' - ); - - useEffect(() => { - if(theme === 'dark') { - document.documentElement.setAttribute('data-theme', 'dark') - } else { - document.documentElement.removeAttribute('data-theme'); - } - }, [theme]); - - const onToggleTheme = () => { - const newTheme = theme === 'dark' ? 'light' : 'dark'; - setTheme(newTheme); - localStorage.setItem('theme', newTheme); - }; + const auth = useAuth(); return ( - - -
- -
-
-
-
- + ) : ( + + )} + + )} + ); -} +}; export default App; diff --git a/lattice/src/App/AuthFlow/AuthFlow.module.scss b/lattice/src/App/AuthFlow/AuthFlow.module.scss new file mode 100644 index 000000000..5ac275622 --- /dev/null +++ b/lattice/src/App/AuthFlow/AuthFlow.module.scss @@ -0,0 +1,56 @@ +.main { + min-height: 100vh; + background-repeat: no-repeat; + background-image: linear-gradient( + to bottom, + rgba(250, 250, 250, 1), + rgba(250, 250, 250, 0.7) + ), + url(/assets/bg-pattern.png); + background-size: cover; + padding-bottom: 32px; +} + +.logoContainer { + text-align: center; +} + +.logo { + height: 85px; + margin: 16px; +} + +.loginForm { + width: 500px; + margin: 0 auto; + padding-top: 75px; +} + +.formError { + color: #f44336; + margin-bottom: 16px; +} + +.sso { + text-align: center; + padding: 24px 0 16px; +} + +.passwordField { + position: relative; + + .forgotPassword { + // [syang] Eww yes, I hate this + position: absolute; + right: 0; + z-index: 1; + } +} + +.backToSignIn { + padding: 24px 0 16px; +} + +.alert { + margin-bottom: 16px; +} diff --git a/lattice/src/App/AuthFlow/Login.tsx b/lattice/src/App/AuthFlow/Login.tsx new file mode 100644 index 000000000..a050e7ebf --- /dev/null +++ b/lattice/src/App/AuthFlow/Login.tsx @@ -0,0 +1,34 @@ +import { ReactComponent as MLogo } from 'assets/m-bug-alt.svg'; + +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; +import CardHeader from '@material-ui/core/CardHeader'; + +import css from './AuthFlow.module.scss'; +import SignInButton from './SignInButton'; + +function Login(props) { + const renderLoginForm = () => ( + + + + + + + ); + + return ( +
+
+
+ +
+ {renderLoginForm()} +
+
+ ); +} +export default Login; diff --git a/lattice/src/App/AuthFlow/SignInButton.tsx b/lattice/src/App/AuthFlow/SignInButton.tsx new file mode 100644 index 000000000..b46ea8ba0 --- /dev/null +++ b/lattice/src/App/AuthFlow/SignInButton.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import { Button } from '@material-ui/core'; + +interface Props { + children?: React.ReactNode; +} + +const SignInButton: React.FC = ({ children }) => { + return ( + + + + ); +}; + +export default SignInButton; diff --git a/lattice/src/App/AuthFlow/SignOutButton.tsx b/lattice/src/App/AuthFlow/SignOutButton.tsx new file mode 100644 index 000000000..7ff19b99f --- /dev/null +++ b/lattice/src/App/AuthFlow/SignOutButton.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +import { Button } from '@material-ui/core'; + +interface Props { + children?: React.ReactNode; +} + +const SignOutButton: React.FC = ({ children }) => { + return ( + + + + ); +}; + +export default SignOutButton; diff --git a/lattice/src/App/AuthFlow/index.ts b/lattice/src/App/AuthFlow/index.ts new file mode 100644 index 000000000..f1d32a23a --- /dev/null +++ b/lattice/src/App/AuthFlow/index.ts @@ -0,0 +1 @@ +export * from './Login'; \ No newline at end of file diff --git a/lattice/src/App/Login/Login.tsx b/lattice/src/App/Login/Login.tsx deleted file mode 100644 index ebd4a1f7b..000000000 --- a/lattice/src/App/Login/Login.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import LoginButton from './LoginButton'; -import { pilosa } from 'services/eventServices'; - -function login() { - pilosa.get.login().then((res) => { - console.log(`login result:`, res); - }); -} - -function Login() { - return ( - <> -
Login
- - - ); -} - -export default Login; diff --git a/lattice/src/App/Login/LoginButton.tsx b/lattice/src/App/Login/LoginButton.tsx deleted file mode 100644 index 88b09634a..000000000 --- a/lattice/src/App/Login/LoginButton.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -interface Props { - onClick: () => void; -} - -const LoginButton: React.FC = ({ onClick }) => { - return ; -}; - -export default LoginButton; diff --git a/lattice/src/App/Login/index.ts b/lattice/src/App/Login/index.ts deleted file mode 100644 index a10c3a83a..000000000 --- a/lattice/src/App/Login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Login'; diff --git a/lattice/src/Main.tsx b/lattice/src/Main.tsx new file mode 100644 index 000000000..bfa1946ee --- /dev/null +++ b/lattice/src/Main.tsx @@ -0,0 +1,67 @@ +import { Home } from "App/Home"; +import { MoleculaTablesContainer } from "App/MoleculaTables"; +import { NotFound } from "App/NotFound"; +import { QueryContainer } from "App/Query"; +import { QueryBuilderContainer } from "App/QueryBuilder"; +import { useEffect, useState } from "react"; +import { Route, Switch } from "react-router-dom"; +import { Header } from "shared/Header"; +import { Nav } from "shared/Nav"; +import { darkTheme, lightTheme } from "theme/"; + +import CssBaseline from "@material-ui/core/CssBaseline"; +import { MuiThemeProvider } from "@material-ui/core/styles"; + +import css from "./App.module.scss"; + +const Main = () => { + const [theme, setTheme] = useState( + localStorage.getItem("theme") || "light" + ); + + useEffect(() => { + if (theme === "dark") { + document.documentElement.setAttribute("data-theme", "dark"); + } else { + document.documentElement.removeAttribute("data-theme"); + } + }, [theme]); + + const onToggleTheme = () => { + const newTheme = theme === "dark" ? "light" : "dark"; + setTheme(newTheme); + localStorage.setItem("theme", newTheme); + }; + + return ( +
+ + +
+
+
+
+
+ +
+ ); +}; + +export default Main; diff --git a/lattice/src/assets/bg-pattern.png b/lattice/src/assets/bg-pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..23bdf09be698e50e8e530146305ff60276e1dee9 GIT binary patch literal 67342 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%0ATmFE`coy9^L;PY|$5 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 literal 0 HcmV?d00001 diff --git a/lattice/src/assets/m-bug-alt.svg b/lattice/src/assets/m-bug-alt.svg new file mode 100644 index 000000000..a0cc81bc1 --- /dev/null +++ b/lattice/src/assets/m-bug-alt.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/lattice/src/index.tsx b/lattice/src/index.tsx index 331a583a3..b8b53d0af 100644 --- a/lattice/src/index.tsx +++ b/lattice/src/index.tsx @@ -1,15 +1,19 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './App'; -import { BrowserRouter as Router, Route } from 'react-router-dom'; -import * as serviceWorker from './serviceWorker'; import './index.scss'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { ProvideAuth } from 'services/useAuth'; + +import App from './App'; +import * as serviceWorker from './serviceWorker'; + ReactDOM.render( - - - , - document.getElementById('root') + + + + + , + document.getElementById("root") ); // If you want your app to work offline and load faster, you can change diff --git a/lattice/src/services/eventServices.tsx b/lattice/src/services/eventServices.tsx index 722ebc59f..2fdca224d 100644 --- a/lattice/src/services/eventServices.tsx +++ b/lattice/src/services/eventServices.tsx @@ -1,49 +1,53 @@ import axios from 'axios'; + import { baseURL } from './baseURL'; const api = axios.create({ baseURL, headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Accept: 'application/json' - } + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + }, }); export const pilosa = { get: { status() { - return api.get('/status'); + return api.get("/status"); }, - login() { - return api.get('/login'); + auth() { + return api.get("/auth"); + }, + userinfo() { + return api.get("/userinfo"); }, info() { - return api.get('/info'); + return api.get("/info"); }, version() { - return api.get('/version'); + return api.get("/version"); }, transactions() { - return api.get('/ui/transaction'); + return api.get("/ui/transaction"); }, transaction(id) { return api.get(`/transaction/${id}`); }, schema() { - return api.get('/schema'); + return api.get("/schema"); }, schemaDetails() { - return api.get('/schema/details'); + return api.get("/schema/details"); }, metrics() { - return api.get('/metrics.json'); + return api.get("/metrics.json"); }, usage() { - return api.get('/ui/usage'); + return api.get("/ui/usage"); }, queryHistory() { - return api.get('/query-history'); - } + return api.get("/query-history"); + }, }, post: { finishTransaction(id) { @@ -51,6 +55,6 @@ export const pilosa = { }, query(index, query) { return api.post(`/index/${index}/query`, query); - } - } + }, + }, }; diff --git a/lattice/src/services/useAuth.tsx b/lattice/src/services/useAuth.tsx new file mode 100644 index 000000000..d6f5c7421 --- /dev/null +++ b/lattice/src/services/useAuth.tsx @@ -0,0 +1,95 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; + +import { pilosa } from './eventServices'; + +const authContext = createContext({}); + +// Provider component that wraps your app and makes auth object ... +// ... available to any child component that calls useAuth(). +export function ProvideAuth({ children }) { + const auth = useProvideAuth(); + return {children}; +} + +// Hook for child components to get the auth object ... +// ... and re-render when it changes. +export const useAuth = () => { + return useContext(authContext); +}; + +export interface IUser { + userid: string; + username: string; +} + +// Provider hook that creates auth object and handles state +function useProvideAuth() { + const history = useHistory(); + const [user, setUser] = useState(undefined); + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [authOn, setAuthOn] = useState(true); + + const userinfo = () => { + pilosa.get.userinfo().then((userinfoRes) => { + if (userinfoRes.data.userid && userinfoRes.data.username) { + setUser(userinfoRes.data); + } else { + setUser(undefined); + } + }); + }; + + const signin = () => { + history.push(`/login`); + }; + + const signout = () => { + history.push("/logout"); + }; + // Subscribe to user on mount + // Because this sets state in the callback it will cause any ... + // ... component that utilizes this hook to re-render with the ... + // ... latest auth object. + useEffect(() => { + pilosa.get + .auth() + .then((res) => { + // User is authenticated + if (res.data === "OK") { + setAuthOn(true); + setIsAuthenticated(true); + + // get userinfo + userinfo(); + } + // Auth is off + else if ( + res.data.startsWith( + "Trying to authenticate but authentication is off" + ) + ) { + setAuthOn(false); + } + // User not authenticated + else { + setAuthOn(true); + setIsAuthenticated(false); + } + }) + .finally(() => { + setIsLoading(false); + }); + }, []); + + return { + isAuthenticated, + isLoading, + user, + authOn, + userinfo, + signin, + signout, + }; +} diff --git a/lattice/src/shared/Header/Header.tsx b/lattice/src/shared/Header/Header.tsx index ed2c51bc4..b32e72e63 100644 --- a/lattice/src/shared/Header/Header.tsx +++ b/lattice/src/shared/Header/Header.tsx @@ -1,12 +1,17 @@ -import React, { FC } from 'react'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; -import { Link } from 'react-router-dom'; -import { ReactComponent as MoleculaLogo } from 'assets/lightTheme/MoleculaLogo.svg'; -import { ReactComponent as MoleculaLogoDark } from 'assets/darkTheme/MoleculaLogo.svg'; -import { ThemeToggle } from 'shared/ThemeToggle'; -import { useTheme } from '@material-ui/core/styles'; -import css from './Header.module.scss'; +import SignOutButton from "App/AuthFlow/SignOutButton"; +import { ReactComponent as MoleculaLogoDark } from "assets/darkTheme/MoleculaLogo.svg"; +import { ReactComponent as MoleculaLogo } from "assets/lightTheme/MoleculaLogo.svg"; +import { FC } from "react"; +import { Link } from "react-router-dom"; +import { useAuth } from "services/useAuth"; +import { ThemeToggle } from "shared/ThemeToggle"; + +import AppBar from "@material-ui/core/AppBar"; +import Button from "@material-ui/core/Button"; +import { useTheme } from "@material-ui/core/styles"; +import Toolbar from "@material-ui/core/Toolbar"; + +import css from "./Header.module.scss"; type HeaderProps = { onToggleTheme: () => void; @@ -14,7 +19,8 @@ type HeaderProps = { export const Header: FC = ({ onToggleTheme }) => { const theme = useTheme(); - const isDark = theme.palette.type === 'dark'; + const isDark = theme.palette.type === "dark"; + const auth = useAuth(); return ( = ({ onToggleTheme }) => { /> + + {auth.isAuthenticated ? ( +
+ {auth.user && ( + + )} + +
+ ) : null}
diff --git a/lattice/src/shared/Nav/Nav.tsx b/lattice/src/shared/Nav/Nav.tsx index 52bfeb1dd..e6d718420 100644 --- a/lattice/src/shared/Nav/Nav.tsx +++ b/lattice/src/shared/Nav/Nav.tsx @@ -51,13 +51,6 @@ export const Nav = () => { - - - - Login - - - ); diff --git a/lattice/src/shared/PrivateRoute/PrivateRoute.tsx b/lattice/src/shared/PrivateRoute/PrivateRoute.tsx new file mode 100644 index 000000000..16fe98247 --- /dev/null +++ b/lattice/src/shared/PrivateRoute/PrivateRoute.tsx @@ -0,0 +1,33 @@ +import { Redirect, Route } from 'react-router-dom'; +import { useAuth } from 'services/useAuth'; + +function PrivateRoute({ component: Component, ...rest }) { + const auth = useAuth(); + + return ( + { + // If the user is authed render the component + if (auth.isAuthenticated) { + // if (true) { + return ; + } else { + // If they are not then we need to redirect to a public page + return ( + + ); + } + }} + /> + ); +} + +export default PrivateRoute; diff --git a/lattice/src/theme/darkTheme.tsx b/lattice/src/theme/darkTheme.tsx index 5f8492608..3e48e31c5 100644 --- a/lattice/src/theme/darkTheme.tsx +++ b/lattice/src/theme/darkTheme.tsx @@ -1,8 +1,8 @@ /* tslint:disable */ -import { createMuiTheme } from '@material-ui/core/styles'; +import { createTheme } from '@material-ui/core/styles'; import { baseTheme } from 'theme/'; -export const darkTheme = createMuiTheme({ +export const darkTheme = createTheme({ ...baseTheme, palette: { background: { diff --git a/lattice/src/theme/lightTheme.tsx b/lattice/src/theme/lightTheme.tsx index 3d2e5bc87..fe05fc08f 100644 --- a/lattice/src/theme/lightTheme.tsx +++ b/lattice/src/theme/lightTheme.tsx @@ -1,8 +1,8 @@ /* tslint:disable */ -import { createMuiTheme } from '@material-ui/core/styles'; +import { createTheme } from '@material-ui/core/styles'; import { baseTheme } from 'theme/'; -export const lightTheme = createMuiTheme({ +export const lightTheme = createTheme({ ...baseTheme, palette: { background: {