From b01118da0835ed8fe6bdece59de09be70e0c6303 Mon Sep 17 00:00:00 2001 From: Ying Date: Thu, 22 Jan 2026 12:35:34 -0500 Subject: [PATCH] Checking agent builder privilege --- .../components/entity_highlights.test.tsx | 75 +++++++++++++++++-- .../components/entity_highlights.tsx | 26 +++++-- 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/entity_highlights.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/entity_highlights.test.tsx index ce8b152d80b44..41042c3b1a670 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/entity_highlights.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/entity_highlights.test.tsx @@ -18,6 +18,7 @@ const mockUseLoadConnectors = jest.fn(); const mockUseSpaceId = jest.fn(); const mockUseStoredAssistantConnectorId = jest.fn(); const mockUseAssistantAvailability = jest.fn(); +const mockUseAgentBuilderAvailability = jest.fn(); const mockUseFetchEntityDetailsHighlights = jest.fn(); const mockUseHasEntityHighlightsLicense = jest.fn(); @@ -42,6 +43,10 @@ jest.mock('../../../../assistant/use_assistant_availability', () => ({ useAssistantAvailability: () => mockUseAssistantAvailability(), })); +jest.mock('../../../../agent_builder/hooks/use_agent_builder_availability', () => ({ + useAgentBuilderAvailability: () => mockUseAgentBuilderAvailability(), +})); + jest.mock('../../../../onboarding/components/hooks/use_stored_state', () => ({ useStoredAssistantConnectorId: () => mockUseStoredAssistantConnectorId(), })); @@ -112,9 +117,12 @@ describe('EntityHighlights', () => { const defaultStoredAssistantConnectorId = ['connector-1', jest.fn()]; const defaultAssistantAvailability = { hasAssistantPrivilege: true, - isAssistantEnabled: true, + hasConnectorsReadPrivilege: true, isAssistantVisible: true, }; + const defaultAgentBuilderAvailability = { + hasAgentBuilderPrivilege: true, + }; const defaultFetchEntityDetailsHighlights = { fetchEntityHighlights: mockFetchEntityHighlights, isChatLoading: false, @@ -132,6 +140,7 @@ describe('EntityHighlights', () => { mockUseSpaceId.mockReturnValue(defaultSpaceId); mockUseStoredAssistantConnectorId.mockReturnValue(defaultStoredAssistantConnectorId); mockUseAssistantAvailability.mockReturnValue(defaultAssistantAvailability); + mockUseAgentBuilderAvailability.mockReturnValue(defaultAgentBuilderAvailability); mockUseFetchEntityDetailsHighlights.mockReturnValue(defaultFetchEntityDetailsHighlights); mockUseHasEntityHighlightsLicense.mockReturnValue(true); }); @@ -145,11 +154,24 @@ describe('EntityHighlights', () => { expect(screen.getByTestId('asset-criticality-selector')).toBeInTheDocument(); }); - it('returns null when assistant is disabled due to no privileges', () => { - mockUseAssistantAvailability.mockReturnValue({ + it('returns null when user has insufficent license', () => { + mockUseHasEntityHighlightsLicense.mockReturnValueOnce(false); + + render(, { + wrapper: TestProviders, + }); + + expect(screen.queryByText('Entity summary')).not.toBeInTheDocument(); + }); + + it('returns null when user has no assistant privilege or agent builder privilege', () => { + mockUseAssistantAvailability.mockReturnValueOnce({ hasAssistantPrivilege: false, - isAssistantEnabled: true, - isAssistantVisible: true, + hasConnectorsReadPrivilege: true, + isAssistantVisible: false, + }); + mockUseAgentBuilderAvailability.mockReturnValueOnce({ + hasAgentBuilderPrivilege: false, }); render(, { @@ -159,11 +181,14 @@ describe('EntityHighlights', () => { expect(screen.queryByText('Entity summary')).not.toBeInTheDocument(); }); - it('returns null when assistant is not enabled', () => { - mockUseAssistantAvailability.mockReturnValue({ + it('returns null when user has no connector read privilege', () => { + mockUseAssistantAvailability.mockReturnValueOnce({ hasAssistantPrivilege: true, - isAssistantEnabled: false, isAssistantVisible: true, + hasConnectorsReadPrivilege: false, + }); + mockUseAgentBuilderAvailability.mockReturnValueOnce({ + hasAgentBuilderPrivilege: true, }); render(, { @@ -173,6 +198,40 @@ describe('EntityHighlights', () => { expect(screen.queryByText('Entity summary')).not.toBeInTheDocument(); }); + it('renders if user has assistant privilege and no agent builder privilege', () => { + mockUseAssistantAvailability.mockReturnValueOnce({ + hasAssistantPrivilege: true, + isAssistantVisible: true, + hasConnectorsReadPrivilege: true, + }); + mockUseAgentBuilderAvailability.mockReturnValueOnce({ + hasAgentBuilderPrivilege: false, + }); + + render(, { + wrapper: TestProviders, + }); + + expect(screen.getByText('Entity summary')).toBeInTheDocument(); + }); + + it('renders if user has agent builder privilege and no assistant privilege', () => { + mockUseAssistantAvailability.mockReturnValueOnce({ + hasAssistantPrivilege: false, + isAssistantVisible: false, + hasConnectorsReadPrivilege: true, + }); + mockUseAgentBuilderAvailability.mockReturnValueOnce({ + hasAgentBuilderPrivilege: true, + }); + + render(, { + wrapper: TestProviders, + }); + + expect(screen.getByText('Entity summary')).toBeInTheDocument(); + }); + it('shows generate button when no assistant result and not loading', () => { render(, { wrapper: TestProviders, diff --git a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/entity_highlights.tsx b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/entity_highlights.tsx index 0adb510474573..9711ff8af2642 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/entity_highlights.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/entity_highlights.tsx @@ -28,6 +28,7 @@ import { import React, { useCallback, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability'; +import { useAgentBuilderAvailability } from '../../../../agent_builder/hooks/use_agent_builder_availability'; import type { EntityType } from '../../../../../common/search_strategy'; import { useStoredAssistantConnectorId } from '../../../../onboarding/components/hooks/use_stored_state'; import { useSpaceId } from '../../../../common/hooks/use_space_id'; @@ -56,8 +57,9 @@ export const EntityHighlightsAccordion: React.FC<{ if (!aiConnectors || !connectorId) return ''; return aiConnectors.find((c) => c.id === connectorId)?.name ?? ''; }, [aiConnectors, connectorId]); - const { hasAssistantPrivilege, isAssistantEnabled, isAssistantVisible } = + const { hasConnectorsReadPrivilege, hasAssistantPrivilege, isAssistantVisible } = useAssistantAvailability(); + const { hasAgentBuilderPrivilege } = useAgentBuilderAvailability(); const hasEntityHighlightsLicense = useHasEntityHighlightsLicense(); const { gradientPanelStyle, @@ -96,10 +98,24 @@ export const EntityHighlightsAccordion: React.FC<{ setPopover(false); }, []); - const disabled = useMemo( - () => !hasAssistantPrivilege || !isAssistantEnabled || !hasEntityHighlightsLicense, - [hasAssistantPrivilege, isAssistantEnabled, hasEntityHighlightsLicense] - ); + const disabled = useMemo(() => { + if (!hasEntityHighlightsLicense) { + return true; + } + + // if user does not have access to connectors, we cannot invoke the inference action + if (!hasConnectorsReadPrivilege) { + return true; + } + + // if user does not have access to assistant or agent builder, disable entity highlights + return !(hasAssistantPrivilege || hasAgentBuilderPrivilege); + }, [ + hasConnectorsReadPrivilege, + hasAgentBuilderPrivilege, + hasAssistantPrivilege, + hasEntityHighlightsLicense, + ]); const isLoading = useMemo( () => isChatLoading || isAnonymizationFieldsLoading,