Skip to content

Commit

Permalink
BED-4868 Wrong Reader Count for AZKeyVaults (#1017)
Browse files Browse the repository at this point in the history
* Added the 'countLabel' field and added a field to the data returned by the endpoint, which EntityInfoDataTable can look at for help getting the correct count for top level Vault Readers.

* Add logic to correctly count the AZKeyVaults.

* Added a test for BED-4868.

* Check in whitespace and header fixes from just prepare-for-codereview
  • Loading branch information
AD7ZJ authored Jan 6, 2025
1 parent 2d7bf4b commit 323562c
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 20 deletions.
111 changes: 101 additions & 10 deletions cmd/ui/src/views/Explore/EntityInfo/EntityInfoDataTable.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import { render, screen } from 'src/test-utils';
import userEvent from '@testing-library/user-event';
import { rest, RequestHandler } from 'msw';
import { setupServer } from 'msw/node';
import { ActiveDirectoryNodeKind, allSections } from 'bh-shared-ui';
import { ActiveDirectoryNodeKind, allSections, AzureNodeKind } from 'bh-shared-ui';
import EntityInfoDataTable from './EntityInfoDataTable';
import { EntityInfoPanelContextProvider } from './EntityInfoPanelContextProvider';

const objectId = 'fake-object-id';
const sections = allSections[ActiveDirectoryNodeKind.GPO]!(objectId);
const adGpoSections = allSections[ActiveDirectoryNodeKind.GPO]!(objectId);
const azKeyVaultSections = allSections[AzureNodeKind.KeyVault]!(objectId);

const queryCount = {
controllers: {
Expand Down Expand Up @@ -58,25 +59,70 @@ const queryCount = {
},
} as const;

const keyVaultTest = {
KeyReaders: {
count: 0,
limit: 128,
skip: 0,
data: [],
},
CertificateReaders: {
count: 8,
limit: 128,
skip: 0,
data: [],
},
SecretReaders: {
count: 3003,
limit: 128,
skip: 0,
data: [],
},
AllReaders: {
count: 1998,
limit: 128,
skip: 0,
data: [],
},
} as const;

const handlers: Array<RequestHandler> = [
rest.get(`api/v2/gpos/${objectId}/:asset`, (req, res, ctx) => {
const asset = req.params.asset as keyof typeof queryCount;
return res(ctx.json(queryCount[asset]));
}),

rest.get(`api/v2/azure/key-vaults*`, (req, res, ctx) => {
if (req.url.searchParams.get('related_entity_type') === 'all-readers') {
return res(ctx.json(keyVaultTest.AllReaders));
}

if (req.url.searchParams.get('related_entity_type') === 'key-readers') {
return res(ctx.json(keyVaultTest.KeyReaders));
}

if (req.url.searchParams.get('related_entity_type') === 'secret-readers') {
return res(ctx.json(keyVaultTest.SecretReaders));
}

if (req.url.searchParams.get('related_entity_type') === 'certificate-readers') {
return res(ctx.json(keyVaultTest.CertificateReaders));
}
}),
];

const server = setupServer(...handlers);

describe('EntityInfoDataTable', () => {
describe('Node count', () => {
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe('EntityInfoDataTable', () => {
describe('Node count for nested table that counts all sections', () => {
it('sums nested section node counts', async () => {
render(
<EntityInfoPanelContextProvider>
<EntityInfoDataTable {...sections[0]} />
<EntityInfoDataTable {...adGpoSections[0]} />
</EntityInfoPanelContextProvider>
);
const sum = await screen.findByText('5,064');
Expand All @@ -93,7 +139,7 @@ describe('EntityInfoDataTable', () => {

render(
<EntityInfoPanelContextProvider>
<EntityInfoDataTable {...sections[0]} />
<EntityInfoDataTable {...adGpoSections[0]} />
</EntityInfoPanelContextProvider>
);

Expand All @@ -113,7 +159,7 @@ describe('EntityInfoDataTable', () => {
const user = userEvent.setup();
render(
<EntityInfoPanelContextProvider>
<EntityInfoDataTable {...sections[0]} />
<EntityInfoDataTable {...adGpoSections[0]} />
</EntityInfoPanelContextProvider>
);

Expand All @@ -127,4 +173,49 @@ describe('EntityInfoDataTable', () => {
expect(zero.textContent).toBe('0');
});
});

describe('Node count for Vault Readers nested table', () => {
it('Verify Vault Reader count is the count returned by All Readers', async () => {
const user = userEvent.setup();

render(
<EntityInfoPanelContextProvider>
<EntityInfoDataTable {...azKeyVaultSections[0]} />
</EntityInfoPanelContextProvider>
);

// wait for the total count to be available - this holds off all following tests until the counts have been returned
const sum = await screen.findByText('1,998');
expect(sum).not.toBeNull();

// verify the vault reader count is as expected
const vaultReadersHeader = await screen.findByText('Vault Readers');
const vaultReadersCount = vaultReadersHeader.nextElementSibling;
expect(vaultReadersCount).toHaveTextContent('1,998');

// expand the Vault Readers accordian
const button = await screen.findByRole('button');
await user.click(button);

// verify the key readers count is as expected
const keyReadersHeader = await screen.findByText('Key Readers');
const keyReadersCount = keyReadersHeader.nextElementSibling;
expect(keyReadersCount).toHaveTextContent('0');

// verify the certificate readers count is as expected
const certReadersHeader = await screen.findByText('Certificate Readers');
const certReadersCount = certReadersHeader.nextElementSibling;
expect(certReadersCount).toHaveTextContent('8');

// verify the secret readers count is as expected
const secretReadersHeader = await screen.findByText('Secret Readers');
const secretReadersCount = secretReadersHeader.nextElementSibling;
expect(secretReadersCount).toHaveTextContent('3,003');

// verify the all readers count is as expected
const allReadersHeader = await screen.findByText('All Readers');
const allReadersCount = allReadersHeader.nextElementSibling;
expect(allReadersCount).toHaveTextContent('1,998');
});
});
});
18 changes: 12 additions & 6 deletions cmd/ui/src/views/Explore/EntityInfo/EntityInfoDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { addSnackbar } from 'src/ducks/global/actions';
import { transformFlatGraphResponse } from 'src/utils';
import EntityInfoCollapsibleSection from './EntityInfoCollapsibleSection';

const EntityInfoDataTable: React.FC<EntityInfoDataTableProps> = ({ id, label, endpoint, sections }) => {
const EntityInfoDataTable: React.FC<EntityInfoDataTableProps> = ({ id, label, endpoint, countLabel, sections }) => {
const dispatch = useDispatch();

const countQuery = useQuery(
Expand Down Expand Up @@ -83,12 +83,18 @@ const EntityInfoDataTable: React.FC<EntityInfoDataTableProps> = ({ id, label, en

let count: number | undefined;
if (Array.isArray(countQuery.data)) {
count = countQuery.data.reduce((acc, val) => {
const count = val.count ?? 0;
return acc + count;
}, 0);
if (countLabel !== undefined) {
countQuery.data.forEach((sectionData: any) => {
if (sectionData.countLabel === countLabel) count = sectionData.count;
});
} else {
count = countQuery.data.reduce((acc, val) => {
const count = val?.count ?? 0;
return acc + count;
}, 0);
}
} else if (countQuery.data) {
count = countQuery.data.count ?? 0;
count = countQuery.data?.count ?? 0;
}

return (
Expand Down
22 changes: 18 additions & 4 deletions packages/javascript/bh-shared-ui/src/utils/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface EntityInfoDataTableProps {
id: string;
label: string;
endpoint?: ({ counts, skip, limit, type }: EntitySectionEndpointParams) => Promise<any>;
countLabel?: string;
sections?: EntityInfoDataTableProps[];
}

Expand Down Expand Up @@ -286,6 +287,7 @@ export const allSections: Partial<Record<EntityKinds, (id: string) => EntityInfo
{
id,
label: 'Vault Readers',
countLabel: 'All Readers',
sections: [
{
id,
Expand All @@ -295,7 +297,10 @@ export const allSections: Partial<Record<EntityKinds, (id: string) => EntityInfo
.getAZEntityInfoV2('key-vaults', id, 'key-readers', counts, skip, limit, type, {
signal: controller.signal,
})
.then((res) => res.data),
.then((res) => {
if (type !== 'graph') res.data.countLabel = 'Key Readers';
return res.data;
}),
},
{
id,
Expand All @@ -305,7 +310,10 @@ export const allSections: Partial<Record<EntityKinds, (id: string) => EntityInfo
.getAZEntityInfoV2('key-vaults', id, 'certificate-readers', counts, skip, limit, type, {
signal: controller.signal,
})
.then((res) => res.data),
.then((res) => {
if (type !== 'graph') res.data.countLabel = 'Certificate Readers';
return res.data;
}),
},
{
id,
Expand All @@ -315,7 +323,10 @@ export const allSections: Partial<Record<EntityKinds, (id: string) => EntityInfo
.getAZEntityInfoV2('key-vaults', id, 'secret-readers', counts, skip, limit, type, {
signal: controller.signal,
})
.then((res) => res.data),
.then((res) => {
if (type !== 'graph') res.data.countLabel = 'Secret Readers';
return res.data;
}),
},
{
id,
Expand All @@ -325,7 +336,10 @@ export const allSections: Partial<Record<EntityKinds, (id: string) => EntityInfo
.getAZEntityInfoV2('key-vaults', id, 'all-readers', counts, skip, limit, type, {
signal: controller.signal,
})
.then((res) => res.data),
.then((res) => {
if (type !== 'graph') res.data.countLabel = 'All Readers';
return res.data;
}),
},
],
},
Expand Down

0 comments on commit 323562c

Please sign in to comment.