removed ConfigBasedAutoLogoutProvider
Signed-off-by: Manuel Scurti <manuel.scurti@agilelab.it>
This commit is contained in:
Vendored
-7
@@ -49,13 +49,6 @@ export interface Config {
|
||||
*/
|
||||
useWorkerTimers?: boolean;
|
||||
|
||||
/**
|
||||
* List of events that the autologout event listener will catch to detect if the user is active
|
||||
* These events are standard DOM events.
|
||||
* @visibility frontend
|
||||
*/
|
||||
events?: string[];
|
||||
|
||||
/**
|
||||
* Enable/disable the automatic logout also on users that are logged in but with no Backstage tabs open.
|
||||
* Default is true.
|
||||
|
||||
@@ -110,11 +110,5 @@
|
||||
"dist",
|
||||
"config.d.ts"
|
||||
],
|
||||
"configSchema": "config.d.ts",
|
||||
"jest": {
|
||||
"resetMocks": false,
|
||||
"setupFiles": [
|
||||
"jest-localstorage-mock"
|
||||
]
|
||||
}
|
||||
"configSchema": "config.d.ts"
|
||||
}
|
||||
|
||||
+184
-93
@@ -14,13 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import React, { PropsWithChildren, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
ConfigApi,
|
||||
configApiRef,
|
||||
IdentityApi,
|
||||
identityApiRef,
|
||||
useApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
EventsType,
|
||||
IdleTimerProvider,
|
||||
IIdleTimer,
|
||||
workerTimers,
|
||||
useIdleTimer,
|
||||
} from 'react-idle-timer';
|
||||
|
||||
import {
|
||||
@@ -34,10 +40,10 @@ import { DefaultTimestampStore, TimestampStore } from './timestampStore';
|
||||
export type AutoLogoutTrackableEvent = EventsType;
|
||||
|
||||
/** @public */
|
||||
export type AutoLogoutProviderProps = {
|
||||
export type AutoLogoutProps = {
|
||||
/**
|
||||
* Enable/disable the AutoLogoutMechanism.
|
||||
* defaults to true
|
||||
* defauls to true.
|
||||
*/
|
||||
enabled?: boolean;
|
||||
/**
|
||||
@@ -60,11 +66,6 @@ export type AutoLogoutProviderProps = {
|
||||
* defaults to true.
|
||||
*/
|
||||
useWorkerTimers?: boolean;
|
||||
/**
|
||||
* List of DOM events that the AutoLogoutProvider will track to determine if the user is active or not.
|
||||
* default list includes all the needed events for keyboard/pointers/mouse devices.
|
||||
*/
|
||||
events?: AutoLogoutTrackableEvent[];
|
||||
/**
|
||||
* Enable/disable the autologout for disconnected users.
|
||||
* disconnected users are the ones that are logged in but have no Backstage tab open in their browsers.
|
||||
@@ -74,6 +75,153 @@ export type AutoLogoutProviderProps = {
|
||||
logoutIfDisconnected?: boolean;
|
||||
};
|
||||
|
||||
type AutoLogoutInternalProps = Omit<Required<AutoLogoutProps>, 'enabled'> & {
|
||||
events: AutoLogoutTrackableEvent[];
|
||||
promptOpen: boolean;
|
||||
setPromptOpen: (value: boolean) => void;
|
||||
remainingTimeCountdown: number;
|
||||
setRemainingTimeCountdown: (amount: number) => void;
|
||||
identityApi: IdentityApi;
|
||||
lastSeenOnlineStore: TimestampStore;
|
||||
};
|
||||
|
||||
const ConditionalAutoLogout = ({
|
||||
idleTimeoutMinutes,
|
||||
events,
|
||||
useWorkerTimers,
|
||||
logoutIfDisconnected,
|
||||
promptBeforeIdleSeconds,
|
||||
promptOpen,
|
||||
setPromptOpen,
|
||||
remainingTimeCountdown,
|
||||
setRemainingTimeCountdown,
|
||||
identityApi,
|
||||
lastSeenOnlineStore,
|
||||
}: AutoLogoutInternalProps): JSX.Element => {
|
||||
const promptBeforeIdleMillis = promptBeforeIdleSeconds * 1000;
|
||||
const promptBeforeIdle = promptBeforeIdleMillis > 0 ? true : false;
|
||||
|
||||
const onPrompt = () => {
|
||||
// onPrompt will be called `promptBeforeIdle` milliseconds before `timeout`.
|
||||
// All events are disabled while the prompt is active.
|
||||
// If the user wishes to stay active, call the `activate()` method.
|
||||
// You can get the remaining prompt time with the `getRemainingTime()` method,
|
||||
setPromptOpen(true);
|
||||
setRemainingTimeCountdown(promptBeforeIdleMillis);
|
||||
};
|
||||
|
||||
const onIdle = () => {
|
||||
// onIdle will be called after the timeout is reached.
|
||||
// Events will be rebound as long as `stopOnMount` is not set.
|
||||
setPromptOpen(false);
|
||||
setRemainingTimeCountdown(0);
|
||||
identityApi.signOut();
|
||||
};
|
||||
|
||||
const onActive = () => {
|
||||
// onActive will only be called if `activate()` is called while `isPrompted()`
|
||||
// is true. Here you will also want to close your modal and perform
|
||||
// any active actions.
|
||||
setPromptOpen(false);
|
||||
setRemainingTimeCountdown(0);
|
||||
};
|
||||
|
||||
const onAction = (
|
||||
_event?: Event | undefined,
|
||||
_idleTimer?: IIdleTimer | null,
|
||||
) => {
|
||||
// onAction will be called if any user event is detected. The list of events that triggers a user event detection is the list of configured events
|
||||
// If any user event is detected we update the Last seen online in storage
|
||||
lastSeenOnlineStore.save(new Date());
|
||||
};
|
||||
|
||||
const timer = useIdleTimer({
|
||||
timeout: idleTimeoutMinutes * 60 * 1000,
|
||||
events: events,
|
||||
crossTab: true,
|
||||
name: 'autologout-timer',
|
||||
timers: useWorkerTimers ? workerTimers : undefined,
|
||||
onIdle: onIdle,
|
||||
onActive: promptBeforeIdle ? onActive : undefined,
|
||||
onAction: logoutIfDisconnected ? onAction : undefined,
|
||||
onPrompt: promptBeforeIdle ? onPrompt : undefined,
|
||||
promptBeforeIdle: promptBeforeIdle ? promptBeforeIdleMillis : undefined,
|
||||
syncTimers: 1000,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{promptBeforeIdle && (
|
||||
<StillTherePrompt
|
||||
idleTimer={timer}
|
||||
open={promptOpen}
|
||||
setOpen={setPromptOpen}
|
||||
remainingTime={remainingTimeCountdown}
|
||||
setRemainingTime={setRemainingTimeCountdown}
|
||||
promptTimeoutMillis={promptBeforeIdleMillis}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const defaultConfig: Required<AutoLogoutProps> = {
|
||||
enabled: true,
|
||||
idleTimeoutMinutes: 0.5,
|
||||
promptBeforeIdleSeconds: 10,
|
||||
useWorkerTimers: true,
|
||||
logoutIfDisconnected: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* A list of DOM events that the activity tracker will use to determine if the user is active or not.
|
||||
*/
|
||||
const defaultTrackedEvents: AutoLogoutTrackableEvent[] = [
|
||||
'mousemove',
|
||||
'keydown',
|
||||
'wheel',
|
||||
'DOMMouseScroll',
|
||||
'mousewheel',
|
||||
'mousedown',
|
||||
'touchstart',
|
||||
'touchmove',
|
||||
'MSPointerDown',
|
||||
'MSPointerMove',
|
||||
'visibilitychange',
|
||||
];
|
||||
|
||||
/**
|
||||
* Parses configuration for the AutoLogout. Properties configured in `app-config` take precedence over the props passed to the React component.
|
||||
* If neither props nor config properties are found, a default value will be set accordingly.
|
||||
*/
|
||||
const parseConfig = (
|
||||
configApi: ConfigApi,
|
||||
props: AutoLogoutProps,
|
||||
): Required<AutoLogoutProps> => {
|
||||
return {
|
||||
enabled:
|
||||
configApi.getOptionalBoolean('auth.autologout.enabled') ??
|
||||
props.enabled ??
|
||||
defaultConfig.enabled,
|
||||
idleTimeoutMinutes:
|
||||
configApi.getOptionalNumber('auth.autologout.idleTimeoutMinutes') ??
|
||||
props.idleTimeoutMinutes ??
|
||||
defaultConfig.idleTimeoutMinutes,
|
||||
promptBeforeIdleSeconds:
|
||||
configApi.getOptionalNumber('auth.autologout.promptBeforeIdleSeconds') ??
|
||||
props.promptBeforeIdleSeconds ??
|
||||
defaultConfig.promptBeforeIdleSeconds,
|
||||
useWorkerTimers:
|
||||
configApi.getOptionalBoolean('auth.autologout.useWorkerTimers') ??
|
||||
props.useWorkerTimers ??
|
||||
defaultConfig.useWorkerTimers,
|
||||
logoutIfDisconnected:
|
||||
configApi.getOptionalBoolean('auth.autologout.logoutIfDisconnected') ??
|
||||
props.logoutIfDisconnected ??
|
||||
defaultConfig.logoutIfDisconnected,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* The Autologout feature enables platform engineers to add a mechanism to log out users after a configurable amount of time of inactivity.
|
||||
* When enabled, the mechanism will track user actions (mouse movement, mouse click, key pressing, taps, etc.) in order to determine if they are active or not.
|
||||
@@ -81,28 +229,19 @@ export type AutoLogoutProviderProps = {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const AutoLogoutProvider = ({
|
||||
children,
|
||||
enabled = true,
|
||||
idleTimeoutMinutes = 60,
|
||||
promptBeforeIdleSeconds = 10,
|
||||
useWorkerTimers = true,
|
||||
events = [
|
||||
'mousemove',
|
||||
'keydown',
|
||||
'wheel',
|
||||
'DOMMouseScroll',
|
||||
'mousewheel',
|
||||
'mousedown',
|
||||
'touchstart',
|
||||
'touchmove',
|
||||
'MSPointerDown',
|
||||
'MSPointerMove',
|
||||
'visibilitychange',
|
||||
],
|
||||
logoutIfDisconnected = true,
|
||||
}: PropsWithChildren<AutoLogoutProviderProps>): JSX.Element => {
|
||||
export const AutoLogout = (props: AutoLogoutProps): JSX.Element => {
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const configApi = useApi(configApiRef);
|
||||
|
||||
const {
|
||||
enabled,
|
||||
idleTimeoutMinutes,
|
||||
promptBeforeIdleSeconds,
|
||||
logoutIfDisconnected,
|
||||
useWorkerTimers,
|
||||
}: AutoLogoutProps = useMemo(() => {
|
||||
return parseConfig(configApi, props);
|
||||
}, [configApi, props]);
|
||||
|
||||
useEffect(() => {
|
||||
if (idleTimeoutMinutes < 0.5) {
|
||||
@@ -142,70 +281,22 @@ export const AutoLogoutProvider = ({
|
||||
});
|
||||
|
||||
if (!enabled) {
|
||||
return <>{children}</>;
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const promptBeforeIdleMillis = promptBeforeIdleSeconds * 1000;
|
||||
const promptBeforeIdle = promptBeforeIdleMillis > 0 ? true : false;
|
||||
|
||||
const onPrompt = () => {
|
||||
// onPrompt will be called `promptBeforeIdle` milliseconds before `timeout`.
|
||||
// All events are disabled while the prompt is active.
|
||||
// If the user wishes to stay active, call the `activate()` method.
|
||||
// You can get the remaining prompt time with the `getRemainingTime()` method,
|
||||
setPromptOpen(true);
|
||||
setRemainingTimeCountdown(promptBeforeIdleMillis);
|
||||
};
|
||||
|
||||
const onIdle = () => {
|
||||
// onIdle will be called after the timeout is reached.
|
||||
// Events will be rebound as long as `stopOnMount` is not set.
|
||||
setPromptOpen(false);
|
||||
setRemainingTimeCountdown(0);
|
||||
identityApi.signOut();
|
||||
};
|
||||
|
||||
const onActive = () => {
|
||||
// onActive will only be called if `activate()` is called while `isPrompted()`
|
||||
// is true. Here you will also want to close your modal and perform
|
||||
// any active actions.
|
||||
setPromptOpen(false);
|
||||
setRemainingTimeCountdown(0);
|
||||
};
|
||||
|
||||
const onAction = (
|
||||
_event?: Event | undefined,
|
||||
_idleTimer?: IIdleTimer | null,
|
||||
) => {
|
||||
// onAction will be called if any user event is detected. The list of events that triggers a user event detection is the list of configured events
|
||||
// If any user event is detected we update the Last seen online in storage
|
||||
lastSeenOnlineStore.save(new Date());
|
||||
};
|
||||
|
||||
return (
|
||||
<IdleTimerProvider
|
||||
timeout={idleTimeoutMinutes * 60 * 1000}
|
||||
events={events}
|
||||
crossTab
|
||||
name="autologout-timer"
|
||||
timers={useWorkerTimers ? workerTimers : undefined}
|
||||
onIdle={onIdle}
|
||||
onActive={promptBeforeIdle ? onActive : undefined}
|
||||
onAction={logoutIfDisconnected ? onAction : undefined}
|
||||
onPrompt={promptBeforeIdle ? onPrompt : undefined}
|
||||
promptBeforeIdle={promptBeforeIdle ? promptBeforeIdleMillis : undefined}
|
||||
syncTimers={1000}
|
||||
>
|
||||
{promptBeforeIdle && (
|
||||
<StillTherePrompt
|
||||
open={promptOpen}
|
||||
setOpen={setPromptOpen}
|
||||
remainingTime={remainingTimeCountdown}
|
||||
setRemainingTime={setRemainingTimeCountdown}
|
||||
promptTimeoutMillis={promptBeforeIdleMillis}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</IdleTimerProvider>
|
||||
<ConditionalAutoLogout
|
||||
idleTimeoutMinutes={idleTimeoutMinutes}
|
||||
promptBeforeIdleSeconds={promptBeforeIdleSeconds}
|
||||
useWorkerTimers={useWorkerTimers}
|
||||
events={defaultTrackedEvents}
|
||||
logoutIfDisconnected={logoutIfDisconnected}
|
||||
promptOpen={promptOpen}
|
||||
setPromptOpen={setPromptOpen}
|
||||
remainingTimeCountdown={remainingTimeCountdown}
|
||||
setRemainingTimeCountdown={setRemainingTimeCountdown}
|
||||
identityApi={identityApi}
|
||||
lastSeenOnlineStore={lastSeenOnlineStore}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+7
-22
@@ -18,10 +18,10 @@ import { createMocks } from 'react-idle-timer';
|
||||
import { MessageChannel } from 'worker_threads';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { identityApiRef } from '@backstage/core-plugin-api';
|
||||
import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { TestApiRegistry } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
|
||||
import { AutoLogoutProvider } from './AutoLogoutProvider';
|
||||
import { AutoLogout } from './AutoLogout';
|
||||
import { cleanup, render } from '@testing-library/react';
|
||||
|
||||
// Mock the signOut function of identityApiRef
|
||||
@@ -43,29 +43,15 @@ describe('AutoLogoutProvider', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders children even when enabled is false', async () => {
|
||||
const { queryByText } = await renderWithEffects(
|
||||
<ApiProvider apis={apis}>
|
||||
<AutoLogoutProvider enabled={false}>
|
||||
<div>Test Child</div>
|
||||
</AutoLogoutProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
|
||||
expect(queryByText('Test Child')).not.toBeNull();
|
||||
expect(mockSignOut).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw error if idleTimeoutMinutes is smaller than promptBeforeSeconds', async () => {
|
||||
expect(() =>
|
||||
render(
|
||||
<ApiProvider apis={apis}>
|
||||
<AutoLogoutProvider
|
||||
<AutoLogout
|
||||
enabled
|
||||
idleTimeoutMinutes={0.5}
|
||||
promptBeforeIdleSeconds={120}
|
||||
>
|
||||
<div>Test Child</div>
|
||||
</AutoLogoutProvider>
|
||||
/>
|
||||
</ApiProvider>,
|
||||
),
|
||||
).toThrow();
|
||||
@@ -75,9 +61,8 @@ describe('AutoLogoutProvider', () => {
|
||||
expect(() =>
|
||||
render(
|
||||
<ApiProvider apis={apis}>
|
||||
<AutoLogoutProvider idleTimeoutMinutes={0.49}>
|
||||
<div>Test Child</div>
|
||||
</AutoLogoutProvider>
|
||||
<AutoLogout enabled idleTimeoutMinutes={0.49} />
|
||||
<div>Test Child</div>
|
||||
</ApiProvider>,
|
||||
),
|
||||
).toThrow();
|
||||
+4
-3
@@ -21,9 +21,10 @@ import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useIdleTimerContext } from 'react-idle-timer';
|
||||
import { IIdleTimer } from 'react-idle-timer';
|
||||
|
||||
export interface StillTherePromptProps {
|
||||
idleTimer: IIdleTimer;
|
||||
promptTimeoutMillis: number;
|
||||
remainingTime: number;
|
||||
setRemainingTime: (amount: number) => void;
|
||||
@@ -33,13 +34,13 @@ export interface StillTherePromptProps {
|
||||
|
||||
export const StillTherePrompt = (props: StillTherePromptProps) => {
|
||||
const {
|
||||
idleTimer,
|
||||
setOpen,
|
||||
open,
|
||||
promptTimeoutMillis,
|
||||
remainingTime,
|
||||
setRemainingTime,
|
||||
} = props;
|
||||
const idleTimer = useIdleTimerContext();
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
@@ -63,7 +64,7 @@ export const StillTherePrompt = (props: StillTherePromptProps) => {
|
||||
const seconds = timeTillPrompt > 1 ? 'seconds' : 'second';
|
||||
|
||||
return (
|
||||
<Dialog open={open}>
|
||||
<Dialog open={open} data-testid="inactivity-prompt-dialog">
|
||||
<DialogTitle>Logging out due to inactivity</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
+1
-2
@@ -13,5 +13,4 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export * from './AutoLogoutProvider';
|
||||
export * from './ConfigBasedAutoLogoutProvider';
|
||||
export * from './AutoLogout';
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* 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 { configApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { PropsWithChildren, useMemo } from 'react';
|
||||
import {
|
||||
AutoLogoutProvider,
|
||||
AutoLogoutProviderProps,
|
||||
AutoLogoutTrackableEvent,
|
||||
} from './AutoLogoutProvider';
|
||||
import React from 'react';
|
||||
|
||||
/** @public */
|
||||
export type ConfigBasedAutoLogoutProviderProps = {};
|
||||
|
||||
/**
|
||||
* A config based flavour of the AutoLogoutProvider.
|
||||
* It allows to configure Autologout settings through `app-config`
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const ConfigBasedAutoLogoutProvider = ({
|
||||
children,
|
||||
}: PropsWithChildren<ConfigBasedAutoLogoutProviderProps>): JSX.Element => {
|
||||
const configApi = useApi(configApiRef);
|
||||
|
||||
const props: AutoLogoutProviderProps = useMemo(() => {
|
||||
return {
|
||||
enabled: configApi.getOptionalBoolean('auth.autologout.enabled'),
|
||||
idleTimeoutMinutes: configApi.getOptionalNumber(
|
||||
'auth.autologout.idleTimeoutMinutes',
|
||||
),
|
||||
promptBeforeIdleSeconds: configApi.getOptionalNumber(
|
||||
'auth.autologout.promptBeforeIdleSeconds',
|
||||
),
|
||||
useWorkerTimers: configApi.getOptionalBoolean(
|
||||
'auth.autologout.useWorkerTimers',
|
||||
),
|
||||
events: configApi
|
||||
.getOptionalStringArray('auth.autologout.events')
|
||||
?.map(event => event as AutoLogoutTrackableEvent),
|
||||
logoutIfDisconnected: configApi.getOptionalBoolean(
|
||||
'auth.autologout.logoutIfDisconnected',
|
||||
),
|
||||
};
|
||||
}, [configApi]);
|
||||
|
||||
return (
|
||||
<AutoLogoutProvider {...{ ...props, enabled: props.enabled ?? false }}>
|
||||
{children}
|
||||
</AutoLogoutProvider>
|
||||
);
|
||||
};
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
export * from './AlertDisplay';
|
||||
export * from './AutoLogoutProvider';
|
||||
export * from './AutoLogout';
|
||||
export * from './Avatar';
|
||||
export * from './LinkButton';
|
||||
export * from './CodeSnippet';
|
||||
|
||||
@@ -31,7 +31,7 @@ import { commonProvider } from './commonProvider';
|
||||
import { guestProvider } from './guestProvider';
|
||||
import { customProvider } from './customProvider';
|
||||
import { IdentityApiSignOutProxy } from './IdentityApiSignOutProxy';
|
||||
import { LAST_SEEN_ONLINE_STORAGE_KEY } from '../../components/AutoLogoutProvider/disconnectedUsers';
|
||||
import { LAST_SEEN_ONLINE_STORAGE_KEY } from '../../components/AutoLogout/disconnectedUsers';
|
||||
|
||||
const PROVIDER_STORAGE_KEY = '@backstage/core:SignInPage:provider';
|
||||
|
||||
|
||||
@@ -15,3 +15,4 @@
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
import 'jest-localstorage-mock';
|
||||
|
||||
Reference in New Issue
Block a user