From f1d867024993a099d7b65c4e091466783c630867 Mon Sep 17 00:00:00 2001 From: npalaska Date: Sun, 12 Feb 2023 04:32:30 -0500 Subject: [PATCH 01/12] Get OIDC access tokens once the authentication redirect is successful --- dashboard/src/actions/authActions.js | 119 +++++++++++++++++- .../AuthComponent/common-components.jsx | 7 +- 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/dashboard/src/actions/authActions.js b/dashboard/src/actions/authActions.js index 94138224ae..832277ed27 100644 --- a/dashboard/src/actions/authActions.js +++ b/dashboard/src/actions/authActions.js @@ -7,8 +7,125 @@ import Cookies from "js-cookie"; import { SUCCESS } from "assets/constants/overviewConstants"; import { showToast } from "actions/toastActions"; import { uid } from "../utils/helper"; +import { fetchEndpoints } from "actions/endpointAction"; +/** + * Get the key value. + * @param {key} key can be a string. + * @return {value} a string value of a key + */ +export function getQueryVariable (key) { + const query = window.location.search.substring(1); + const vars = query.split('&'); + let code = ""; + for (let i = 0; i < vars.length; i++) { + const pair = vars[i].split('='); + if (pair[0] === key) { + code = pair[1]; + } + // else return "no-idea"; + } + return code; + +} + +/** + * Get user details from the OIDC server. + * @param {endpoints} endpoints from the getState. + * @return {user} a dict value of a user details obtained from the token + */ +export function getOIDCUserDetails(endpoints) { + const reqUser = new XMLHttpRequest(); + const OIDCToken = Cookies.get("token"); + const oidcServer = endpoints.openid.server; + const oidcRealm = endpoints.openid.realm; + const uri = `${oidcServer}/realms/${oidcRealm}/protocol/openid-connect/userinfo`; + let userPayload = {}; + return new Promise((resolve, reject) => { + reqUser.onreadystatechange = function() { + if (reqUser.readyState === 4) { + const response = JSON.parse(reqUser.responseText); + userPayload = { + "username": response?.preferred_username, + "email": response?.email, + "first_name": response?.given_name, + "last_name": response?.family_name, + }; + resolve(userPayload); + } + } + reqUser.open('GET', uri, false); + reqUser.setRequestHeader('Content-type', 'application/json'); + reqUser.setRequestHeader('Authorization', 'Bearer ' + OIDCToken); + reqUser.send(); + }); +} + +export const loadTokens = () => async (dispatch, getState) => { + const code = getQueryVariable('code'); + + if (code !== "") { + let endpoints = getState().apiEndpoint.endpoints; + if (!("openid" in endpoints)){ + await dispatch(fetchEndpoints); + } + endpoints = getState().apiEndpoint.endpoints; + + const oidcServer = endpoints["openid"]?.server + const oidcRealm = endpoints["openid"]?.realm + const oidcClient = endpoints["openid"]?.client + const uri = `${oidcServer}/realms/${oidcRealm}/protocol/openid-connect/token`; + const queryParams = [ + 'grant_type=authorization_code', + 'client_id=' + oidcClient, + 'code=' + code, + 'redirect_uri=' + window.location.href.split('?')[0], + ]; + + const req = new XMLHttpRequest(); + req.onreadystatechange = async function () { + if (req.readyState === 4) { + const response = JSON.parse(req.responseText); + const keepUser = getState().userAuth.keepLoggedIn; + const expiryTime = keepUser + ? CONSTANTS.EXPIRY_KEEPUSER_DAYS + : CONSTANTS.EXPIRY_DEFAULT_DAYS; + Cookies.set("isLoggedIn", true, { expires: expiryTime }); + Cookies.set("token", response["access_token"], { + expires: response["expires_in"], + }); + let userPayload = {}; + await getOIDCUserDetails(endpoints).then(res => userPayload = res); + dispatch({ + type: TYPES.GET_USER_DETAILS, + payload: userPayload, + }); + Cookies.set("refresh_token", response["refresh_token"], { + expires: response["refresh_expires_in"], + }); + Cookies.set("username", userPayload.username, { + expires: expiryTime, + }); + const loginDetails = { + isLoggedIn: true, + token: response["access_token"], + username: userPayload.username, + }; + dispatch({ + type: TYPES.SET_LOGIN_DETAILS, + payload: loginDetails, + }); + dispatch(showToast(SUCCESS, "Logged in successfully!")); + } + dispatch({ type: TYPES.COMPLETED }); + } + req.open('POST', uri); + req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + req.send(queryParams.join('&')); + } +} + // Create an Authentication Request export const authenticationRequest = () => async (dispatch, getState) => { try { @@ -23,7 +140,7 @@ export const authenticationRequest = () => async (dispatch, getState) => { 'client_id=' + oidcClient, 'response_type=code', 'redirect_uri=' + window.location.href.split('?')[0], - 'scope=profile', + 'scope=openid profile', 'prompt=login', 'max_age=120' ]; diff --git a/dashboard/src/modules/components/AuthComponent/common-components.jsx b/dashboard/src/modules/components/AuthComponent/common-components.jsx index b6c11ed6ee..64d99ee550 100644 --- a/dashboard/src/modules/components/AuthComponent/common-components.jsx +++ b/dashboard/src/modules/components/AuthComponent/common-components.jsx @@ -18,10 +18,10 @@ import { import { CheckIcon, CloseIcon, TimesIcon } from "@patternfly/react-icons"; import { useDispatch, useSelector } from "react-redux"; import { useNavigate, useOutletContext } from "react-router-dom"; -import { authenticationRequest } from "actions/authActions"; +import { authenticationRequest, loadTokens } from "actions/authActions"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import PBenchLogo from "assets/logo/pbench_logo.svg"; -import React from "react"; +import React, { useEffect } from "react"; import { faAngleLeft } from "@fortawesome/free-solid-svg-icons"; import { movePage } from "actions/authActions"; import { passwordConstraintsText } from "./signupFormData"; @@ -85,6 +85,9 @@ export const AuthForm = () => { const navigatePage = (toPage) => { dispatch(movePage(toPage, navigate)); }; + useEffect(() => { + dispatch(loadTokens()); + }, ); return ( From 9995f7daeffafbe19758b2a3210c27973c0ec400 Mon Sep 17 00:00:00 2001 From: npalaska Date: Thu, 2 Mar 2023 16:28:49 -0500 Subject: [PATCH 02/12] address partial comments --- dashboard/src/actions/authActions.js | 133 ++++++++++++--------------- 1 file changed, 57 insertions(+), 76 deletions(-) diff --git a/dashboard/src/actions/authActions.js b/dashboard/src/actions/authActions.js index 832277ed27..c03868dbaf 100644 --- a/dashboard/src/actions/authActions.js +++ b/dashboard/src/actions/authActions.js @@ -10,26 +10,6 @@ import { uid } from "../utils/helper"; import { fetchEndpoints } from "actions/endpointAction"; -/** - * Get the key value. - * @param {key} key can be a string. - * @return {value} a string value of a key - */ -export function getQueryVariable (key) { - const query = window.location.search.substring(1); - const vars = query.split('&'); - let code = ""; - for (let i = 0; i < vars.length; i++) { - const pair = vars[i].split('='); - if (pair[0] === key) { - code = pair[1]; - } - // else return "no-idea"; - } - return code; - -} - /** * Get user details from the OIDC server. * @param {endpoints} endpoints from the getState. @@ -63,67 +43,68 @@ export function getOIDCUserDetails(endpoints) { } export const loadTokens = () => async (dispatch, getState) => { - const code = getQueryVariable('code'); + const code = new URLSearchParams(window.location.search).get("code"); - if (code !== "") { - let endpoints = getState().apiEndpoint.endpoints; - if (!("openid" in endpoints)){ - await dispatch(fetchEndpoints); - } - endpoints = getState().apiEndpoint.endpoints; + if (code === null) { + return; + } + let endpoints = getState().apiEndpoint.endpoints; + console.log(getState()); + if (!("openid" in endpoints)){ + await dispatch(fetchEndpoints); + } + endpoints = getState().apiEndpoint.endpoints; - const oidcServer = endpoints["openid"]?.server - const oidcRealm = endpoints["openid"]?.realm - const oidcClient = endpoints["openid"]?.client - const uri = `${oidcServer}/realms/${oidcRealm}/protocol/openid-connect/token`; - const queryParams = [ - 'grant_type=authorization_code', - 'client_id=' + oidcClient, - 'code=' + code, - 'redirect_uri=' + window.location.href.split('?')[0], - ]; + const oidcServer = endpoints.openid.server + const oidcRealm = endpoints.openid.realm + const oidcClient = endpoints.openid.client + const uri = `${oidcServer}/realms/${oidcRealm}/protocol/openid-connect/token`; + const queryParams = [ + 'grant_type=authorization_code', + 'client_id=' + oidcClient, + 'code=' + code, + 'redirect_uri=' + window.location.href.split('?')[0], + ]; - const req = new XMLHttpRequest(); - req.onreadystatechange = async function () { - if (req.readyState === 4) { - const response = JSON.parse(req.responseText); - const keepUser = getState().userAuth.keepLoggedIn; - const expiryTime = keepUser - ? CONSTANTS.EXPIRY_KEEPUSER_DAYS - : CONSTANTS.EXPIRY_DEFAULT_DAYS; - Cookies.set("isLoggedIn", true, { expires: expiryTime }); - Cookies.set("token", response["access_token"], { - expires: response["expires_in"], - }); - let userPayload = {}; - await getOIDCUserDetails(endpoints).then(res => userPayload = res); - dispatch({ - type: TYPES.GET_USER_DETAILS, - payload: userPayload, - }); - Cookies.set("refresh_token", response["refresh_token"], { - expires: response["refresh_expires_in"], - }); - Cookies.set("username", userPayload.username, { - expires: expiryTime, - }); - const loginDetails = { - isLoggedIn: true, - token: response["access_token"], - username: userPayload.username, - }; - dispatch({ - type: TYPES.SET_LOGIN_DETAILS, - payload: loginDetails, - }); - dispatch(showToast(SUCCESS, "Logged in successfully!")); - } - dispatch({ type: TYPES.COMPLETED }); + const req = new XMLHttpRequest(); + req.onreadystatechange = async function () { + if (req.readyState === 4) { + const response = JSON.parse(req.responseText); + const keepUser = getState().userAuth.keepLoggedIn; + const expiryTime = keepUser + ? CONSTANTS.EXPIRY_KEEPUSER_DAYS + : CONSTANTS.EXPIRY_DEFAULT_DAYS; + Cookies.set("isLoggedIn", true, { expires: expiryTime }); + Cookies.set("token", response["access_token"], { + expires: response["expires_in"], + }); + const userPayload = await getOIDCUserDetails(endpoints); + dispatch({ + type: TYPES.GET_USER_DETAILS, + payload: userPayload, + }); + Cookies.set("refresh_token", response["refresh_token"], { + expires: response["refresh_expires_in"], + }); + Cookies.set("username", userPayload.username, { + expires: expiryTime, + }); + const loginDetails = { + isLoggedIn: true, + token: response["access_token"], + username: userPayload.username, + }; + dispatch({ + type: TYPES.SET_LOGIN_DETAILS, + payload: loginDetails, + }); + dispatch(showToast(SUCCESS, "Logged in successfully!")); } - req.open('POST', uri); - req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - req.send(queryParams.join('&')); + dispatch({ type: TYPES.COMPLETED }); } + req.open('POST', uri); + req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + req.send(queryParams.join('&')); } // Create an Authentication Request From b3a052ced3d0918a536daf76bb9986f927a5c598 Mon Sep 17 00:00:00 2001 From: npalaska Date: Thu, 9 Mar 2023 14:59:30 -0500 Subject: [PATCH 03/12] remove redundant endpoints reload --- dashboard/src/actions/authActions.js | 47 ++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/dashboard/src/actions/authActions.js b/dashboard/src/actions/authActions.js index c03868dbaf..c96a60e5e5 100644 --- a/dashboard/src/actions/authActions.js +++ b/dashboard/src/actions/authActions.js @@ -1,3 +1,4 @@ +import axios from "axios"; import * as APP_ROUTES from "utils/routeConstants"; import * as CONSTANTS from "../assets/constants/authConstants"; import * as TYPES from "./types"; @@ -10,6 +11,33 @@ import { uid } from "../utils/helper"; import { fetchEndpoints } from "actions/endpointAction"; +/** + * Loop to check if the endpoints are loaded. + * @param {getState} getState object. + * @return {promise} promise object + */ +function waitForEndpoints (getState) { + const waitStart = Date.now(); + const maxWait = 10000; // Milliseconds + /** + * Loop to check if the endpoints are loaded. + * @param {resolve} resolve object. + * @param {reject} reject object + */ + function check(resolve, reject) { + if (Object.keys(getState().apiEndpoint.endpoints).length !== 0) { + resolve("Loaded"); + } else if (Date.now() - waitStart > maxWait) { + reject(new Error('Something went wrong')); + } else { + setTimeout(check, 250, resolve, reject); + } + }; + return new Promise((resolve, reject) => { + check(resolve, reject); + }); +} + /** * Get user details from the OIDC server. * @param {endpoints} endpoints from the getState. @@ -50,21 +78,20 @@ export const loadTokens = () => async (dispatch, getState) => { } let endpoints = getState().apiEndpoint.endpoints; console.log(getState()); - if (!("openid" in endpoints)){ - await dispatch(fetchEndpoints); - } + await waitForEndpoints(getState); endpoints = getState().apiEndpoint.endpoints; + console.log(endpoints); const oidcServer = endpoints.openid.server const oidcRealm = endpoints.openid.realm const oidcClient = endpoints.openid.client const uri = `${oidcServer}/realms/${oidcRealm}/protocol/openid-connect/token`; - const queryParams = [ - 'grant_type=authorization_code', - 'client_id=' + oidcClient, - 'code=' + code, - 'redirect_uri=' + window.location.href.split('?')[0], - ]; + const queryParams = new URLSearchParams({ + grant_type: "authorization_code", + client_id: oidcClient, + code: code, + redirect_uri: window.location.href.split('?')[0] + }); const req = new XMLHttpRequest(); req.onreadystatechange = async function () { @@ -104,7 +131,7 @@ export const loadTokens = () => async (dispatch, getState) => { } req.open('POST', uri); req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - req.send(queryParams.join('&')); + req.send(queryParams); } // Create an Authentication Request From 474457ed56d973e4197e462367eca5c5d0a33d6b Mon Sep 17 00:00:00 2001 From: npalaska Date: Tue, 21 Mar 2023 16:37:18 -0400 Subject: [PATCH 04/12] Dashboard integration with keyclok - New dependency added @react-keycloak/web and keycloak-js - Checks the SSO session by wrapping the entire App inside KeycloakProvider. - Removed the current use of login and registration related components. PBENCH-1073 --- dashboard/package.json | 2 + dashboard/src/App.js | 37 ++- dashboard/src/actions/authActions.js | 291 ++---------------- dashboard/src/actions/endpointAction.js | 10 + dashboard/src/actions/overviewActions.js | 3 +- dashboard/src/actions/profileActions.js | 85 ----- dashboard/src/actions/toastActions.js | 4 +- dashboard/src/actions/types.js | 1 + .../components/AuthComponent/LoginForm.jsx | 153 --------- .../components/AuthComponent/SignupForm.jsx | 223 -------------- .../AuthComponent/common-components.jsx | 88 +----- .../AuthComponent/signupFormData.js | 70 ----- .../components/HeaderComponent/index.jsx | 15 +- .../components/OverviewComponent/index.jsx | 3 +- .../components/ProfileComponent/index.jsx | 122 +------- .../components/SidebarComponent/index.jsx | 7 +- .../components/TableComponent/index.jsx | 5 +- dashboard/src/reducers/endpointReducer.js | 9 +- dashboard/src/utils/axiosInstance.js | 9 +- 19 files changed, 110 insertions(+), 1027 deletions(-) delete mode 100644 dashboard/src/actions/profileActions.js delete mode 100644 dashboard/src/modules/components/AuthComponent/LoginForm.jsx delete mode 100644 dashboard/src/modules/components/AuthComponent/SignupForm.jsx delete mode 100644 dashboard/src/modules/components/AuthComponent/signupFormData.js diff --git a/dashboard/package.json b/dashboard/package.json index 887e399cc5..4e78c1b249 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -9,6 +9,7 @@ "@patternfly/patternfly": "^4.183.1", "@patternfly/react-core": "^4.198.19", "@patternfly/react-table": "^4.75.2", + "@react-keycloak/web": "^3.4.0", "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", @@ -21,6 +22,7 @@ "gulp": "^4.0.2", "jest": "^27.5.1", "js-cookie": "^3.0.1", + "keycloak-js": "^21.0.1", "less-watch-compiler": "^1.16.3", "patternfly": "^3.9.0", "react": "^17.0.2", diff --git a/dashboard/src/App.js b/dashboard/src/App.js index d55e79b3d6..45505eb462 100644 --- a/dashboard/src/App.js +++ b/dashboard/src/App.js @@ -16,21 +16,29 @@ import { AuthForm } from "modules/components/AuthComponent/common-components"; import AuthLayout from "modules/containers/AuthLayout"; import ComingSoonPage from "modules/components/EmptyPageComponent/ComingSoon"; import Cookies from "js-cookie"; -import LoginForm from "modules/components/AuthComponent/LoginForm"; import MainLayout from "modules/containers/MainLayout"; import NoMatchingPage from "modules/components/EmptyPageComponent/NoMatchingPage"; import OverviewComponent from "modules/components/OverviewComponent"; import ProfileComponent from "modules/components/ProfileComponent"; -import SignupForm from "modules/components/AuthComponent/SignupForm"; import TableOfContent from "modules/components/TableOfContent"; import TableWithFavorite from "modules/components/TableComponent"; import favicon from "./assets/logo/favicon.ico"; import { fetchEndpoints } from "./actions/endpointAction"; -import { getUserDetails } from "actions/authActions"; import { showToast } from "actions/toastActions"; -import { useDispatch } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; +import { ReactKeycloakProvider } from '@react-keycloak/web'; -const ProtectedRoute = ({ redirectPath = APP_ROUTES.AUTH_LOGIN, children }) => { +const eventLogger = (event, error) => { + // We might want to consider to refresh the tokens here + // if the event === 'onTokenExpired' +}; + +const tokenLogger = (tokens) => { + // Placeholder for to perform action when new token is generated + // console.log('onKeycloakTokens', tokens["refreshToken"]); +}; + +const ProtectedRoute = ({ redirectPath = APP_ROUTES.AUTH, children }) => { const loggedIn = Cookies.get("isLoggedIn"); const dispatch = useDispatch(); @@ -47,25 +55,36 @@ const HomeRoute = ({ redirectPath = APP_ROUTES.HOME }) => { const App = () => { const dispatch = useDispatch(); + const { keycloak } = useSelector( + state => state.apiEndpoint + ); useEffect(() => { const faviconLogo = document.getElementById("favicon"); faviconLogo?.setAttribute("href", favicon); dispatch(fetchEndpoints); - dispatch(getUserDetails()); }, [dispatch]); return (
+ { keycloak && ( + }> }> - } /> } /> - } /> }> } /> @@ -97,6 +116,8 @@ const App = () => { + + )}
); }; diff --git a/dashboard/src/actions/authActions.js b/dashboard/src/actions/authActions.js index c96a60e5e5..1ca7a9b97d 100644 --- a/dashboard/src/actions/authActions.js +++ b/dashboard/src/actions/authActions.js @@ -1,14 +1,10 @@ -import axios from "axios"; import * as APP_ROUTES from "utils/routeConstants"; import * as CONSTANTS from "../assets/constants/authConstants"; import * as TYPES from "./types"; -import API from "../utils/axiosInstance"; import Cookies from "js-cookie"; import { SUCCESS } from "assets/constants/overviewConstants"; import { showToast } from "actions/toastActions"; -import { uid } from "../utils/helper"; -import { fetchEndpoints } from "actions/endpointAction"; /** @@ -16,7 +12,7 @@ import { fetchEndpoints } from "actions/endpointAction"; * @param {getState} getState object. * @return {promise} promise object */ -function waitForEndpoints (getState) { +export function waitForKeycloak (getState) { const waitStart = Date.now(); const maxWait = 10000; // Milliseconds /** @@ -38,203 +34,26 @@ function waitForEndpoints (getState) { }); } -/** - * Get user details from the OIDC server. - * @param {endpoints} endpoints from the getState. - * @return {user} a dict value of a user details obtained from the token - */ -export function getOIDCUserDetails(endpoints) { - const reqUser = new XMLHttpRequest(); - const OIDCToken = Cookies.get("token"); - const oidcServer = endpoints.openid.server; - const oidcRealm = endpoints.openid.realm; - const uri = `${oidcServer}/realms/${oidcRealm}/protocol/openid-connect/userinfo`; - let userPayload = {}; - return new Promise((resolve, reject) => { - reqUser.onreadystatechange = function() { - if (reqUser.readyState === 4) { - const response = JSON.parse(reqUser.responseText); - userPayload = { - "username": response?.preferred_username, - "email": response?.email, - "first_name": response?.given_name, - "last_name": response?.family_name, - }; - resolve(userPayload); - } - } - reqUser.open('GET', uri, false); - reqUser.setRequestHeader('Content-type', 'application/json'); - reqUser.setRequestHeader('Authorization', 'Bearer ' + OIDCToken); - reqUser.send(); - }); -} - -export const loadTokens = () => async (dispatch, getState) => { - const code = new URLSearchParams(window.location.search).get("code"); - - if (code === null) { - return; - } - let endpoints = getState().apiEndpoint.endpoints; - console.log(getState()); - await waitForEndpoints(getState); - endpoints = getState().apiEndpoint.endpoints; - console.log(endpoints); - - const oidcServer = endpoints.openid.server - const oidcRealm = endpoints.openid.realm - const oidcClient = endpoints.openid.client - const uri = `${oidcServer}/realms/${oidcRealm}/protocol/openid-connect/token`; - const queryParams = new URLSearchParams({ - grant_type: "authorization_code", - client_id: oidcClient, - code: code, - redirect_uri: window.location.href.split('?')[0] - }); - - const req = new XMLHttpRequest(); - req.onreadystatechange = async function () { - if (req.readyState === 4) { - const response = JSON.parse(req.responseText); - const keepUser = getState().userAuth.keepLoggedIn; - const expiryTime = keepUser - ? CONSTANTS.EXPIRY_KEEPUSER_DAYS - : CONSTANTS.EXPIRY_DEFAULT_DAYS; - Cookies.set("isLoggedIn", true, { expires: expiryTime }); - Cookies.set("token", response["access_token"], { - expires: response["expires_in"], - }); - const userPayload = await getOIDCUserDetails(endpoints); - dispatch({ - type: TYPES.GET_USER_DETAILS, - payload: userPayload, - }); - Cookies.set("refresh_token", response["refresh_token"], { - expires: response["refresh_expires_in"], - }); - Cookies.set("username", userPayload.username, { - expires: expiryTime, - }); - const loginDetails = { - isLoggedIn: true, - token: response["access_token"], - username: userPayload.username, - }; - dispatch({ - type: TYPES.SET_LOGIN_DETAILS, - payload: loginDetails, - }); - dispatch(showToast(SUCCESS, "Logged in successfully!")); - } - dispatch({ type: TYPES.COMPLETED }); - } - req.open('POST', uri); - req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - req.send(queryParams); -} - -// Create an Authentication Request -export const authenticationRequest = () => async (dispatch, getState) => { - try { - const endpoints = getState().apiEndpoint.endpoints; - const oidcServer = endpoints.openid.server; - const oidcRealm = endpoints.openid.realm; - const oidcClient = endpoints.openid.client; - // URI parameters ref: https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint - // Refer Step 3 of pbench/docs/user_authentication/third_party_token_management.md - const uri = `${oidcServer}/realms/${oidcRealm}/protocol/openid-connect/auth`; - const queryParams = [ - 'client_id=' + oidcClient, - 'response_type=code', - 'redirect_uri=' + window.location.href.split('?')[0], - 'scope=openid profile', - 'prompt=login', - 'max_age=120' - ]; - window.location.href = uri + '?' + queryParams.join('&'); - } catch (error) { - const alerts = getState().userAuth.alerts; - dispatch(error?.response - ? toggleLoginBtn(true) - : { type: TYPES.OPENID_ERROR } - ); - const alert = { - title: error?.response ? error.response.data?.message : error?.message, - key: uid(), +// Perform some house keeping when the user logs in +export const authCookies = () => async (dispatch, getState) => { + await waitForKeycloak(getState); + const keycloak = getState().apiEndpoint.keycloak; + if (keycloak.authenticated){ + Cookies.set("isLoggedIn", true, { expires: keycloak.refreshTokenParsed?.exp }); + Cookies.set("username", keycloak.tokenParsed?.preferred_username, { expires: keycloak.refreshTokenParsed?.exp }); + const userPayload = { + "username": keycloak.tokenParsed?.preferred_username, + "email": keycloak.tokenParsed?.email, + "first_name": keycloak.tokenParsed?.given_name, + "last_name": keycloak.tokenParsed?.family_name, }; dispatch({ - type: TYPES.USER_NOTION_ALERTS, - payload: [...alerts, alert], - }); - dispatch({ type: TYPES.COMPLETED }); - } -}; - -export const makeLoginRequest = - (details, navigate) => async (dispatch, getState) => { - try { - dispatch({ type: TYPES.LOADING }); - // empty the alerts - dispatch({ - type: TYPES.USER_NOTION_ALERTS, - payload: [], - }); - const endpoints = getState().apiEndpoint.endpoints; - const response = await API.post(endpoints?.api?.login, { - ...details, - }); - if (response.status === 200 && Object.keys(response.data).length > 0) { - const keepUser = getState().userAuth.keepLoggedIn; - const expiryTime = keepUser - ? CONSTANTS.EXPIRY_KEEPUSER_DAYS - : CONSTANTS.EXPIRY_DEFAULT_DAYS; - Cookies.set("isLoggedIn", true, { expires: expiryTime }); - Cookies.set("token", response.data?.auth_token, { - expires: expiryTime, - }); - Cookies.set("username", response.data?.username, { - expires: expiryTime, - }); - const loginDetails = { - isLoggedIn: true, - token: response.data?.auth_token, - username: response.data?.username, - }; - await dispatch({ - type: TYPES.SET_LOGIN_DETAILS, - payload: loginDetails, - }); - - navigate(APP_ROUTES.OVERVIEW); - - dispatch(showToast(SUCCESS, "Logged in successfully!")); - } - dispatch({ type: TYPES.COMPLETED }); - } catch (error) { - const alerts = getState().userAuth.alerts; - let alert = {}; - if (error?.response) { - alert = { - title: error?.response?.data?.message, - key: uid(), - }; - dispatch(toggleLoginBtn(true)); - } else { - alert = { - title: error?.message, - key: uid(), - }; - dispatch({ type: TYPES.NETWORK_ERROR }); - } - alerts.push(alert); - dispatch({ - type: TYPES.USER_NOTION_ALERTS, - payload: alerts, + type: TYPES.GET_USER_DETAILS, + payload: userPayload, }); - dispatch({ type: TYPES.COMPLETED }); + dispatch(showToast(SUCCESS, "Logged in successfully!")); } - }; +} export const movePage = (toPage, navigate) => async (dispatch) => { // empty the alerts @@ -245,83 +64,15 @@ export const movePage = (toPage, navigate) => async (dispatch) => { navigate(toPage); }; -export const setUserLoggedInState = (value) => async (dispatch) => { - dispatch({ - type: TYPES.KEEP_USER_LOGGED_IN, - payload: value, - }); -}; - -export const registerUser = - (details, navigate) => async (dispatch, getState) => { - try { - dispatch({ type: TYPES.LOADING }); - // empty the alerts - dispatch({ - type: TYPES.USER_NOTION_ALERTS, - payload: [], - }); - const endpoints = getState().apiEndpoint.endpoints; - const response = await API.post(endpoints?.api?.register, { - ...details, - }); - if (response.status === 201) { - dispatch(showToast(SUCCESS, "Account created!", "Login to continue")); - navigate(APP_ROUTES.AUTH_LOGIN); - } - dispatch({ type: TYPES.COMPLETED }); - } catch (error) { - const alerts = getState().userAuth.alerts; - let amsg = {}; - document.querySelector(".signup-card").scrollTo(0, 0); - if (error?.response) { - amsg = error?.response?.data?.message; - dispatch(toggleSignupBtn(true)); - } else { - amsg = error?.message; - dispatch({ type: TYPES.NETWORK_ERROR }); - } - const alert = { title: amsg, key: uid() }; - alerts.push(alert); - dispatch({ - type: TYPES.USER_NOTION_ALERTS, - payload: alerts, - }); - dispatch({ type: TYPES.COMPLETED }); - } - }; - -export const toggleSignupBtn = (isDisabled) => async (dispatch) => { - dispatch({ - type: TYPES.SET_SIGNUP_BUTTON, - payload: isDisabled, - }); -}; - -export const toggleLoginBtn = (isDisabled) => async (dispatch) => { - dispatch({ - type: TYPES.SET_LOGIN_BUTTON, - payload: isDisabled, - }); -}; - -export const getUserDetails = () => async (dispatch) => { - const loginDetails = { - isLoggedIn: Cookies.get("isLoggedIn"), - token: Cookies.get("token"), - username: Cookies.get("username"), - }; - dispatch({ - type: TYPES.SET_LOGIN_DETAILS, - payload: loginDetails, - }); -}; -export const logout = () => async (dispatch) => { +export const logout = () => async (dispatch, getState) => { dispatch({ type: TYPES.LOADING }); + // const keycloak = useKeycloak(); + const keycloak = getState().apiEndpoint.keycloak; const keys = ["username", "token", "isLoggedIn"]; for (const key of keys) { Cookies.remove(key); } + keycloak.logout(); dispatch({ type: TYPES.COMPLETED }); setTimeout(() => { window.location.href = APP_ROUTES.AUTH; diff --git a/dashboard/src/actions/endpointAction.js b/dashboard/src/actions/endpointAction.js index c7020d1995..6568a7f975 100644 --- a/dashboard/src/actions/endpointAction.js +++ b/dashboard/src/actions/endpointAction.js @@ -1,4 +1,5 @@ import * as TYPES from "./types"; +import Keycloak from 'keycloak-js'; export const fetchEndpoints = async (dispatch) => { try { @@ -12,6 +13,15 @@ export const fetchEndpoints = async (dispatch) => { type: TYPES.SET_ENDPOINTS, payload: data, }); + const keycloak = new Keycloak({ + url: data?.openid?.server, + realm: data?.openid?.realm, + clientId: data?.openid?.client, + }); + dispatch({ + type: TYPES.SET_KEYCLOAK, + payload: keycloak, + }); } catch (error) { dispatch({ type: TYPES.SHOW_TOAST, diff --git a/dashboard/src/actions/overviewActions.js b/dashboard/src/actions/overviewActions.js index 324bffa687..773890559d 100644 --- a/dashboard/src/actions/overviewActions.js +++ b/dashboard/src/actions/overviewActions.js @@ -5,11 +5,12 @@ import API from "../utils/axiosInstance"; import { DANGER } from "assets/constants/toastConstants"; import { findNoOfDays } from "utils/dateFunctions"; import { showToast } from "./toastActions"; +import Cookies from "js-cookie"; export const getDatasets = () => async (dispatch, getState) => { const alreadyRendered = getState().overview.loadingDone; try { - const username = getState().userAuth.loginDetails.username; + const username = Cookies.get("username");; if (alreadyRendered) { dispatch({ type: TYPES.LOADING }); diff --git a/dashboard/src/actions/profileActions.js b/dashboard/src/actions/profileActions.js deleted file mode 100644 index 14ae1a6c13..0000000000 --- a/dashboard/src/actions/profileActions.js +++ /dev/null @@ -1,85 +0,0 @@ -import * as TYPES from "./types"; - -import { showFailureToast, showToast } from "./toastActions"; - -import API from "../utils/axiosInstance"; - -export const getProfileDetails = () => async (dispatch, getState) => { - try { - dispatch({ type: TYPES.LOADING }); - - const username = getState().userAuth.loginDetails.username; - const endpoints = getState().apiEndpoint.endpoints; - - const response = await API.get(`${endpoints?.api?.user}/${username}`); - - if (response.status === 200 && Object.keys(response.data).length > 0) { - dispatch({ - type: TYPES.GET_USER_DETAILS, - payload: response?.data, - }); - } else { - dispatch(showFailureToast()); - } - dispatch({ type: TYPES.COMPLETED }); - } catch (error) { - dispatch(showToast("danger", error?.response?.data?.message)); - dispatch({ type: TYPES.NETWORK_ERROR }); - dispatch({ type: TYPES.COMPLETED }); - } -}; - -export const updateUserDetails = - (value, fieldName) => async (dispatch, getState) => { - const userDetails = { ...getState().userProfile.userDetails }; - const updatedUserDetails = { ...getState().userProfile.updatedUserDetails }; - - userDetails[fieldName] = value; - updatedUserDetails[fieldName] = value; - const payload = { - userDetails, - updatedUserDetails, - }; - dispatch({ - type: TYPES.UPDATE_USER_DETAILS, - payload, - }); - }; - -export const sendForUpdate = () => async (dispatch, getState) => { - try { - dispatch({ type: TYPES.LOADING }); - - const username = getState().userAuth.loginDetails.username; - const endpoints = getState().apiEndpoint.endpoints; - - if (username) { - const response = await API.put(`${endpoints?.api?.user}/${username}`, { - ...getState().userProfile.updatedUserDetails, - }); - if (response.status === 200) { - dispatch(showToast("success", "Updated!")); - dispatch({ - type: TYPES.GET_USER_DETAILS, - payload: response?.data, - }); - dispatch({ type: TYPES.RESET_DATA }); - } else { - dispatch(showFailureToast()); - } - dispatch({ type: TYPES.COMPLETED }); - } - } catch (error) { - dispatch(showToast("danger", error?.response?.data?.message)); - dispatch({ type: TYPES.NETWORK_ERROR }); - dispatch({ type: TYPES.COMPLETED }); - } -}; - -export const resetUserDetails = () => async (dispatch, getState) => { - dispatch({ - type: TYPES.SET_USER_DETAILS, - payload: getState().userProfile.userDetails_copy, - }); - dispatch({ type: TYPES.RESET_DATA }); -}; diff --git a/dashboard/src/actions/toastActions.js b/dashboard/src/actions/toastActions.js index 6e22673280..a98d3e4366 100644 --- a/dashboard/src/actions/toastActions.js +++ b/dashboard/src/actions/toastActions.js @@ -1,6 +1,6 @@ import * as TYPES from "./types"; +import * as APP_ROUTES from "utils/routeConstants"; -import { logout } from "./authActions"; import { uid } from "utils/helper"; export const showSessionExpired = () => async (dispatch) => { @@ -10,7 +10,7 @@ export const showSessionExpired = () => async (dispatch) => { message: "Please login to continue", }; dispatch(showToast(toast.variant, toast.title, toast.message)); - dispatch(logout()); + window.location.href = APP_ROUTES.AUTH }; export const showFailureToast = () => async (dispatch) => { diff --git a/dashboard/src/actions/types.js b/dashboard/src/actions/types.js index 5780207860..3bd2630692 100644 --- a/dashboard/src/actions/types.js +++ b/dashboard/src/actions/types.js @@ -1,5 +1,6 @@ /* ENDPOINTS */ export const SET_ENDPOINTS = "SET_ENDPOINTS"; +export const SET_KEYCLOAK = "SET_KEYCLOAK"; /* TOAST */ export const SHOW_TOAST = "SHOW_TOAST"; diff --git a/dashboard/src/modules/components/AuthComponent/LoginForm.jsx b/dashboard/src/modules/components/AuthComponent/LoginForm.jsx deleted file mode 100644 index 3ad49852a4..0000000000 --- a/dashboard/src/modules/components/AuthComponent/LoginForm.jsx +++ /dev/null @@ -1,153 +0,0 @@ -import * as APP_ROUTES from "utils/routeConstants"; - -import { - Alert, - AlertGroup, - Button, - Card, - CardBody, - CardFooter, - CardTitle, - Checkbox, - Form, - FormGroup, - TextInput, -} from "@patternfly/react-core"; -import { - Back, - LoginHeader, - NoLoginComponent, - PasswordTextInput, -} from "./common-components"; -import { EyeIcon, EyeSlashIcon } from "@patternfly/react-icons"; -import React, { useCallback, useEffect, useState } from "react"; -import { makeLoginRequest, setUserLoggedInState } from "actions/authActions"; -import { useDispatch, useSelector } from "react-redux"; - -import { useOutletContext } from "react-router-dom"; - -const LoginForm = () => { - const dispatch = useDispatch(); - const navigate = useOutletContext(); - const alerts = useSelector((state) => state.userAuth.alerts); - const [details, setDetails] = useState({ - password: "", - username: "", - }); - const [btnDisabled, setBtnDisabled] = useState(true); - const [showPassword, setShowPassword] = useState(false); - - const { endpoints } = useSelector((state) => state.apiEndpoint); - const isLoading = useSelector((state) => state.loading.isLoading); - - const primaryLoadingProps = {}; - if (isLoading) { - primaryLoadingProps.spinnerAriaValueText = "Loading"; - primaryLoadingProps.spinnerAriaLabelledBy = "primary-loading-button"; - primaryLoadingProps.isLoading = true; - } - const handleUsernameChange = (value) => { - setDetails({ - ...details, - username: value, - }); - }; - const handlePasswordChange = (value) => { - setDetails({ - ...details, - password: value, - }); - }; - const sendLoginDetails = () => { - dispatch(makeLoginRequest(details, navigate)); - }; - const checkOkButton = useCallback(() => { - if ( - details.username?.length > 0 && - details.password?.length > 0 && - Object.keys(endpoints).length > 0 - ) { - setBtnDisabled(false); - } else { - setBtnDisabled(true); - } - }, [details, endpoints]); - - const keepUser = useSelector((state) => state.userAuth.keepLoggedIn); - const checkBoxChangeHander = (value) => { - dispatch(setUserLoggedInState(value)); - }; - const onShowPassword = () => { - setShowPassword(!showPassword); - }; - useEffect(() => { - checkOkButton(); - }, [checkOkButton, details]); - - return ( - - - - - - {alerts.map(({ title, key }) => ( - - ))} - - - -
- - - - -
- - -
-
- - -
- -
- -
- -
-
- ); -}; - -export default LoginForm; diff --git a/dashboard/src/modules/components/AuthComponent/SignupForm.jsx b/dashboard/src/modules/components/AuthComponent/SignupForm.jsx deleted file mode 100644 index 8ce71859e5..0000000000 --- a/dashboard/src/modules/components/AuthComponent/SignupForm.jsx +++ /dev/null @@ -1,223 +0,0 @@ -import * as APP_ROUTES from "utils/routeConstants"; - -import { - Alert, - AlertGroup, - Button, - Card, - CardBody, - CardFooter, - CardTitle, - Form, - FormGroup, - TextInput, -} from "@patternfly/react-core"; -import { - Back, - LoginHeader, - NoLoginComponent, - PasswordConstraints, - PasswordTextInput, -} from "./common-components"; -import { EyeIcon, EyeSlashIcon } from "@patternfly/react-icons"; -import React, { useCallback, useEffect, useState } from "react"; -import { registerUser, toggleSignupBtn } from "actions/authActions"; -import { useDispatch, useSelector } from "react-redux"; -import { validateEmail, validatePassword } from "utils/helper.js"; - -import { signupFormData } from "./signupFormData"; -import { useOutletContext } from "react-router-dom"; - -const SignupForm = () => { - const dispatch = useDispatch(); - const navigate = useOutletContext(); - const { endpoints } = useSelector((state) => state.apiEndpoint); - const { alerts, isSignupBtnDisabled, passwordLength } = useSelector( - (state) => state.userAuth - ); - const [showPassword, setShowPassword] = useState(false); - const [userDetails, setUserDetails] = useState({ - firstName: "", - lastName: "", - userName: "", - email: "", - password: "", - passwordConfirm: "", - }); - const [errors, setErrors] = useState({ - firstName: "", - lastName: "", - userName: "", - email: "", - passwordConstraints: "", - passwordConfirm: "", - }); - const [constraints, setConstraints] = useState({ - passwordLength: "indeterminate", - passwordSpecialChars: "indeterminate", - passwordContainsNumber: "indeterminate", - passwordBlockLetter: "indeterminate", - }); - - const validateForm = useCallback(() => { - if ( - userDetails.firstName?.trim() === "" || - userDetails.lastName?.trim() === "" || - userDetails.userName?.trim() === "" || - userDetails.email?.trim() === "" || - userDetails.password?.trim() === "" || - userDetails.passwordConfirm?.trim() === "" - ) { - return false; - } - // check if no errors. - for (const dep of Object.entries(errors)) { - if (dep[1].length > 0) { - return false; - } - } - // check if all constraints are met. - for (const ct of Object.entries(constraints)) { - if (ct[1] !== "success") { - return false; - } - } - // if we reach here, it means - // we have covered all of the edge cases. - return true; - }, [constraints, errors, userDetails]); - - const onShowPassword = () => { - setShowPassword(!showPassword); - }; - - useEffect(() => { - if (validateForm() && Object.keys(endpoints).length > 0) { - dispatch(toggleSignupBtn(false)); - } else { - dispatch(toggleSignupBtn(true)); - } - }, [validateForm, userDetails, dispatch, endpoints]); - - const checkPasswordError = (password, cnfPassword) => { - if (password !== cnfPassword) { - setErrors({ - ...errors, - passwordConfirm: "The above passwords do not match!", - }); - } else { - setErrors({ ...errors, passwordConfirm: "" }); - } - }; - const changeHandler = (value, fieldName) => { - setUserDetails({ - ...userDetails, - [fieldName]: value, - }); - if (fieldName === "email") { - const isEmailValid = validateEmail(value); - setErrors({ - ...errors, - ...isEmailValid, - }); - } else if (fieldName === "password") { - const validPassword = validatePassword(value, passwordLength); - setConstraints({ - ...constraints, - ...validPassword, - }); - // edge case where user deliberately - // edits the password field, even when - // confirm password is not empty. - checkPasswordError(value, userDetails.confirmPassword); - } else if (fieldName === "passwordConfirm") { - checkPasswordError(userDetails.password, value); - } - }; - const sendForRegistration = () => { - const details = { - email: userDetails.email, - password: userDetails.password, - first_name: userDetails.firstName, - last_name: userDetails.lastName, - username: userDetails.userName, - }; - dispatch(registerUser({ ...details }, navigate)); - }; - return ( - - - - - - {alerts.map(({ title, key }) => ( - - ))} - - - -
- {signupFormData.map((formItem, index) => { - return ( - -
- {formItem.name === "password" ? ( - - changeHandler(val, formItem.name) - } - /> - ) : ( - changeHandler(val, formItem.name)} - /> - )} - {formItem.name === "password" && ( - - )} -
-

{errors[formItem.name]}

-
- ); - })} -
-
- - -
- -
- -
-
- ); -}; - -export default SignupForm; diff --git a/dashboard/src/modules/components/AuthComponent/common-components.jsx b/dashboard/src/modules/components/AuthComponent/common-components.jsx index 64d99ee550..9c9cdb9ac9 100644 --- a/dashboard/src/modules/components/AuthComponent/common-components.jsx +++ b/dashboard/src/modules/components/AuthComponent/common-components.jsx @@ -5,26 +5,21 @@ import * as APP_ROUTES from "utils/routeConstants"; import { Button, Card, - CardBody, CardFooter, CardTitle, Flex, FlexItem, - HelperText, - HelperTextItem, - TextInput, Title, } from "@patternfly/react-core"; -import { CheckIcon, CloseIcon, TimesIcon } from "@patternfly/react-icons"; -import { useDispatch, useSelector } from "react-redux"; +import { useDispatch } from "react-redux"; import { useNavigate, useOutletContext } from "react-router-dom"; -import { authenticationRequest, loadTokens } from "actions/authActions"; +import { authCookies } from "actions/authActions"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import PBenchLogo from "assets/logo/pbench_logo.svg"; import React, { useEffect } from "react"; import { faAngleLeft } from "@fortawesome/free-solid-svg-icons"; import { movePage } from "actions/authActions"; -import { passwordConstraintsText } from "./signupFormData"; +import { useKeycloak } from '@react-keycloak/web'; export const LoginHeader = (props) => { return {props?.title}; @@ -80,49 +75,25 @@ export const LoginRightComponent = () => { export const AuthForm = () => { const navigate = useOutletContext(); + const { keycloak } = useKeycloak(); const dispatch = useDispatch(); const navigatePage = (toPage) => { dispatch(movePage(toPage, navigate)); }; useEffect(() => { - dispatch(loadTokens()); + dispatch(authCookies()); }, ); return ( - -
- -
-
-
- Need an account? - -
-
- -
-
-
-
Or log in with...
@@ -133,38 +104,6 @@ export const AuthForm = () => { ); }; -export const PasswordConstraints = (props) => { - const { checkConstraints } = props; - const iconList = { - indeterminate: , - success: , - error: , - }; - const passwordLength = useSelector((state) => state.userAuth.passwordLength); - return ( - <> -

Passwords must contain at least:

-
- {passwordConstraintsText.map((constraint, index) => { - const variant = checkConstraints[constraint.name]; - return ( - - - {constraint.name === "passwordLength" && passwordLength}{" "} - {constraint.label} - - - ); - })} -
- - ); -}; - export const NoLoginComponent = () => { const navigate = useNavigate(); const dispatch = useDispatch(); @@ -184,18 +123,3 @@ export const NoLoginComponent = () => {
); }; - -export const PasswordTextInput = (props) => { - const { isRequired, id, name, onChangeMethod, value, isShowPassword } = props; - return ( - onChangeMethod(val, name)} - /> - ); -}; diff --git a/dashboard/src/modules/components/AuthComponent/signupFormData.js b/dashboard/src/modules/components/AuthComponent/signupFormData.js deleted file mode 100644 index a98eee4cc2..0000000000 --- a/dashboard/src/modules/components/AuthComponent/signupFormData.js +++ /dev/null @@ -1,70 +0,0 @@ -export const signupFormData = [ - { - key: 1, - label: "First name", - id: "horizontal-form-first-name", - name: "firstName", - isRequired: true, - type: "text", - }, - { - key: 2, - label: "Last name", - id: "horizontal-form-last-name", - name: "lastName", - isRequired: true, - type: "text", - }, - { - key: 3, - label: "User name", - id: "horizontal-form-user-name", - name: "userName", - isRequired: true, - type: "text", - }, - { - key: 4, - label: "Email address", - id: "horizontal-form-email-address", - name: "email", - isRequired: true, - type: "text", - }, - { - key: 5, - label: "Password", - id: "horizontal-form-password", - name: "password", - isRequired: true, - type: "password", - }, - { - key: 6, - label: "Confirm password", - id: "horizontal-form-confirm-password", - name: "passwordConfirm", - isRequired: true, - type: "password", - }, -]; - - -export const passwordConstraintsText = [ - { - label: "characters", - name: "passwordLength", - }, - { - label: "1 special character", - name: "passwordSpecialChars", - }, - { - label: "1 number", - name: "passwordContainsNumber", - }, - { - label: "1 uppercase letter", - name: "passwordBlockLetter", - } -] diff --git a/dashboard/src/modules/components/HeaderComponent/index.jsx b/dashboard/src/modules/components/HeaderComponent/index.jsx index ea34080021..3f902ac8d7 100644 --- a/dashboard/src/modules/components/HeaderComponent/index.jsx +++ b/dashboard/src/modules/components/HeaderComponent/index.jsx @@ -34,14 +34,19 @@ import { useLocation, useNavigate } from "react-router-dom"; import Cookies from "js-cookie"; import { logout } from "actions/authActions"; import pbenchLogo from "assets/logo/pbench_logo.svg"; +import { useKeycloak } from '@react-keycloak/web'; +import { movePage } from "actions/authActions"; const HeaderToolbar = () => { const dispatch = useDispatch(); - const loginDetails = useSelector((state) => state.userAuth.loginDetails); + const username = Cookies.get("username"); + const { keycloak } = useKeycloak(); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const navigate = useNavigate(); const { pathname } = useLocation(); - const isLoggedIn = Cookies.get("isLoggedIn"); + const navigatePage = (toPage) => { + dispatch(movePage(toPage, navigate)); + }; const onDropdownSelect = (event) => { const type = event.target.name; @@ -91,14 +96,14 @@ const HeaderToolbar = () => { - {isLoggedIn ? ( + {keycloak.authenticated ? ( - {loginDetails?.username} + {username} } dropdownItems={userDropdownItems} @@ -108,7 +113,7 @@ const HeaderToolbar = () => { aria-label="Login" className="header-login-button" variant={ButtonVariant.plain} - onClick={() => navigate(APP_ROUTES.AUTH_LOGIN)} + onClick={() => navigatePage(APP_ROUTES.AUTH)} > Login diff --git a/dashboard/src/modules/components/OverviewComponent/index.jsx b/dashboard/src/modules/components/OverviewComponent/index.jsx index 079e552c3c..613a4b3153 100644 --- a/dashboard/src/modules/components/OverviewComponent/index.jsx +++ b/dashboard/src/modules/components/OverviewComponent/index.jsx @@ -28,7 +28,6 @@ import { getDatasets } from "actions/overviewActions"; const OverviewComponent = () => { const dispatch = useDispatch(); const { endpoints } = useSelector((state) => state.apiEndpoint); - const { loginDetails } = useSelector((state) => state.userAuth); const { expiringRuns, savedRuns, newRuns, loadingDone } = useSelector( (state) => state.overview ); @@ -39,7 +38,7 @@ const OverviewComponent = () => { if (Object.keys(endpoints).length > 0) { dispatch(getDatasets()); } - }, [dispatch, endpoints, loginDetails]); + }, [dispatch, endpoints]); const onToggle = (id) => { if (expanded.has(id)) { diff --git a/dashboard/src/modules/components/ProfileComponent/index.jsx b/dashboard/src/modules/components/ProfileComponent/index.jsx index 6f1fc6bf2e..40e85ddf6b 100644 --- a/dashboard/src/modules/components/ProfileComponent/index.jsx +++ b/dashboard/src/modules/components/ProfileComponent/index.jsx @@ -1,7 +1,6 @@ -import React, { useEffect, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; +import React from "react"; +import { useSelector } from "react-redux"; import { - Button, Card, CardBody, Grid, @@ -10,47 +9,15 @@ import { LevelItem, Text, TextContent, - TextInput, TextVariants, isValidDate, } from "@patternfly/react-core"; -import { KeyIcon, PencilAltIcon, UserAltIcon } from "@patternfly/react-icons"; +import { KeyIcon, UserAltIcon } from "@patternfly/react-icons"; import "./index.less"; import avatar from "assets/images/avatar.jpg"; -import { - getProfileDetails, - resetUserDetails, - sendForUpdate, - updateUserDetails, -} from "actions/profileActions"; const ProfileComponent = () => { - const [editView, setEditView] = useState(false); - const dispatch = useDispatch(); - const user = useSelector((state) => state.userProfile.userDetails); - const loginDetails = useSelector((state) => state.userAuth.loginDetails); - const { endpoints } = useSelector((state) => state.apiEndpoint); - - const isUserDetailsUpdated = useSelector( - (state) => state.userProfile.isUserDetailsUpdated - ); - - const edit = () => { - if (editView) { - dispatch(resetUserDetails()); - } - setEditView(!editView); - }; - const saveEdit = () => { - dispatch(sendForUpdate()); - setEditView(false); - }; - useEffect(() => { - if (loginDetails?.username && Object.keys(endpoints).length > 0) { - dispatch(getProfileDetails()); - } - }, [dispatch, loginDetails?.username, endpoints]); const formatDate = (date) => { const registerDate = new Date(date); @@ -58,9 +25,6 @@ const ProfileComponent = () => { ? registerDate.toLocaleDateString() : "----"; }; - const handleInputChange = (value, name) => { - dispatch(updateUserDetails(value, name)); - }; return (
@@ -78,14 +42,6 @@ const ProfileComponent = () => {
-
Profile Picture
@@ -95,19 +51,7 @@ const ProfileComponent = () => {
First Name
- {editView ? ( - - handleInputChange(val, "first_name") - } - /> - ) : ( + {( {user?.first_name} @@ -117,19 +61,7 @@ const ProfileComponent = () => {
Last Name
- {editView ? ( - - handleInputChange(val, "last_name") - } - /> - ) : ( + {( {user?.last_name} @@ -141,15 +73,7 @@ const ProfileComponent = () => {
User Name
- {editView ? ( - - ) : ( + {( {user?.username} @@ -159,44 +83,14 @@ const ProfileComponent = () => {
Email
- {editView ? ( - handleInputChange(val, "email")} - /> - ) : ( + {( {user?.email} )}
- {editView ? ( - - - {" "} - - - - ) : ( + {( <> )}
diff --git a/dashboard/src/modules/components/SidebarComponent/index.jsx b/dashboard/src/modules/components/SidebarComponent/index.jsx index 7f1a86e904..2ea635594d 100644 --- a/dashboard/src/modules/components/SidebarComponent/index.jsx +++ b/dashboard/src/modules/components/SidebarComponent/index.jsx @@ -9,8 +9,7 @@ import React, { useEffect } from "react"; import { menuOptions, menuOptionsNonLoggedIn } from "./sideMenuOptions"; import { useDispatch, useSelector } from "react-redux"; import { useLocation, useNavigate } from "react-router-dom"; - -import Cookies from "js-cookie"; +import { useKeycloak } from '@react-keycloak/web'; import { setActiveItem } from "actions/sideBarActions"; const MenuItem = ({ data, activeItem }) => { @@ -34,7 +33,7 @@ const Menu = () => { const { pathname } = useLocation(); const activeItem = useSelector((state) => state.sidebar.activeMenuItem); - const isLoggedIn = Cookies.get("isLoggedIn"); + const { keycloak } = useKeycloak(); const onSelect = (result) => { dispatch(setActiveItem(result.itemId)); }; @@ -49,7 +48,7 @@ const Menu = () => { return (