From 12a846e35b1e2c53bc7befe1396c00645275f48c Mon Sep 17 00:00:00 2001 From: Sachin Chaurasiya Date: Tue, 30 May 2023 18:01:52 +0530 Subject: [PATCH] fix(#10190): entities permissions cache issue (#11797) * fix(#10190): entities permissions cache issue * chore: update handler name * fix: component name typo * fix: minor issue * add unit test * chore: revert the reset entities permissions changes * chore: reset everything for permissions on logout * test: add unit test * update unit test --- .../src/main/resources/ui/src/App.tsx | 6 +- .../ErrorBoundary.tsx} | 12 +-- .../ErrorFallback.tsx | 0 .../PermissionProvider/PermissionProvider.tsx | 10 +++ .../auth-provider/AuthProvider.test.tsx | 73 +++++++++++++++++++ .../auth-provider/AuthProvider.tsx | 7 +- 6 files changed, 98 insertions(+), 10 deletions(-) rename openmetadata-ui/src/main/resources/ui/src/components/{ErrorBoundry/ErrorBoundry.tsx => ErrorBoundary/ErrorBoundary.tsx} (76%) rename openmetadata-ui/src/main/resources/ui/src/components/{ErrorBoundry => ErrorBoundary}/ErrorFallback.tsx (100%) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.test.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/App.tsx b/openmetadata-ui/src/main/resources/ui/src/App.tsx index d02474d0a55..8dd5d1a159f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/App.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/App.tsx @@ -13,7 +13,7 @@ import ApplicationConfigProvider from 'components/ApplicationConfigProvider/ApplicationConfigProvider'; import { AuthProvider } from 'components/authentication/auth-provider/AuthProvider'; -import ErrorBoundry from 'components/ErrorBoundry/ErrorBoundry'; +import ErrorBoundary from 'components/ErrorBoundary/ErrorBoundary'; import GlobalSearchProvider from 'components/GlobalSearchProvider/GlobalSearchProvider'; import PermissionProvider from 'components/PermissionProvider/PermissionProvider'; import AppRouter from 'components/router/AppRouter'; @@ -34,7 +34,7 @@ const App: FunctionComponent = () => {
- + @@ -50,7 +50,7 @@ const App: FunctionComponent = () => { - + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundry/ErrorBoundry.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundary/ErrorBoundary.tsx similarity index 76% rename from openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundry/ErrorBoundry.tsx rename to openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundary/ErrorBoundary.tsx index ccb50df9ea3..815d5621b9d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundry/ErrorBoundry.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -12,7 +12,7 @@ */ import React from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; +import { ErrorBoundary as ErrorBoundaryWrapper } from 'react-error-boundary'; import { useHistory } from 'react-router-dom'; import { ROUTES } from '../../constants/constants'; import ErrorFallback from './ErrorFallback'; @@ -21,7 +21,7 @@ interface Props { children: React.ReactNode; } -const ErrorBoundry: React.FC = ({ children }) => { +const ErrorBoundary: React.FC = ({ children }) => { const history = useHistory(); const onErrorReset = () => { @@ -29,10 +29,12 @@ const ErrorBoundry: React.FC = ({ children }) => { }; return ( - + {children} - + ); }; -export default ErrorBoundry; +export default ErrorBoundary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundry/ErrorFallback.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundary/ErrorFallback.tsx similarity index 100% rename from openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundry/ErrorFallback.tsx rename to openmetadata-ui/src/main/resources/ui/src/components/ErrorBoundary/ErrorFallback.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/components/PermissionProvider/PermissionProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/PermissionProvider/PermissionProvider.tsx index 5c7dae3d6e3..92d71e53310 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/PermissionProvider/PermissionProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/PermissionProvider/PermissionProvider.tsx @@ -12,6 +12,7 @@ */ import { CookieStorage } from 'cookie-storage'; +import { isEmpty, isUndefined } from 'lodash'; import { observer } from 'mobx-react'; import React, { createContext, @@ -166,10 +167,19 @@ const PermissionProvider: FC = ({ children }) => { } }; + const resetPermissions = () => { + setEntitiesPermission({} as EntityPermissionMap); + setPermissions({} as UIPermission); + setResourcesPermission({} as UIPermission); + }; + useEffect(() => { if (isProtectedRoute(location.pathname)) { fetchLoggedInUserPermissions(); } + if (isUndefined(currentUser) || isEmpty(currentUser)) { + resetPermissions(); + } }, [currentUser]); return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.test.tsx new file mode 100644 index 00000000000..9e06299f23c --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.test.tsx @@ -0,0 +1,73 @@ +/* + * Copyright 2023 Collate. + * Licensed 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 { + act, + render, + screen, + waitForElementToBeRemoved, +} from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import AppState from 'AppState'; +import React from 'react'; +import AuthProvider, { useAuthContext } from './AuthProvider'; + +jest.mock('react-router-dom', () => ({ + useHistory: jest.fn().mockReturnValue({ push: jest.fn(), listen: jest.fn() }), + useLocation: jest.fn().mockReturnValue({ pathname: 'pathname' }), +})); + +jest.mock('rest/miscAPI', () => ({ + fetchAuthenticationConfig: jest + .fn() + .mockImplementation(() => Promise.resolve()), + fetchAuthorizerConfig: jest.fn().mockImplementation(() => Promise.resolve()), +})); + +jest.mock('rest/userAPI', () => ({ + getLoggedInUser: jest.fn().mockImplementation(() => Promise.resolve()), + updateUser: jest.fn().mockImplementation(() => Promise.resolve()), +})); + +describe('Test auth provider', () => { + it('Logout handler should call the "updateUserDetails" method', async () => { + const mockUpdateUserDetails = jest.spyOn(AppState, 'updateUserDetails'); + const ConsumerComponent = () => { + const { onLogoutHandler } = useAuthContext(); + + return ( + + ); + }; + + render( + + + + ); + + await waitForElementToBeRemoved(() => screen.getByTestId('loader')); + + const logoutButton = screen.getByTestId('logout-button'); + + expect(logoutButton).toBeInTheDocument(); + + await act(async () => { + userEvent.click(logoutButton); + }); + + expect(mockUpdateUserDetails).toHaveBeenCalled(); + expect(mockUpdateUserDetails).toHaveBeenCalledWith({}); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.tsx index bd6ba42fb6c..ce82a1f8f1c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/authentication/auth-provider/AuthProvider.tsx @@ -16,6 +16,7 @@ import { Auth0Provider } from '@auth0/auth0-react'; import { Configuration } from '@azure/msal-browser'; import { MsalProvider } from '@azure/msal-react'; import { LoginCallback } from '@okta/okta-react'; +import appState from 'AppState'; import { AxiosError } from 'axios'; import { CookieStorage } from 'cookie-storage'; import { AuthorizerConfiguration } from 'generated/configuration/authorizerConfiguration'; @@ -36,7 +37,6 @@ import { useHistory, useLocation } from 'react-router-dom'; import axiosClient from 'rest/index'; import { fetchAuthenticationConfig, fetchAuthorizerConfig } from 'rest/miscAPI'; import { getLoggedInUser, updateUser } from 'rest/userAPI'; -import appState from '../../../AppState'; import { NO_AUTH } from '../../../constants/auth.constants'; import { REDIRECT_PATHNAME, ROUTES } from '../../../constants/constants'; import { ClientErrors } from '../../../enums/axios.enum'; @@ -133,10 +133,13 @@ export const AuthProvider = ({ clearTimeout(timeoutId); authenticatorRef.current?.invokeLogout(); + // reset the user details on logout + appState.updateUserDetails({} as User); + // remove analytics session on logout removeSession(); setLoading(false); - }, [timeoutId]); + }, [timeoutId, appState]); const onRenewIdTokenHandler = () => { return authenticatorRef.current?.renewIdToken();