diff --git a/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.test.tsx b/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.test.tsx
index 77fd1536b2..c2b717834c 100644
--- a/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.test.tsx
+++ b/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.test.tsx
@@ -1,5 +1,6 @@
import { render } from 'utils/testRenderer';
import { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import { ProjectsList } from 'component/admin/apiToken/ProjectsList/ProjectsList';
describe('ProjectsList', () => {
@@ -55,4 +56,58 @@ describe('ProjectsList', () => {
expect(container.textContent).toEqual('*');
});
+
+ describe('orphaned tokens', () => {
+ it('should show warning icon if it is an orphaned token', async () => {
+ render(
+ ,
+ );
+
+ const errorIcon = await screen.findByTestId('ErrorIcon');
+ expect(errorIcon).toBeInTheDocument();
+ });
+
+ it('should show tooltip with warning message if it is an orphaned token', async () => {
+ const user = userEvent.setup();
+ render(
+ ,
+ );
+
+ const errorIcon = await screen.findByTestId('ErrorIcon');
+ user.hover(errorIcon);
+
+ const tooltip = await screen.findByRole('tooltip');
+ expect(tooltip).toHaveTextContent(/orphaned token/);
+ });
+
+ it('should not show warning icon if token is in v1 format', async () => {
+ render(
+ ,
+ );
+
+ const errorIcon = await screen.queryByTestId('ErrorIcon');
+ expect(errorIcon).toBeNull();
+ });
+
+ it('should not show warning for wildcard tokens', async () => {
+ render(
+ ,
+ );
+
+ const errorIcon = await screen.queryByTestId('ErrorIcon');
+ expect(errorIcon).toBeNull();
+ });
+ });
});
diff --git a/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.tsx b/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.tsx
index 905fc2b9f0..4be5daae7c 100644
--- a/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.tsx
+++ b/frontend/src/component/admin/apiToken/ProjectsList/ProjectsList.tsx
@@ -1,11 +1,13 @@
+import { Fragment, type FC } from 'react';
import { styled } from '@mui/material';
import { Highlighter } from 'component/common/Highlighter/Highlighter';
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
-import { Fragment, type FC } from 'react';
+import ErrorIcon from '@mui/icons-material/Error';
import { Link } from 'react-router-dom';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
const StyledLink = styled(Link)(({ theme }) => ({
textDecoration: 'none',
@@ -15,12 +17,28 @@ const StyledLink = styled(Link)(({ theme }) => ({
},
}));
+const StyledErrorIcon = styled(ErrorIcon)(({ theme }) => ({
+ color: theme.palette.error.main,
+ marginBottom: theme.spacing(0.5),
+ marginLeft: theme.spacing(0.5),
+}));
+
+const StyledContainer = styled('div')({
+ display: 'flex',
+ alignItems: 'center',
+});
+
interface IProjectsListProps {
project?: string;
projects?: string | string[];
+ secret?: string;
}
-export const ProjectsList: FC = ({ projects, project }) => {
+export const ProjectsList: FC = ({
+ projects,
+ project,
+ secret,
+}) => {
const { searchQuery } = useSearchHighlightContext();
const projectsList =
@@ -67,16 +85,36 @@ export const ProjectsList: FC = ({ projects, project }) => {
return ;
}
+ const tokenFormat = secret?.includes(':') ? 'v2' : 'v1'; // see https://docs.getunleash.io/reference/api-tokens-and-client-keys#format
+ const isWildcardToken = secret?.startsWith('*:');
+ const isOrphanedToken = tokenFormat === 'v2' && !isWildcardToken;
+
return (
+ This is an orphaned token. All of its original
+ projects have been deleted and it now has access to
+ all current and future projects. You should stop
+ using this token and delete it. It will lose access
+ to all projects at a later date.
+ >
+ ) : (
+ 'ALL current and future projects.'
+ )
+ }
placement='bottom'
arrow
>
-
+
*
-
+ }
+ />
+
);
diff --git a/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx b/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx
index 54c09eac94..4012414daf 100644
--- a/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx
+++ b/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx
@@ -61,6 +61,7 @@ export const useApiTokenTable = (
),
width: 160,