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
This commit is contained in:
Sachin Chaurasiya 2023-05-30 18:01:52 +05:30 committed by GitHub
parent be7e6254d9
commit 12a846e35b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 10 deletions

View File

@ -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 = () => {
<div className="content-wrapper" data-testid="content-wrapper">
<Router>
<I18nextProvider i18n={i18n}>
<ErrorBoundry>
<ErrorBoundary>
<ApplicationConfigProvider>
<AuthProvider childComponentType={AppRouter}>
<HelmetProvider>
@ -50,7 +50,7 @@ const App: FunctionComponent = () => {
</HelmetProvider>
</AuthProvider>
</ApplicationConfigProvider>
</ErrorBoundry>
</ErrorBoundary>
</I18nextProvider>
</Router>
<ToastContainer {...TOAST_OPTIONS} newestOnTop />

View File

@ -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<Props> = ({ children }) => {
const ErrorBoundary: React.FC<Props> = ({ children }) => {
const history = useHistory();
const onErrorReset = () => {
@ -29,10 +29,12 @@ const ErrorBoundry: React.FC<Props> = ({ children }) => {
};
return (
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={onErrorReset}>
<ErrorBoundaryWrapper
FallbackComponent={ErrorFallback}
onReset={onErrorReset}>
{children}
</ErrorBoundary>
</ErrorBoundaryWrapper>
);
};
export default ErrorBoundry;
export default ErrorBoundary;

View File

@ -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<PermissionProviderProps> = ({ 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 (

View File

@ -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 (
<button data-testid="logout-button" onClick={onLogoutHandler}>
Logout
</button>
);
};
render(
<AuthProvider childComponentType={ConsumerComponent}>
<ConsumerComponent />
</AuthProvider>
);
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({});
});
});

View File

@ -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();