diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.ts b/src/core/server/saved_objects/migrations/core/elastic_index.ts index 8bda77563be8c..f473b3ed02526 100644 --- a/src/core/server/saved_objects/migrations/core/elastic_index.ts +++ b/src/core/server/saved_objects/migrations/core/elastic_index.ts @@ -43,6 +43,8 @@ export const REMOVED_TYPES: string[] = [ 'server', // https://github.com/elastic/kibana/issues/95617 'tsvb-validation-telemetry', + // replaced by osquery-manager-usage-metric + 'osquery-usage-metric', ].sort(); // When migrating from the outdated index we use a read query which excludes diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/type_registrations.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/type_registrations.test.ts index 0bad209ad9cef..ce4c8078e0c95 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/type_registrations.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/type_registrations.test.ts @@ -72,6 +72,7 @@ const previouslyRegisteredTypes = [ 'monitoring-telemetry', 'osquery-saved-query', 'osquery-usage-metric', + 'osquery-manager-usage-metric', 'query', 'sample-data-telemetry', 'search', diff --git a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts index 6a9a4cd9ba83c..716c81573e85a 100644 --- a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts +++ b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts @@ -44,7 +44,7 @@ export const getAgentUsage = async ( error, offline, updating, - } = await AgentService.getAgentStatusForAgentPolicy(soClient, esClient); + } = await AgentService.getAgentStatusForAgentPolicy(esClient); return { total_enrolled: total, healthy: online, diff --git a/x-pack/plugins/fleet/server/collectors/fleet_server_collector.ts b/x-pack/plugins/fleet/server/collectors/fleet_server_collector.ts index 9616ba11545e0..47440e791747c 100644 --- a/x-pack/plugins/fleet/server/collectors/fleet_server_collector.ts +++ b/x-pack/plugins/fleet/server/collectors/fleet_server_collector.ts @@ -76,7 +76,6 @@ export const getFleetServerUsage = async ( } const { total, inactive, online, error, updating, offline } = await getAgentStatusForAgentPolicy( - soClient, esClient, undefined, Array.from(policyIds) diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index c4ba7e363bc5a..9f07dfac9670b 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -101,6 +101,7 @@ export const createMockAgentPolicyService = (): jest.Mocked => { return { getAgentStatusById: jest.fn(), + getAgentStatusForAgentPolicy: jest.fn(), authenticateAgentWithAccessToken: jest.fn(), getAgent: jest.fn(), listAgents: jest.fn(), diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 5f7cd27d9fc34..9991f4ee20980 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -72,6 +72,7 @@ import { } from './services'; import { getAgentStatusById, + getAgentStatusForAgentPolicy, authenticateAgentWithAccessToken, getAgentsByKuery, getAgentById, @@ -286,6 +287,7 @@ export class FleetPlugin getAgent: getAgentById, listAgents: getAgentsByKuery, getAgentStatusById, + getAgentStatusForAgentPolicy, authenticateAgentWithAccessToken, }, agentPolicyService: { diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index 72a7f4e35ddf5..fd4721309eebb 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -202,13 +202,11 @@ export const getAgentStatusForAgentPolicyHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const soClient = context.core.savedObjects.client; const esClient = context.core.elasticsearch.client.asCurrentUser; try { // TODO change path const results = await AgentService.getAgentStatusForAgentPolicy( - soClient, esClient, request.query.policyId, request.query.kuery diff --git a/x-pack/plugins/fleet/server/services/agents/status.ts b/x-pack/plugins/fleet/server/services/agents/status.ts index 26cca630f9581..cd8f9b95599b8 100644 --- a/x-pack/plugins/fleet/server/services/agents/status.ts +++ b/x-pack/plugins/fleet/server/services/agents/status.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; +import type { ElasticsearchClient } from 'src/core/server'; import pMap from 'p-map'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; @@ -49,7 +49,6 @@ function joinKuerys(...kuerys: Array) { } export async function getAgentStatusForAgentPolicy( - soClient: SavedObjectsClientContract, esClient: ElasticsearchClient, agentPolicyId?: string, filterKuery?: string diff --git a/x-pack/plugins/fleet/server/services/index.ts b/x-pack/plugins/fleet/server/services/index.ts index f4355320c5a6a..ecef04af6b11e 100644 --- a/x-pack/plugins/fleet/server/services/index.ts +++ b/x-pack/plugins/fleet/server/services/index.ts @@ -10,6 +10,8 @@ import type { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/ser import type { AgentStatus, Agent } from '../types'; +import type { GetAgentStatusResponse } from '../../common'; + import type { getAgentById, getAgentsByKuery } from './agents'; import type { agentPolicyService } from './agent_policy'; import * as settingsService from './settings'; @@ -56,6 +58,14 @@ export interface AgentService { * Return the status by the Agent's id */ getAgentStatusById(esClient: ElasticsearchClient, agentId: string): Promise; + /** + * Return the status by the Agent's Policy id + */ + getAgentStatusForAgentPolicy( + esClient: ElasticsearchClient, + agentPolicyId?: string, + filterKuery?: string + ): Promise; /** * List agents */ diff --git a/x-pack/plugins/osquery/common/types.ts b/x-pack/plugins/osquery/common/types.ts index d195198e54a73..7244066f798ba 100644 --- a/x-pack/plugins/osquery/common/types.ts +++ b/x-pack/plugins/osquery/common/types.ts @@ -9,8 +9,11 @@ import { PackagePolicy, PackagePolicyInput, PackagePolicyInputStream } from '../ export const savedQuerySavedObjectType = 'osquery-saved-query'; export const packSavedObjectType = 'osquery-pack'; -export const usageMetricSavedObjectType = 'osquery-usage-metric'; -export type SavedObjectType = 'osquery-saved-query' | 'osquery-pack' | 'osquery-usage-metric'; +export const usageMetricSavedObjectType = 'osquery-manager-usage-metric'; +export type SavedObjectType = + | 'osquery-saved-query' + | 'osquery-pack' + | 'osquery-manager-usage-metric'; /** * This makes any optional property the same as Required would but also has the diff --git a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx index 75277059bbf97..083d0193be2a2 100644 --- a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx +++ b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx @@ -15,6 +15,7 @@ import { AgentIdToName } from '../agents/agent_id_to_name'; import { useActionResults } from './use_action_results'; import { useAllResults } from '../results/use_all_results'; import { Direction } from '../../common/search_strategy'; +import { useActionResultsPrivileges } from './use_action_privileges'; interface ActionResultsSummaryProps { actionId: string; @@ -41,6 +42,7 @@ const ActionResultsSummaryComponent: React.FC = ({ expirationDate, ]); const [isLive, setIsLive] = useState(true); + const { data: hasActionResultsPrivileges } = useActionResultsPrivileges(); const { // @ts-expect-error update types data: { aggregations, edges }, @@ -52,6 +54,7 @@ const ActionResultsSummaryComponent: React.FC = ({ direction: Direction.asc, sortField: '@timestamp', isLive, + skip: !hasActionResultsPrivileges, }); if (expired) { // @ts-expect-error update types @@ -77,6 +80,7 @@ const ActionResultsSummaryComponent: React.FC = ({ }, ], isLive, + skip: !hasActionResultsPrivileges, }); const renderAgentIdColumn = useCallback((agentId) => , []); diff --git a/x-pack/plugins/osquery/public/action_results/use_action_privileges.tsx b/x-pack/plugins/osquery/public/action_results/use_action_privileges.tsx new file mode 100644 index 0000000000000..2c80c874e89fa --- /dev/null +++ b/x-pack/plugins/osquery/public/action_results/use_action_privileges.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useQuery } from 'react-query'; + +import { i18n } from '@kbn/i18n'; +import { useKibana } from '../common/lib/kibana'; +import { useErrorToast } from '../common/hooks/use_error_toast'; + +export const useActionResultsPrivileges = () => { + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); + + return useQuery( + ['actionResultsPrivileges'], + () => http.get('/internal/osquery/privileges_check'), + { + keepPreviousData: true, + select: (response) => response?.has_all_requested ?? false, + onSuccess: () => setErrorToast(), + onError: (error: Error) => + setErrorToast(error, { + title: i18n.translate('xpack.osquery.action_results_privileges.fetchError', { + defaultMessage: 'Error while fetching action results privileges', + }), + }), + } + ); +}; diff --git a/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts b/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts index c51f2d2f44a5c..74061915d3b86 100644 --- a/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts +++ b/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts @@ -9,11 +9,7 @@ import { useQuery } from 'react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; -import { - agentPolicyRouteService, - GetAgentPoliciesResponse, - GetAgentPoliciesResponseItem, -} from '../../../fleet/common'; +import { GetAgentPoliciesResponse, GetAgentPoliciesResponseItem } from '../../../fleet/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; export const useAgentPolicies = () => { @@ -22,12 +18,7 @@ export const useAgentPolicies = () => { return useQuery( ['agentPolicies'], - () => - http.get(agentPolicyRouteService.getListPath(), { - query: { - perPage: 100, - }, - }), + () => http.get('/internal/osquery/fleet_wrapper/agent_policies/'), { initialData: { items: [], total: 0, page: 1, perPage: 100 }, keepPreviousData: true, diff --git a/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts b/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts index dcebf136b6773..302567ef25640 100644 --- a/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts +++ b/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts @@ -9,7 +9,6 @@ import { useQuery } from 'react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; -import { agentPolicyRouteService } from '../../../fleet/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; interface UseAgentPolicy { @@ -23,7 +22,7 @@ export const useAgentPolicy = ({ policyId, skip }: UseAgentPolicy) => { return useQuery( ['agentPolicy', { policyId }], - () => http.get(agentPolicyRouteService.getInfoPath(policyId)), + () => http.get(`/internal/osquery/fleet_wrapper/agent_policies/${policyId}`), { enabled: !skip, keepPreviousData: true, diff --git a/x-pack/plugins/osquery/public/agents/agents_table.tsx b/x-pack/plugins/osquery/public/agents/agents_table.tsx index 53e2ce1d53420..8a40cb171070d 100644 --- a/x-pack/plugins/osquery/public/agents/agents_table.tsx +++ b/x-pack/plugins/osquery/public/agents/agents_table.tsx @@ -65,9 +65,13 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh osqueryPolicyData ); const grouper = useMemo(() => new AgentGrouper(), []); - const { agentsLoading, agents } = useAllAgents(osqueryPolicyData, debouncedSearchValue, { - perPage, - }); + const { isLoading: agentsLoading, data: agents } = useAllAgents( + osqueryPolicyData, + debouncedSearchValue, + { + perPage, + } + ); // option related const [options, setOptions] = useState([]); @@ -108,8 +112,8 @@ const AgentsTableComponent: React.FC = ({ agentSelection, onCh grouper.setTotalAgents(totalNumAgents); grouper.updateGroup(AGENT_GROUP_KEY.Platform, groups.platforms); grouper.updateGroup(AGENT_GROUP_KEY.Policy, groups.policies); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - grouper.updateGroup(AGENT_GROUP_KEY.Agent, agents!); + // @ts-expect-error update types + grouper.updateGroup(AGENT_GROUP_KEY.Agent, agents); const newOptions = grouper.generateOptions(); setOptions(newOptions); }, [groups.platforms, groups.policies, totalNumAgents, groupsLoading, agents, grouper]); diff --git a/x-pack/plugins/osquery/public/agents/use_agent_details.ts b/x-pack/plugins/osquery/public/agents/use_agent_details.ts index 1a0663812dec3..b0c2fb2e1cbaf 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_details.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_details.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from 'react-query'; -import { GetOneAgentResponse, agentRouteService } from '../../../fleet/common'; +import { GetOneAgentResponse } from '../../../fleet/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; @@ -21,7 +21,7 @@ export const useAgentDetails = ({ agentId }: UseAgentDetails) => { const setErrorToast = useErrorToast(); return useQuery( ['agentDetails', agentId], - () => http.get(agentRouteService.getInfoPath(agentId)), + () => http.get(`/internal/osquery/fleet_wrapper/agents/${agentId}`), { enabled: agentId.length > 0, onSuccess: () => setErrorToast(), diff --git a/x-pack/plugins/osquery/public/agents/use_agent_policies.ts b/x-pack/plugins/osquery/public/agents/use_agent_policies.ts index 115b5af9d3a1b..e8d6fe7eb97ac 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_policies.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_policies.ts @@ -9,7 +9,7 @@ import { mapKeys } from 'lodash'; import { useQueries, UseQueryResult } from 'react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; -import { agentPolicyRouteService, GetOneAgentPolicyResponse } from '../../../fleet/common'; +import { GetOneAgentPolicyResponse } from '../../../fleet/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; export const useAgentPolicies = (policyIds: string[] = []) => { @@ -19,7 +19,7 @@ export const useAgentPolicies = (policyIds: string[] = []) => { const agentResponse = useQueries( policyIds.map((policyId) => ({ queryKey: ['agentPolicy', policyId], - queryFn: () => http.get(agentPolicyRouteService.getInfoPath(policyId)), + queryFn: () => http.get(`/internal/osquery/fleet_wrapper/agent_policies/${policyId}`), enabled: policyIds.length > 0, onSuccess: () => setErrorToast(), onError: (error) => diff --git a/x-pack/plugins/osquery/public/agents/use_agent_status.ts b/x-pack/plugins/osquery/public/agents/use_agent_status.ts index c8bc8d2fe5c0e..ba2237dbe57ea 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_status.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_status.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from 'react-query'; -import { GetAgentStatusResponse, agentRouteService } from '../../../fleet/common'; +import { GetAgentStatusResponse } from '../../../fleet/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; @@ -25,7 +25,7 @@ export const useAgentStatus = ({ policyId, skip }: UseAgentStatus) => { ['agentStatus', policyId], () => http.get( - agentRouteService.getStatusPath(), + `/internal/osquery/fleet_wrapper/agent-status`, policyId ? { query: { diff --git a/x-pack/plugins/osquery/public/agents/use_all_agents.ts b/x-pack/plugins/osquery/public/agents/use_all_agents.ts index fac43eaa7ffc3..42e4954989c66 100644 --- a/x-pack/plugins/osquery/public/agents/use_all_agents.ts +++ b/x-pack/plugins/osquery/public/agents/use_all_agents.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from 'react-query'; -import { GetAgentsResponse, agentRouteService } from '../../../fleet/common'; +import { GetAgentsResponse } from '../../../fleet/common'; import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; @@ -31,7 +31,8 @@ export const useAllAgents = ( const { perPage } = opts; const { http } = useKibana().services; const setErrorToast = useErrorToast(); - const { isLoading: agentsLoading, data: agentData } = useQuery( + + return useQuery( ['agents', osqueryPolicies, searchValue, perPage], () => { let kuery = `${osqueryPolicies.map((p) => `policy_id:${p}`).join(' or ')}`; @@ -40,7 +41,7 @@ export const useAllAgents = ( kuery += ` and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`; } - return http.get(agentRouteService.getListPath(), { + return http.get(`/internal/osquery/fleet_wrapper/agents`, { query: { kuery, perPage, @@ -48,6 +49,8 @@ export const useAllAgents = ( }); }, { + // @ts-expect-error update types + select: (data) => data?.agents || [], enabled: !osqueryPoliciesLoading && osqueryPolicies.length > 0, onSuccess: () => setErrorToast(), onError: (error) => @@ -58,6 +61,4 @@ export const useAllAgents = ( }), } ); - - return { agentsLoading, agents: agentData?.list }; }; diff --git a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts index 9064dac1ae5d0..4b9ff931f3a91 100644 --- a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts +++ b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts @@ -10,8 +10,6 @@ import { useQuery } from 'react-query'; import { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; -import { packagePolicyRouteService, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../fleet/common'; -import { OSQUERY_INTEGRATION_NAME } from '../../common'; import { useErrorToast } from '../common/hooks/use_error_toast'; export const useOsqueryPolicies = () => { @@ -20,12 +18,7 @@ export const useOsqueryPolicies = () => { const { isLoading: osqueryPoliciesLoading, data: osqueryPolicies = [] } = useQuery( ['osqueryPolicies'], - () => - http.get(packagePolicyRouteService.getListPath(), { - query: { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`, - }, - }), + () => http.get('/internal/osquery/fleet_wrapper/package_policies'), { select: (response) => uniq(response.items.map((p: { policy_id: string }) => p.policy_id)), diff --git a/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx b/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx index 236fdb1af1815..58f9f8dbec61d 100644 --- a/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx +++ b/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx @@ -6,11 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { find } from 'lodash/fp'; import { useQuery } from 'react-query'; -import { GetPackagesResponse, epmRouteService } from '../../../../fleet/common'; -import { OSQUERY_INTEGRATION_NAME } from '../../../common'; import { useKibana } from '../lib/kibana'; import { useErrorToast } from './use_error_toast'; @@ -18,23 +15,12 @@ export const useOsqueryIntegration = () => { const { http } = useKibana().services; const setErrorToast = useErrorToast(); - return useQuery( - 'integrations', - () => - http.get(epmRouteService.getListPath(), { - query: { - experimental: true, - }, - }), - { - select: ({ response }: GetPackagesResponse) => - find(['name', OSQUERY_INTEGRATION_NAME], response), - onError: (error: Error) => - setErrorToast(error, { - title: i18n.translate('xpack.osquery.osquery_integration.fetchError', { - defaultMessage: 'Error while fetching osquery integration', - }), + return useQuery('integration', () => http.get('/internal/osquery/status'), { + onError: (error: Error) => + setErrorToast(error, { + title: i18n.translate('xpack.osquery.osquery_integration.fetchError', { + defaultMessage: 'Error while fetching osquery integration', }), - } - ); + }), + }); }; diff --git a/x-pack/plugins/osquery/public/editor/index.tsx b/x-pack/plugins/osquery/public/editor/index.tsx index 5be2b1816ad86..8c844d9eda3bc 100644 --- a/x-pack/plugins/osquery/public/editor/index.tsx +++ b/x-pack/plugins/osquery/public/editor/index.tsx @@ -28,13 +28,13 @@ interface OsqueryEditorProps { const OsqueryEditorComponent: React.FC = ({ defaultValue, - // disabled, + disabled, onChange, }) => ( ( + <> + + + + + + + +); + +export const DisabledCallout = React.memo(DisabledCalloutComponent); diff --git a/x-pack/plugins/osquery/public/fleet_integration/osquery_managed_custom_button_extension.tsx b/x-pack/plugins/osquery/public/fleet_integration/osquery_managed_custom_button_extension.tsx index 775b5c7a06d21..67791cb34e683 100644 --- a/x-pack/plugins/osquery/public/fleet_integration/osquery_managed_custom_button_extension.tsx +++ b/x-pack/plugins/osquery/public/fleet_integration/osquery_managed_custom_button_extension.tsx @@ -5,16 +5,42 @@ * 2.0. */ -import React from 'react'; +import { EuiLoadingContent } from '@elastic/eui'; +import React, { useEffect } from 'react'; import { PackageCustomExtensionComponentProps } from '../../../fleet/public'; import { NavigationButtons } from './navigation_buttons'; +import { DisabledCallout } from './disabled_callout'; +import { useKibana } from '../common/lib/kibana'; /** * Exports Osquery-specific package policy instructions * for use in the Fleet app custom tab */ export const OsqueryManagedCustomButtonExtension = React.memo( - () => + () => { + const [disabled, setDisabled] = React.useState(null); + const { http } = useKibana().services; + + useEffect(() => { + const fetchStatus = () => { + http.get('/internal/osquery/status').then((response) => { + setDisabled(response.install_status !== 'installed'); + }); + }; + fetchStatus(); + }, [http]); + + if (disabled === null) { + return ; + } + + return ( + <> + {disabled ? : null} + + + ); + } ); OsqueryManagedCustomButtonExtension.displayName = 'OsqueryManagedCustomButtonExtension'; diff --git a/x-pack/plugins/osquery/public/fleet_integration/osquery_managed_policy_create_import_extension.tsx b/x-pack/plugins/osquery/public/fleet_integration/osquery_managed_policy_create_import_extension.tsx index 63036f5f693f7..9fd3c9b032ef8 100644 --- a/x-pack/plugins/osquery/public/fleet_integration/osquery_managed_policy_create_import_extension.tsx +++ b/x-pack/plugins/osquery/public/fleet_integration/osquery_managed_policy_create_import_extension.tsx @@ -11,7 +11,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; import { produce } from 'immer'; -import { i18n } from '@kbn/i18n'; import { agentRouteService, agentPolicyRouteService, @@ -29,6 +28,7 @@ import { import { ScheduledQueryGroupQueriesTable } from '../scheduled_query_groups/scheduled_query_group_queries_table'; import { useKibana } from '../common/lib/kibana'; import { NavigationButtons } from './navigation_buttons'; +import { DisabledCallout } from './disabled_callout'; import { OsqueryManagerPackagePolicy } from '../../common/types'; /** @@ -163,22 +163,7 @@ export const OsqueryManagedPolicyCreateImportExtension = React.memo< return ( <> - {!editMode ? ( - <> - - - - - - - - ) : null} + {!editMode ? : null} {policyAgentsCount === 0 ? ( <> diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx index 8654a74fecfb4..bf614ff4e9bcd 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -47,6 +47,7 @@ const LiveQueryFormComponent: React.FC = ({ defaultValue, onSuccess, }) => { + const permissions = useKibana().services.application.capabilities.osquery; const { http } = useKibana().services; const [showSavedQueryFlyout, setShowSavedQueryFlyout] = useState(false); const setErrorToast = useErrorToast(); @@ -175,7 +176,12 @@ const LiveQueryFormComponent: React.FC = ({ {!agentId && ( = ({ [ agentId, agentSelected, + permissions.writeSavedQueries, handleShowSaveQueryFlout, queryComponentProps, queryValueProvided, diff --git a/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx b/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx index 070339bb58af2..c79fae9eb5d21 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/live_query_query_field.tsx @@ -5,8 +5,9 @@ * 2.0. */ -import { EuiFormRow, EuiSpacer } from '@elastic/eui'; +import { EuiCodeBlock, EuiFormRow, EuiSpacer } from '@elastic/eui'; import React, { useCallback, useRef } from 'react'; +import styled from 'styled-components'; import { OsquerySchemaLink } from '../../components/osquery_schema_link'; import { FieldHook } from '../../shared_imports'; @@ -15,6 +16,11 @@ import { SavedQueriesDropdown, SavedQueriesDropdownRef, } from '../../saved_queries/saved_queries_dropdown'; +import { useKibana } from '../../common/lib/kibana'; + +const StyledEuiCodeBlock = styled(EuiCodeBlock)` + min-height: 150px; +`; interface LiveQueryQueryFieldProps { disabled?: boolean; @@ -22,6 +28,7 @@ interface LiveQueryQueryFieldProps { } const LiveQueryQueryFieldComponent: React.FC = ({ disabled, field }) => { + const permissions = useKibana().services.application.capabilities.osquery; const { value, setValue, errors } = field; const error = errors[0]?.message; const savedQueriesDropdownRef = useRef(null); @@ -46,12 +53,23 @@ const LiveQueryQueryFieldComponent: React.FC = ({ disa <> }> - + {!permissions.writeLiveQueries ? ( + + {value} + + ) : ( + + )} diff --git a/x-pack/plugins/osquery/public/plugin.ts b/x-pack/plugins/osquery/public/plugin.ts index 12f9025e406db..8555997d61787 100644 --- a/x-pack/plugins/osquery/public/plugin.ts +++ b/x-pack/plugins/osquery/public/plugin.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { BehaviorSubject, Subject } from 'rxjs'; import { AppMountParameters, CoreSetup, @@ -13,9 +12,6 @@ import { PluginInitializerContext, CoreStart, DEFAULT_APP_CATEGORIES, - AppStatus, - AppNavLinkStatus, - AppUpdater, } from '../../../../src/core/public'; import { Storage } from '../../../../src/plugins/kibana_utils/public'; import { @@ -25,7 +21,6 @@ import { AppPluginStartDependencies, } from './types'; import { OSQUERY_INTEGRATION_NAME, PLUGIN_NAME } from '../common'; -import { Installation } from '../../fleet/common'; import { LazyOsqueryManagedPolicyCreateImportExtension, LazyOsqueryManagedPolicyEditExtension, @@ -33,48 +28,7 @@ import { } from './fleet_integration'; import { getLazyOsqueryAction } from './shared_components'; -export function toggleOsqueryPlugin( - updater$: Subject, - http: CoreStart['http'], - registerExtension?: StartPlugins['fleet']['registerExtension'] -) { - if (http.anonymousPaths.isAnonymous(window.location.pathname)) { - updater$.next(() => ({ - status: AppStatus.inaccessible, - navLinkStatus: AppNavLinkStatus.hidden, - })); - return; - } - - http - .fetch(`/internal/osquery/status`) - .then((response) => { - const installed = response?.install_status === 'installed'; - - if (installed && registerExtension) { - registerExtension({ - package: OSQUERY_INTEGRATION_NAME, - view: 'package-detail-custom', - Component: LazyOsqueryManagedCustomButtonExtension, - }); - } - - updater$.next(() => ({ - navLinkStatus: installed ? AppNavLinkStatus.visible : AppNavLinkStatus.hidden, - })); - }) - .catch(() => { - updater$.next(() => ({ - status: AppStatus.inaccessible, - navLinkStatus: AppNavLinkStatus.hidden, - })); - }); -} - export class OsqueryPlugin implements Plugin { - private readonly appUpdater$ = new BehaviorSubject(() => ({ - navLinkStatus: AppNavLinkStatus.hidden, - })); private kibanaVersion: string; private storage = new Storage(localStorage); @@ -102,8 +56,6 @@ export class OsqueryPlugin implements Plugin ({ - status: AppStatus.inaccessible, - navLinkStatus: AppNavLinkStatus.hidden, - })); + + registerExtension({ + package: OSQUERY_INTEGRATION_NAME, + view: 'package-detail-custom', + Component: LazyOsqueryManagedCustomButtonExtension, + }); } return { diff --git a/x-pack/plugins/osquery/public/results/results_table.tsx b/x-pack/plugins/osquery/public/results/results_table.tsx index d82737ab51e7c..d293847215d68 100644 --- a/x-pack/plugins/osquery/public/results/results_table.tsx +++ b/x-pack/plugins/osquery/public/results/results_table.tsx @@ -8,6 +8,7 @@ import { isEmpty, isEqual, keys, map } from 'lodash/fp'; import { EuiCallOut, + EuiCode, EuiDataGrid, EuiDataGridSorting, EuiDataGridProps, @@ -31,6 +32,8 @@ import { ViewResultsInLensAction, ViewResultsActionButtonType, } from '../scheduled_query_groups/scheduled_query_group_queries_table'; +import { useActionResultsPrivileges } from '../action_results/use_action_privileges'; +import { OSQUERY_INTEGRATION_NAME } from '../../common'; const DataContext = createContext([]); @@ -49,6 +52,7 @@ const ResultsTableComponent: React.FC = ({ endDate, }) => { const [isLive, setIsLive] = useState(true); + const { data: hasActionResultsPrivileges } = useActionResultsPrivileges(); const { // @ts-expect-error update types data: { aggregations }, @@ -60,6 +64,7 @@ const ResultsTableComponent: React.FC = ({ direction: Direction.asc, sortField: '@timestamp', isLive, + skip: !hasActionResultsPrivileges, }); const expired = useMemo(() => (!endDate ? false : new Date(endDate) < new Date()), [endDate]); const { getUrlForApp } = useKibana().services.application; @@ -104,6 +109,7 @@ const ResultsTableComponent: React.FC = ({ field: sortedColumn.id, direction: sortedColumn.direction as Direction, })), + skip: !hasActionResultsPrivileges, }); const [visibleColumns, setVisibleColumns] = useState([]); @@ -237,6 +243,17 @@ const ResultsTableComponent: React.FC = ({ ] ); + if (!hasActionResultsPrivileges) { + return ( + +

+ You're missing read privileges to read from + logs-{OSQUERY_INTEGRATION_NAME}.result*. +

+
+ ); + } + if (!isFetched) { return ; } diff --git a/x-pack/plugins/osquery/public/routes/components/index.ts b/x-pack/plugins/osquery/public/routes/components/index.ts new file mode 100644 index 0000000000000..877c25fe7cdad --- /dev/null +++ b/x-pack/plugins/osquery/public/routes/components/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './missing_privileges'; diff --git a/x-pack/plugins/osquery/public/routes/components/missing_privileges.tsx b/x-pack/plugins/osquery/public/routes/components/missing_privileges.tsx new file mode 100644 index 0000000000000..6adabff599124 --- /dev/null +++ b/x-pack/plugins/osquery/public/routes/components/missing_privileges.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiEmptyPrompt, EuiPanel, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import styled from 'styled-components'; + +const Panel = styled(EuiPanel)` + max-width: 500px; + margin-right: auto; + margin-left: auto; +`; + +const MissingPrivilegesComponent = () => ( +
+ + + + + + } + body={ +

+ +

+ } + /> +
+ +
+); + +export const MissingPrivileges = React.memo(MissingPrivilegesComponent); diff --git a/x-pack/plugins/osquery/public/routes/live_queries/index.tsx b/x-pack/plugins/osquery/public/routes/live_queries/index.tsx index af039e85e9785..47815516dd726 100644 --- a/x-pack/plugins/osquery/public/routes/live_queries/index.tsx +++ b/x-pack/plugins/osquery/public/routes/live_queries/index.tsx @@ -12,15 +12,26 @@ import { LiveQueriesPage } from './list'; import { NewLiveQueryPage } from './new'; import { LiveQueryDetailsPage } from './details'; import { useBreadcrumbs } from '../../common/hooks/use_breadcrumbs'; +import { useKibana } from '../../common/lib/kibana'; +import { MissingPrivileges } from '../components'; const LiveQueriesComponent = () => { + const permissions = useKibana().services.application.capabilities.osquery; useBreadcrumbs('live_queries'); const match = useRouteMatch(); + if (!permissions.readLiveQueries) { + return ; + } + return ( - + {permissions.runSavedQueries || permissions.writeLiveQueries ? ( + + ) : ( + + )} diff --git a/x-pack/plugins/osquery/public/routes/live_queries/list/index.tsx b/x-pack/plugins/osquery/public/routes/live_queries/list/index.tsx index 90ac7b5cc17ae..23bc44b455405 100644 --- a/x-pack/plugins/osquery/public/routes/live_queries/list/index.tsx +++ b/x-pack/plugins/osquery/public/routes/live_queries/list/index.tsx @@ -9,13 +9,14 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; -import { useRouterNavigate } from '../../../common/lib/kibana'; +import { useKibana, useRouterNavigate } from '../../../common/lib/kibana'; import { ActionsTable } from '../../../actions/actions_table'; import { WithHeaderLayout } from '../../../components/layouts'; import { useBreadcrumbs } from '../../../common/hooks/use_breadcrumbs'; import { BetaBadge, BetaBadgeRowWrapper } from '../../../components/beta_badge'; const LiveQueriesPageComponent = () => { + const permissions = useKibana().services.application.capabilities.osquery; useBreadcrumbs('live_queries'); const newQueryLinkProps = useRouterNavigate('live_queries/new'); @@ -40,14 +41,19 @@ const LiveQueriesPageComponent = () => { const RightColumn = useMemo( () => ( - + ), - [newQueryLinkProps] + [permissions.writeLiveQueries, permissions.runSavedQueries, newQueryLinkProps] ); return ( diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx index 8d77b7819bd3e..a7596575b90c4 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/form.tsx @@ -24,11 +24,13 @@ import { useSavedQueryForm } from '../../../saved_queries/form/use_saved_query_f interface EditSavedQueryFormProps { defaultValue?: unknown; handleSubmit: () => Promise; + viewMode?: boolean; } const EditSavedQueryFormComponent: React.FC = ({ defaultValue, handleSubmit, + viewMode, }) => { const savedQueryListProps = useRouterNavigate('saved_queries'); @@ -39,41 +41,45 @@ const EditSavedQueryFormComponent: React.FC = ({ return (
- - - - - + + {!viewMode && ( + <> + + - - - - - - - - + + + + + + + + + + + + - - - - - - + + + + + + )} ); }; diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/edit/index.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/edit/index.tsx index 5bdba133fad72..401966460a7e7 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/edit/index.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/edit/index.tsx @@ -17,7 +17,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { useParams } from 'react-router-dom'; -import { useRouterNavigate } from '../../../common/lib/kibana'; +import { useKibana, useRouterNavigate } from '../../../common/lib/kibana'; import { WithHeaderLayout } from '../../../components/layouts'; import { useBreadcrumbs } from '../../../common/hooks/use_breadcrumbs'; import { BetaBadge, BetaBadgeRowWrapper } from '../../../components/beta_badge'; @@ -25,6 +25,8 @@ import { EditSavedQueryForm } from './form'; import { useDeleteSavedQuery, useUpdateSavedQuery, useSavedQuery } from '../../../saved_queries'; const EditSavedQueryPageComponent = () => { + const permissions = useKibana().services.application.capabilities.osquery; + const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); const { savedQueryId } = useParams<{ savedQueryId: string }>(); const savedQueryListProps = useRouterNavigate('saved_queries'); @@ -35,6 +37,8 @@ const EditSavedQueryPageComponent = () => { useBreadcrumbs('saved_query_edit', { savedQueryName: savedQueryDetails?.attributes?.id ?? '' }); + const viewMode = useMemo(() => !permissions.writeSavedQueries, [permissions.writeSavedQueries]); + const handleCloseDeleteConfirmationModal = useCallback(() => { setIsDeleteModalVisible(false); }, []); @@ -63,21 +67,32 @@ const EditSavedQueryPageComponent = () => {

- + {viewMode ? ( + + ) : ( + + )}

), - [savedQueryDetails?.attributes?.id, savedQueryListProps] + [savedQueryDetails?.attributes?.id, savedQueryListProps, viewMode] ); const RightColumn = useMemo( @@ -95,12 +110,17 @@ const EditSavedQueryPageComponent = () => { if (isLoading) return null; return ( - + {!isLoading && !isEmpty(savedQueryDetails) && ( )} {isDeleteModalVisible ? ( diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/index.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/index.tsx index f986129bdfefc..a2241ae017df4 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/index.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/index.tsx @@ -12,15 +12,22 @@ import { QueriesPage } from './list'; import { NewSavedQueryPage } from './new'; import { EditSavedQueryPage } from './edit'; import { useBreadcrumbs } from '../../common/hooks/use_breadcrumbs'; +import { MissingPrivileges } from '../components'; +import { useKibana } from '../../common/lib/kibana'; const SavedQueriesComponent = () => { + const permissions = useKibana().services.application.capabilities.osquery; useBreadcrumbs('saved_queries'); const match = useRouteMatch(); + if (!permissions.readSavedQueries) { + return ; + } + return ( - + {permissions.writeSavedQueries ? : } diff --git a/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx b/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx index 0c04e816dae7a..e82dcf85780e1 100644 --- a/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx +++ b/x-pack/plugins/osquery/public/routes/saved_queries/list/index.tsx @@ -21,16 +21,63 @@ import { useHistory } from 'react-router-dom'; import { SavedObject } from 'kibana/public'; import { WithHeaderLayout } from '../../../components/layouts'; import { useBreadcrumbs } from '../../../common/hooks/use_breadcrumbs'; -import { useRouterNavigate } from '../../../common/lib/kibana'; +import { useKibana, useRouterNavigate } from '../../../common/lib/kibana'; import { BetaBadge, BetaBadgeRowWrapper } from '../../../components/beta_badge'; import { useSavedQueries } from '../../../saved_queries/use_saved_queries'; +interface PlayButtonProps { + disabled: boolean; + savedQueryId: string; + savedQueryName: string; +} + +const PlayButtonComponent: React.FC = ({ + disabled = false, + savedQueryId, + savedQueryName, +}) => { + const { push } = useHistory(); + + // TODO: Fix href + const handlePlayClick = useCallback( + () => + push('/live_queries/new', { + form: { + savedQueryId, + }, + }), + [push, savedQueryId] + ); + + return ( + + ); +}; + +const PlayButton = React.memo(PlayButtonComponent); + interface EditButtonProps { + disabled?: boolean; savedQueryId: string; savedQueryName: string; } -const EditButtonComponent: React.FC = ({ savedQueryId, savedQueryName }) => { +const EditButtonComponent: React.FC = ({ + disabled = false, + savedQueryId, + savedQueryName, +}) => { const buttonProps = useRouterNavigate(`saved_queries/${savedQueryId}`); return ( @@ -38,6 +85,7 @@ const EditButtonComponent: React.FC = ({ savedQueryId, savedQue color="primary" {...buttonProps} iconType="pencil" + isDisabled={disabled} aria-label={i18n.translate('xpack.osquery.savedQueryList.queriesTable.editActionAriaLabel', { defaultMessage: 'Edit {savedQueryName}', values: { @@ -51,8 +99,9 @@ const EditButtonComponent: React.FC = ({ savedQueryId, savedQue const EditButton = React.memo(EditButtonComponent); const SavedQueriesPageComponent = () => { + const permissions = useKibana().services.application.capabilities.osquery; + useBreadcrumbs('saved_queries'); - const { push } = useHistory(); const newQueryLinkProps = useRouterNavigate('saved_queries/new'); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(20); @@ -61,16 +110,6 @@ const SavedQueriesPageComponent = () => { const { data } = useSavedQueries({ isLive: true }); - const handlePlayClick = useCallback( - (item) => - push('/live_queries/new', { - form: { - savedQueryId: item.id, - }, - }), - [push] - ); - const renderEditAction = useCallback( (item: SavedObject<{ name: string }>) => ( @@ -78,6 +117,17 @@ const SavedQueriesPageComponent = () => { [] ); + const renderPlayAction = useCallback( + (item: SavedObject<{ name: string }>) => ( + + ), + [permissions.runSavedQueries, permissions.writeLiveQueries] + ); + const renderUpdatedAt = useCallback((updatedAt, item) => { if (!updatedAt) return '-'; @@ -128,17 +178,10 @@ const SavedQueriesPageComponent = () => { name: i18n.translate('xpack.osquery.savedQueries.table.actionsColumnTitle', { defaultMessage: 'Actions', }), - actions: [ - { - type: 'icon', - icon: 'play', - onClick: handlePlayClick, - }, - { render: renderEditAction }, - ], + actions: [{ render: renderPlayAction }, { render: renderEditAction }], }, ], - [handlePlayClick, renderEditAction, renderUpdatedAt] + [renderEditAction, renderPlayAction, renderUpdatedAt] ); const onTableChange = useCallback(({ page = {}, sort = {} }) => { @@ -189,14 +232,19 @@ const SavedQueriesPageComponent = () => { const RightColumn = useMemo( () => ( - + ), - [newQueryLinkProps] + [permissions.writeSavedQueries, newQueryLinkProps] ); return ( diff --git a/x-pack/plugins/osquery/public/routes/scheduled_query_groups/details/index.tsx b/x-pack/plugins/osquery/public/routes/scheduled_query_groups/details/index.tsx index 960de043eac6e..dc6df49615093 100644 --- a/x-pack/plugins/osquery/public/routes/scheduled_query_groups/details/index.tsx +++ b/x-pack/plugins/osquery/public/routes/scheduled_query_groups/details/index.tsx @@ -21,7 +21,7 @@ import React, { useMemo } from 'react'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; -import { useRouterNavigate } from '../../../common/lib/kibana'; +import { useKibana, useRouterNavigate } from '../../../common/lib/kibana'; import { WithHeaderLayout } from '../../../components/layouts'; import { useScheduledQueryGroup } from '../../../scheduled_query_groups/use_scheduled_query_group'; import { ScheduledQueryGroupQueriesTable } from '../../../scheduled_query_groups/scheduled_query_group_queries_table'; @@ -36,6 +36,7 @@ const Divider = styled.div` `; const ScheduledQueryGroupDetailsPageComponent = () => { + const permissions = useKibana().services.application.capabilities.osquery; const { scheduledQueryGroupId } = useParams<{ scheduledQueryGroupId: string }>(); const scheduledQueryGroupsListProps = useRouterNavigate('scheduled_query_groups'); const editQueryLinkProps = useRouterNavigate( @@ -111,7 +112,12 @@ const ScheduledQueryGroupDetailsPageComponent = () => { - + { ), - [data?.policy_id, editQueryLinkProps] + [data?.policy_id, editQueryLinkProps, permissions] ); return ( diff --git a/x-pack/plugins/osquery/public/routes/scheduled_query_groups/index.tsx b/x-pack/plugins/osquery/public/routes/scheduled_query_groups/index.tsx index 76ca2bf14d303..53bf4ae79a908 100644 --- a/x-pack/plugins/osquery/public/routes/scheduled_query_groups/index.tsx +++ b/x-pack/plugins/osquery/public/routes/scheduled_query_groups/index.tsx @@ -13,18 +13,25 @@ import { AddScheduledQueryGroupPage } from './add'; import { EditScheduledQueryGroupPage } from './edit'; import { ScheduledQueryGroupDetailsPage } from './details'; import { useBreadcrumbs } from '../../common/hooks/use_breadcrumbs'; +import { useKibana } from '../../common/lib/kibana'; +import { MissingPrivileges } from '../components'; const ScheduledQueryGroupsComponent = () => { + const permissions = useKibana().services.application.capabilities.osquery; useBreadcrumbs('scheduled_query_groups'); const match = useRouteMatch(); + if (!permissions.readPacks) { + return ; + } + return ( - + {permissions.writePacks ? : } - + {permissions.writePacks ? : } diff --git a/x-pack/plugins/osquery/public/routes/scheduled_query_groups/list/index.tsx b/x-pack/plugins/osquery/public/routes/scheduled_query_groups/list/index.tsx index b02ef95498b5c..006dd0e6ec1b6 100644 --- a/x-pack/plugins/osquery/public/routes/scheduled_query_groups/list/index.tsx +++ b/x-pack/plugins/osquery/public/routes/scheduled_query_groups/list/index.tsx @@ -9,12 +9,13 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; -import { useRouterNavigate } from '../../../common/lib/kibana'; +import { useKibana, useRouterNavigate } from '../../../common/lib/kibana'; import { WithHeaderLayout } from '../../../components/layouts'; import { ScheduledQueryGroupsTable } from '../../../scheduled_query_groups/scheduled_query_groups_table'; import { BetaBadge, BetaBadgeRowWrapper } from '../../../components/beta_badge'; const ScheduledQueryGroupsPageComponent = () => { + const permissions = useKibana().services.application.capabilities.osquery; const newQueryLinkProps = useRouterNavigate('scheduled_query_groups/add'); const LeftColumn = useMemo( @@ -38,14 +39,19 @@ const ScheduledQueryGroupsPageComponent = () => { const RightColumn = useMemo( () => ( - + ), - [newQueryLinkProps] + [newQueryLinkProps, permissions.writePacks] ); return ( diff --git a/x-pack/plugins/osquery/public/saved_queries/form/index.tsx b/x-pack/plugins/osquery/public/saved_queries/form/index.tsx index 9bbf847c4d2a0..beff34a8919a0 100644 --- a/x-pack/plugins/osquery/public/saved_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/saved_queries/form/index.tsx @@ -6,7 +6,7 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle, EuiText } from '@elastic/eui'; -import React from 'react'; +import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -17,64 +17,78 @@ import { CodeEditorField } from './code_editor_field'; export const CommonUseField = getUseField({ component: Field }); -const SavedQueryFormComponent = () => ( - <> - - - - - - - - - -
+interface SavedQueryFormProps { + viewMode?: boolean; +} + +const SavedQueryFormComponent: React.FC = ({ viewMode }) => { + const euiFieldProps = useMemo( + () => ({ + isDisabled: !!viewMode, + }), + [viewMode] + ); + + return ( + <> + + + + + + + + + +
+ +
+
+ -
-
- - +
+
+ + + + + + - - - - - - - - - - - - - - - - -); + + + + + + + + ); +}; export const SavedQueryForm = React.memo(SavedQueryFormComponent); diff --git a/x-pack/plugins/osquery/public/scheduled_query_groups/active_state_switch.tsx b/x-pack/plugins/osquery/public/scheduled_query_groups/active_state_switch.tsx index bcb47d0adc833..7f26534626b12 100644 --- a/x-pack/plugins/osquery/public/scheduled_query_groups/active_state_switch.tsx +++ b/x-pack/plugins/osquery/public/scheduled_query_groups/active_state_switch.tsx @@ -28,12 +28,16 @@ const StyledEuiLoadingSpinner = styled(EuiLoadingSpinner)` `; interface ActiveStateSwitchProps { + disabled?: boolean; item: PackagePolicy; } const ActiveStateSwitchComponent: React.FC = ({ item }) => { const queryClient = useQueryClient(); const { + application: { + capabilities: { osquery: permissions }, + }, http, notifications: { toasts }, } = useKibana().services; @@ -126,7 +130,7 @@ const ActiveStateSwitchComponent: React.FC = ({ item }) {isLoading && } ( ['scheduledQueryGroup', { scheduledQueryGroupId }], - () => http.get(packagePolicyRouteService.getInfoPath(scheduledQueryGroupId)), + () => http.get(`/internal/osquery/scheduled_query_group/${scheduledQueryGroupId}`), { keepPreviousData: true, enabled: !skip || !scheduledQueryGroupId, diff --git a/x-pack/plugins/osquery/public/scheduled_query_groups/use_scheduled_query_groups.ts b/x-pack/plugins/osquery/public/scheduled_query_groups/use_scheduled_query_groups.ts index 3302d8e621eb7..01b67a3d5164a 100644 --- a/x-pack/plugins/osquery/public/scheduled_query_groups/use_scheduled_query_groups.ts +++ b/x-pack/plugins/osquery/public/scheduled_query_groups/use_scheduled_query_groups.ts @@ -9,12 +9,7 @@ import { produce } from 'immer'; import { useQuery } from 'react-query'; import { useKibana } from '../common/lib/kibana'; -import { - ListResult, - PackagePolicy, - packagePolicyRouteService, - PACKAGE_POLICY_SAVED_OBJECT_TYPE, -} from '../../../fleet/common'; +import { ListResult, PackagePolicy, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../fleet/common'; import { OSQUERY_INTEGRATION_NAME } from '../../common'; export const useScheduledQueryGroups = () => { @@ -23,7 +18,7 @@ export const useScheduledQueryGroups = () => { return useQuery>( ['scheduledQueries'], () => - http.get(packagePolicyRouteService.getListPath(), { + http.get('/internal/osquery/scheduled_query_group', { query: { page: 1, perPage: 10000, diff --git a/x-pack/plugins/osquery/server/lib/osquery_app_context_services.ts b/x-pack/plugins/osquery/server/lib/osquery_app_context_services.ts index 6ebf469b8fb29..ca4fd1ebeffd2 100644 --- a/x-pack/plugins/osquery/server/lib/osquery_app_context_services.ts +++ b/x-pack/plugins/osquery/server/lib/osquery_app_context_services.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Logger, LoggerFactory } from 'src/core/server'; +import { CoreSetup, Logger, LoggerFactory } from '../../../../../src/core/server'; import { SecurityPluginStart } from '../../../security/server'; import { AgentService, @@ -71,6 +71,7 @@ export interface OsqueryAppContext { logFactory: LoggerFactory; config(): ConfigType; security: SecurityPluginStart; + getStartServices: CoreSetup['getStartServices']; /** * Object readiness is tied to plugin start method */ diff --git a/x-pack/plugins/osquery/server/plugin.ts b/x-pack/plugins/osquery/server/plugin.ts index 6bc12f5736e5e..ff8483fdb385a 100644 --- a/x-pack/plugins/osquery/server/plugin.ts +++ b/x-pack/plugins/osquery/server/plugin.ts @@ -5,14 +5,20 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; +import { + PACKAGE_POLICY_SAVED_OBJECT_TYPE, + AGENT_POLICY_SAVED_OBJECT_TYPE, + PACKAGES_SAVED_OBJECT_TYPE, +} from '../../fleet/common'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger, + DEFAULT_APP_CATEGORIES, } from '../../../../src/core/server'; - import { createConfig } from './create_config'; import { OsqueryPluginSetup, OsqueryPluginStart, SetupPlugins, StartPlugins } from './types'; import { defineRoutes } from './routes'; @@ -21,6 +27,169 @@ import { initSavedObjects } from './saved_objects'; import { initUsageCollectors } from './usage'; import { OsqueryAppContext, OsqueryAppContextService } from './lib/osquery_app_context_services'; import { ConfigType } from './config'; +import { packSavedObjectType, savedQuerySavedObjectType } from '../common/types'; +import { PLUGIN_ID } from '../common'; + +const registerFeatures = (features: SetupPlugins['features']) => { + features.registerKibanaFeature({ + id: PLUGIN_ID, + name: i18n.translate('xpack.osquery.features.osqueryFeatureName', { + defaultMessage: 'Osquery', + }), + category: DEFAULT_APP_CATEGORIES.management, + app: [PLUGIN_ID, 'kibana'], + catalogue: [PLUGIN_ID], + order: 2300, + excludeFromBasePrivileges: true, + privileges: { + all: { + api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-write`], + app: [PLUGIN_ID, 'kibana'], + catalogue: [PLUGIN_ID], + savedObject: { + all: [PACKAGE_POLICY_SAVED_OBJECT_TYPE], + read: [PACKAGES_SAVED_OBJECT_TYPE, AGENT_POLICY_SAVED_OBJECT_TYPE], + }, + ui: ['write'], + }, + read: { + api: [`${PLUGIN_ID}-read`], + app: [PLUGIN_ID, 'kibana'], + catalogue: [PLUGIN_ID], + savedObject: { + all: [], + read: [ + PACKAGE_POLICY_SAVED_OBJECT_TYPE, + PACKAGES_SAVED_OBJECT_TYPE, + AGENT_POLICY_SAVED_OBJECT_TYPE, + ], + }, + ui: ['read'], + }, + }, + subFeatures: [ + { + name: i18n.translate('xpack.osquery.features.liveQueriesSubFeatureName', { + defaultMessage: 'Live queries', + }), + privilegeGroups: [ + { + groupType: 'mutually_exclusive', + privileges: [ + { + api: [`${PLUGIN_ID}-writeLiveQueries`, `${PLUGIN_ID}-readLiveQueries`], + id: 'live_queries_all', + includeIn: 'all', + name: 'All', + savedObject: { + all: [], + read: [], + }, + ui: ['writeLiveQueries', 'readLiveQueries'], + }, + { + api: [`${PLUGIN_ID}-readLiveQueries`], + id: 'live_queries_read', + includeIn: 'read', + name: 'Read', + savedObject: { + all: [], + read: [], + }, + ui: ['readLiveQueries'], + }, + ], + }, + { + groupType: 'independent', + privileges: [ + { + api: [`${PLUGIN_ID}-runSavedQueries`], + id: 'run_saved_queries', + name: i18n.translate('xpack.osquery.features.runSavedQueriesPrivilegeName', { + defaultMessage: 'Run Saved queries', + }), + includeIn: 'all', + savedObject: { + all: [], + read: [], + }, + ui: ['runSavedQueries'], + }, + ], + }, + ], + }, + { + name: i18n.translate('xpack.osquery.features.savedQueriesSubFeatureName', { + defaultMessage: 'Saved queries', + }), + privilegeGroups: [ + { + groupType: 'mutually_exclusive', + privileges: [ + { + id: 'saved_queries_all', + includeIn: 'all', + name: 'All', + savedObject: { + all: [savedQuerySavedObjectType], + read: [], + }, + ui: ['writeSavedQueries', 'readSavedQueries'], + }, + { + id: 'saved_queries_read', + includeIn: 'read', + name: 'Read', + savedObject: { + all: [], + read: [savedQuerySavedObjectType], + }, + ui: ['readSavedQueries'], + }, + ], + }, + ], + }, + { + // TODO: Rename it to "Packs" as part of https://github.com/elastic/kibana/pull/107345 + name: i18n.translate('xpack.osquery.features.scheduledQueryGroupsSubFeatureName', { + defaultMessage: 'Scheduled query groups', + }), + privilegeGroups: [ + { + groupType: 'mutually_exclusive', + privileges: [ + { + api: [`${PLUGIN_ID}-writePacks`], + id: 'packs_all', + includeIn: 'all', + name: 'All', + savedObject: { + all: [packSavedObjectType], + read: [], + }, + ui: ['writePacks', 'readPacks'], + }, + { + api: [`${PLUGIN_ID}-readPacks`], + id: 'packs_read', + includeIn: 'read', + name: 'Read', + savedObject: { + all: [], + read: [packSavedObjectType], + }, + ui: ['readPacks'], + }, + ], + }, + ], + }, + ], + }); +}; export class OsqueryPlugin implements Plugin { private readonly logger: Logger; @@ -40,10 +209,13 @@ export class OsqueryPlugin implements Plugin config, security: plugins.security, diff --git a/x-pack/plugins/osquery/server/routes/action/create_action_route.ts b/x-pack/plugins/osquery/server/routes/action/create_action_route.ts index 478bfc1053bdf..79c1149675b0d 100644 --- a/x-pack/plugins/osquery/server/routes/action/create_action_route.ts +++ b/x-pack/plugins/osquery/server/routes/action/create_action_route.ts @@ -8,6 +8,7 @@ import uuid from 'uuid'; import moment from 'moment'; +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; @@ -19,6 +20,7 @@ import { } from '../../../common/schemas/routes/action/create_action_request_body_schema'; import { incrementCount } from '../usage'; +import { getInternalSavedObjectsClient } from '../../usage/collector'; export const createActionRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.post( @@ -30,10 +32,17 @@ export const createActionRoute = (router: IRouter, osqueryContext: OsqueryAppCon CreateActionRequestBodySchema >(createActionRequestBodySchema), }, + options: { + tags: [`access:${PLUGIN_ID}-readLiveQueries`, `access:${PLUGIN_ID}-runSavedQueries`], + }, }, async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asCurrentUser; + const esClient = context.core.elasticsearch.client.asInternalUser; const soClient = context.core.savedObjects.client; + const internalSavedObjectsClient = await getInternalSavedObjectsClient( + osqueryContext.getStartServices + ); + const { agentSelection } = request.body as { agentSelection: AgentSelection }; const selectedAgents = await parseAgentSelection( esClient, @@ -41,12 +50,14 @@ export const createActionRoute = (router: IRouter, osqueryContext: OsqueryAppCon osqueryContext, agentSelection ); - incrementCount(soClient, 'live_query'); + incrementCount(internalSavedObjectsClient, 'live_query'); if (!selectedAgents.length) { - incrementCount(soClient, 'live_query', 'errors'); + incrementCount(internalSavedObjectsClient, 'live_query', 'errors'); return response.badRequest({ body: new Error('No agents found for selection') }); } + // TODO: Add check for `runSavedQueries` only + try { const currentUser = await osqueryContext.security.authc.getCurrentUser(request)?.username; const action = { @@ -74,7 +85,7 @@ export const createActionRoute = (router: IRouter, osqueryContext: OsqueryAppCon }, }); } catch (error) { - incrementCount(soClient, 'live_query', 'errors'); + incrementCount(internalSavedObjectsClient, 'live_query', 'errors'); return response.customError({ statusCode: 500, body: new Error(`Error occurred while processing ${error}`), diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts new file mode 100644 index 0000000000000..67b4a27ab9ec7 --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_details.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { PLUGIN_ID } from '../../../common'; +import { IRouter } from '../../../../../../src/core/server'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +export const getAgentDetailsRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { + router.get( + { + path: '/internal/osquery/fleet_wrapper/agents/{id}', + validate: { + params: schema.object({}, { unknowns: 'allow' }), + }, + options: { tags: [`access:${PLUGIN_ID}-read`] }, + }, + async (context, request, response) => { + const esClient = context.core.elasticsearch.client.asInternalUser; + + const agent = await osqueryContext.service + .getAgentService() + // @ts-expect-error update types + ?.getAgent(esClient, request.params.id); + + return response.ok({ body: { item: agent } }); + } + ); +}; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts new file mode 100644 index 0000000000000..e35e776cb1958 --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policies.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { PLUGIN_ID } from '../../../common'; +import { IRouter } from '../../../../../../src/core/server'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +export const getAgentPoliciesRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { + router.get( + { + path: '/internal/osquery/fleet_wrapper/agent_policies', + validate: { + params: schema.object({}, { unknowns: 'allow' }), + query: schema.object({}, { unknowns: 'allow' }), + }, + options: { tags: [`access:${PLUGIN_ID}-read`] }, + }, + async (context, request, response) => { + const soClient = context.core.savedObjects.client; + + const agentPolicies = await osqueryContext.service.getAgentPolicyService()?.list(soClient, { + ...(request.query || {}), + perPage: 100, + }); + + return response.ok({ body: agentPolicies }); + } + ); +}; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts new file mode 100644 index 0000000000000..f845b04e99c93 --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_policy.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { PLUGIN_ID } from '../../../common'; +import { IRouter } from '../../../../../../src/core/server'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +export const getAgentPolicyRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { + router.get( + { + path: '/internal/osquery/fleet_wrapper/agent_policies/{id}', + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + options: { tags: [`access:${PLUGIN_ID}-read`] }, + }, + async (context, request, response) => { + const soClient = context.core.savedObjects.client; + + const packageInfo = await osqueryContext.service + .getAgentPolicyService() + ?.get(soClient, request.params.id); + + return response.ok({ body: { item: packageInfo } }); + } + ); +}; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts new file mode 100644 index 0000000000000..dea4402472958 --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agent_status_for_agent_policy.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { PLUGIN_ID } from '../../../common'; +import { GetAgentStatusResponse } from '../../../../fleet/common'; +import { IRouter } from '../../../../../../src/core/server'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +export const getAgentStatusForAgentPolicyRoute = ( + router: IRouter, + osqueryContext: OsqueryAppContext +) => { + router.get( + { + path: '/internal/osquery/fleet_wrapper/agent-status', + validate: { + query: schema.object({ + policyId: schema.string(), + kuery: schema.maybe(schema.string()), + }), + params: schema.object({}, { unknowns: 'allow' }), + }, + options: { tags: [`access:${PLUGIN_ID}-read`] }, + }, + async (context, request, response) => { + const esClient = context.core.elasticsearch.client.asInternalUser; + + const results = await osqueryContext.service + .getAgentService() + ?.getAgentStatusForAgentPolicy(esClient, request.query.policyId, request.query.kuery); + + if (!results) { + return response.ok({ body: {} }); + } + + const body: GetAgentStatusResponse = { results }; + + return response.ok({ body }); + } + ); +}; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts new file mode 100644 index 0000000000000..d45cb26e0d199 --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_agents.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { PLUGIN_ID } from '../../../common'; +import { IRouter } from '../../../../../../src/core/server'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +export const getAgentsRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { + router.get( + { + path: '/internal/osquery/fleet_wrapper/agents', + validate: { + query: schema.object({}, { unknowns: 'allow' }), + }, + options: { tags: [`access:${PLUGIN_ID}-read`] }, + }, + async (context, request, response) => { + const esClient = context.core.elasticsearch.client.asInternalUser; + + const agents = await osqueryContext.service + .getAgentService() + // @ts-expect-error update types + ?.listAgents(esClient, request.query); + + return response.ok({ body: agents }); + } + ); +}; diff --git a/x-pack/plugins/osquery/server/routes/scheduled_query/find_scheduled_query_route.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts similarity index 81% rename from x-pack/plugins/osquery/server/routes/scheduled_query/find_scheduled_query_route.ts rename to x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts index 43d5f3fc893f0..b95dfbdfb9cb4 100644 --- a/x-pack/plugins/osquery/server/routes/scheduled_query/find_scheduled_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/get_package_policies.ts @@ -6,19 +6,19 @@ */ import { schema } from '@kbn/config-schema'; -import { OSQUERY_INTEGRATION_NAME } from '../../../common'; - +import { PLUGIN_ID, OSQUERY_INTEGRATION_NAME } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../fleet/common'; import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; -export const findScheduledQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { +export const getPackagePoliciesRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.get( { - path: '/internal/osquery/scheduled_query', + path: '/internal/osquery/fleet_wrapper/package_policies', validate: { query: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-read`] }, }, async (context, request, response) => { const kuery = `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.attributes.package.name: ${OSQUERY_INTEGRATION_NAME}`; diff --git a/x-pack/plugins/osquery/server/routes/fleet_wrapper/index.ts b/x-pack/plugins/osquery/server/routes/fleet_wrapper/index.ts new file mode 100644 index 0000000000000..1821e19da975e --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/fleet_wrapper/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from '../../../../../../src/core/server'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; +import { getAgentPoliciesRoute } from './get_agent_policies'; +import { getAgentPolicyRoute } from './get_agent_policy'; +import { getAgentStatusForAgentPolicyRoute } from './get_agent_status_for_agent_policy'; +import { getPackagePoliciesRoute } from './get_package_policies'; +import { getAgentsRoute } from './get_agents'; +import { getAgentDetailsRoute } from './get_agent_details'; + +export const initFleetWrapperRoutes = (router: IRouter, context: OsqueryAppContext) => { + getAgentDetailsRoute(router, context); + getAgentPoliciesRoute(router, context); + getAgentPolicyRoute(router, context); + getAgentStatusForAgentPolicyRoute(router, context); + getPackagePoliciesRoute(router, context); + getAgentsRoute(router, context); +}; diff --git a/x-pack/plugins/osquery/server/routes/index.ts b/x-pack/plugins/osquery/server/routes/index.ts index dd11141b2553f..c927c711a23cb 100644 --- a/x-pack/plugins/osquery/server/routes/index.ts +++ b/x-pack/plugins/osquery/server/routes/index.ts @@ -10,13 +10,19 @@ import { initActionRoutes } from './action'; import { OsqueryAppContext } from '../lib/osquery_app_context_services'; import { initSavedQueryRoutes } from './saved_query'; import { initStatusRoutes } from './status'; +import { initFleetWrapperRoutes } from './fleet_wrapper'; import { initPackRoutes } from './pack'; +import { initScheduledQueryGroupRoutes } from './scheduled_query_group'; +import { initPrivilegesCheckRoutes } from './privileges_check'; export const defineRoutes = (router: IRouter, context: OsqueryAppContext) => { const config = context.config(); initActionRoutes(router, context); initStatusRoutes(router, context); + initScheduledQueryGroupRoutes(router, context); + initFleetWrapperRoutes(router, context); + initPrivilegesCheckRoutes(router, context); if (config.packs) { initPackRoutes(router); diff --git a/x-pack/plugins/osquery/server/routes/privileges_check/index.ts b/x-pack/plugins/osquery/server/routes/privileges_check/index.ts new file mode 100644 index 0000000000000..8932b23b85f5a --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/privileges_check/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from '../../../../../../src/core/server'; +import { privilegesCheckRoute } from './privileges_check_route'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +export const initPrivilegesCheckRoutes = (router: IRouter, context: OsqueryAppContext) => { + privilegesCheckRoute(router, context); +}; diff --git a/x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts b/x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts new file mode 100644 index 0000000000000..80c335c1c46d3 --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/privileges_check/privileges_check_route.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { OSQUERY_INTEGRATION_NAME, PLUGIN_ID } from '../../../common'; +import { IRouter } from '../../../../../../src/core/server'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const privilegesCheckRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { + router.get( + { + path: '/internal/osquery/privileges_check', + validate: {}, + options: { + tags: [`access:${PLUGIN_ID}-readLiveQueries`], + }, + }, + async (context, request, response) => { + const esClient = context.core.elasticsearch.client.asCurrentUser; + + const privileges = ( + await esClient.security.hasPrivileges({ + body: { + index: [ + { + names: [`logs-${OSQUERY_INTEGRATION_NAME}.result*`], + privileges: ['read'], + }, + ], + }, + }) + ).body; + + return response.ok({ + body: privileges, + }); + } + ); +}; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts index a41cb7cc39b40..fe8220c559de8 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/create_saved_query_route.ts @@ -6,7 +6,7 @@ */ import { IRouter } from '../../../../../../src/core/server'; - +import { PLUGIN_ID } from '../../../common'; import { createSavedQueryRequestSchema, CreateSavedQueryRequestSchemaDecoded, @@ -24,6 +24,7 @@ export const createSavedQueryRoute = (router: IRouter) => { CreateSavedQueryRequestSchemaDecoded >(createSavedQueryRequestSchema), }, + options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, }, async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts index 5b8e231ba61ec..a34db8c11ddc3 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/delete_saved_query_route.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; - +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { savedQuerySavedObjectType } from '../../../common/types'; @@ -17,6 +17,7 @@ export const deleteSavedQueryRoute = (router: IRouter) => { validate: { body: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, }, async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts index 6d737ba0d0220..79d6927d06722 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/find_saved_query_route.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; - +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { savedQuerySavedObjectType } from '../../../common/types'; @@ -17,6 +17,7 @@ export const findSavedQueryRoute = (router: IRouter) => { validate: { query: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-readSavedQueries`] }, }, async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts index 2d399648df4cc..4157ed1582305 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/read_saved_query_route.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; - +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { savedQuerySavedObjectType } from '../../../common/types'; @@ -17,6 +17,7 @@ export const readSavedQueryRoute = (router: IRouter) => { validate: { params: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-readSavedQueries`] }, }, async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; diff --git a/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts b/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts index f9ecf675489dc..8edf95e311543 100644 --- a/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/saved_query/update_saved_query_route.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; - +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { savedQuerySavedObjectType } from '../../../common/types'; @@ -18,6 +18,7 @@ export const updateSavedQueryRoute = (router: IRouter) => { params: schema.object({}, { unknowns: 'allow' }), body: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-writeSavedQueries`] }, }, async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; diff --git a/x-pack/plugins/osquery/server/routes/scheduled_query/create_scheduled_query_route.ts b/x-pack/plugins/osquery/server/routes/scheduled_query_group/create_scheduled_query_route.ts similarity index 87% rename from x-pack/plugins/osquery/server/routes/scheduled_query/create_scheduled_query_route.ts rename to x-pack/plugins/osquery/server/routes/scheduled_query_group/create_scheduled_query_route.ts index a3b882392989f..831fb30f6e320 100644 --- a/x-pack/plugins/osquery/server/routes/scheduled_query/create_scheduled_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/scheduled_query_group/create_scheduled_query_route.ts @@ -6,16 +6,18 @@ */ import { schema } from '@kbn/config-schema'; +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; export const createScheduledQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { router.post( { - path: '/internal/osquery/scheduled', + path: '/internal/osquery/scheduled_query_group', validate: { body: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, }, async (context, request, response) => { const esClient = context.core.elasticsearch.client.asCurrentUser; diff --git a/x-pack/plugins/osquery/server/routes/scheduled_query/delete_scheduled_query_route.ts b/x-pack/plugins/osquery/server/routes/scheduled_query_group/delete_scheduled_query_route.ts similarity index 87% rename from x-pack/plugins/osquery/server/routes/scheduled_query/delete_scheduled_query_route.ts rename to x-pack/plugins/osquery/server/routes/scheduled_query_group/delete_scheduled_query_route.ts index 5b8e231ba61ec..c914512bb155e 100644 --- a/x-pack/plugins/osquery/server/routes/scheduled_query/delete_scheduled_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/scheduled_query_group/delete_scheduled_query_route.ts @@ -6,17 +6,18 @@ */ import { schema } from '@kbn/config-schema'; - +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { savedQuerySavedObjectType } from '../../../common/types'; export const deleteSavedQueryRoute = (router: IRouter) => { router.delete( { - path: '/internal/osquery/saved_query', + path: '/internal/osquery/scheduled_query_group', validate: { body: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, }, async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; diff --git a/x-pack/plugins/osquery/server/routes/scheduled_query_group/find_scheduled_query_group_route.ts b/x-pack/plugins/osquery/server/routes/scheduled_query_group/find_scheduled_query_group_route.ts new file mode 100644 index 0000000000000..15c45e09b1bfd --- /dev/null +++ b/x-pack/plugins/osquery/server/routes/scheduled_query_group/find_scheduled_query_group_route.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; +import { PLUGIN_ID, OSQUERY_INTEGRATION_NAME } from '../../../common'; +import { IRouter } from '../../../../../../src/core/server'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../fleet/common'; +import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; + +export const findScheduledQueryGroupRoute = ( + router: IRouter, + osqueryContext: OsqueryAppContext +) => { + router.get( + { + path: '/internal/osquery/scheduled_query_group', + validate: { + query: schema.object({}, { unknowns: 'allow' }), + }, + options: { tags: [`access:${PLUGIN_ID}-readPacks`] }, + }, + async (context, request, response) => { + const kuery = `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.attributes.package.name: ${OSQUERY_INTEGRATION_NAME}`; + const packagePolicyService = osqueryContext.service.getPackagePolicyService(); + const policies = await packagePolicyService?.list(context.core.savedObjects.client, { + kuery, + }); + + return response.ok({ + body: policies, + }); + } + ); +}; diff --git a/x-pack/plugins/osquery/server/routes/scheduled_query/index.ts b/x-pack/plugins/osquery/server/routes/scheduled_query_group/index.ts similarity index 67% rename from x-pack/plugins/osquery/server/routes/scheduled_query/index.ts rename to x-pack/plugins/osquery/server/routes/scheduled_query_group/index.ts index 706bc38397296..416981a5cb5f2 100644 --- a/x-pack/plugins/osquery/server/routes/scheduled_query/index.ts +++ b/x-pack/plugins/osquery/server/routes/scheduled_query_group/index.ts @@ -10,14 +10,14 @@ import { IRouter } from '../../../../../../src/core/server'; import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; // import { createScheduledQueryRoute } from './create_scheduled_query_route'; // import { deleteScheduledQueryRoute } from './delete_scheduled_query_route'; -import { findScheduledQueryRoute } from './find_scheduled_query_route'; -import { readScheduledQueryRoute } from './read_scheduled_query_route'; +import { findScheduledQueryGroupRoute } from './find_scheduled_query_group_route'; +import { readScheduledQueryGroupRoute } from './read_scheduled_query_group_route'; // import { updateScheduledQueryRoute } from './update_scheduled_query_route'; -export const initScheduledQueryRoutes = (router: IRouter, context: OsqueryAppContext) => { +export const initScheduledQueryGroupRoutes = (router: IRouter, context: OsqueryAppContext) => { // createScheduledQueryRoute(router); // deleteScheduledQueryRoute(router); - findScheduledQueryRoute(router, context); - readScheduledQueryRoute(router, context); + findScheduledQueryGroupRoute(router, context); + readScheduledQueryGroupRoute(router, context); // updateScheduledQueryRoute(router); }; diff --git a/x-pack/plugins/osquery/server/routes/scheduled_query/read_scheduled_query_route.ts b/x-pack/plugins/osquery/server/routes/scheduled_query_group/read_scheduled_query_group_route.ts similarity index 65% rename from x-pack/plugins/osquery/server/routes/scheduled_query/read_scheduled_query_route.ts rename to x-pack/plugins/osquery/server/routes/scheduled_query_group/read_scheduled_query_group_route.ts index 009374f6a2e9e..de8125aab5b29 100644 --- a/x-pack/plugins/osquery/server/routes/scheduled_query/read_scheduled_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/scheduled_query_group/read_scheduled_query_group_route.ts @@ -6,28 +6,34 @@ */ import { schema } from '@kbn/config-schema'; - +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; -export const readScheduledQueryRoute = (router: IRouter, osqueryContext: OsqueryAppContext) => { +export const readScheduledQueryGroupRoute = ( + router: IRouter, + osqueryContext: OsqueryAppContext +) => { router.get( { - path: '/internal/osquery/scheduled_query/{id}', + path: '/internal/osquery/scheduled_query_group/{id}', validate: { params: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-readPacks`] }, }, async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; const packagePolicyService = osqueryContext.service.getPackagePolicyService(); - // @ts-expect-error update types - const scheduledQuery = await packagePolicyService?.get(savedObjectsClient, request.params.id); + const scheduledQueryGroup = await packagePolicyService?.get( + savedObjectsClient, + // @ts-expect-error update types + request.params.id + ); return response.ok({ - // @ts-expect-error update types - body: scheduledQuery, + body: { item: scheduledQueryGroup }, }); } ); diff --git a/x-pack/plugins/osquery/server/routes/scheduled_query/update_scheduled_query_route.ts b/x-pack/plugins/osquery/server/routes/scheduled_query_group/update_scheduled_query_route.ts similarity index 92% rename from x-pack/plugins/osquery/server/routes/scheduled_query/update_scheduled_query_route.ts rename to x-pack/plugins/osquery/server/routes/scheduled_query_group/update_scheduled_query_route.ts index efb4f2990e942..2a6e7a33fcddd 100644 --- a/x-pack/plugins/osquery/server/routes/scheduled_query/update_scheduled_query_route.ts +++ b/x-pack/plugins/osquery/server/routes/scheduled_query_group/update_scheduled_query_route.ts @@ -6,7 +6,7 @@ */ import { schema } from '@kbn/config-schema'; - +import { PLUGIN_ID } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { savedQuerySavedObjectType } from '../../../common/types'; @@ -18,6 +18,7 @@ export const updateSavedQueryRoute = (router: IRouter) => { params: schema.object({}, { unknowns: 'allow' }), body: schema.object({}, { unknowns: 'allow' }), }, + options: { tags: [`access:${PLUGIN_ID}-writePacks`] }, }, async (context, request, response) => { const savedObjectsClient = context.core.savedObjects.client; diff --git a/x-pack/plugins/osquery/server/routes/status/create_status_route.ts b/x-pack/plugins/osquery/server/routes/status/create_status_route.ts index d7ea49c6152cd..0a527424f9f42 100644 --- a/x-pack/plugins/osquery/server/routes/status/create_status_route.ts +++ b/x-pack/plugins/osquery/server/routes/status/create_status_route.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { OSQUERY_INTEGRATION_NAME } from '../../../common'; +import { PLUGIN_ID, OSQUERY_INTEGRATION_NAME } from '../../../common'; import { IRouter } from '../../../../../../src/core/server'; import { OsqueryAppContext } from '../../lib/osquery_app_context_services'; @@ -14,16 +14,10 @@ export const createStatusRoute = (router: IRouter, osqueryContext: OsqueryAppCon { path: '/internal/osquery/status', validate: false, + options: { tags: [`access:${PLUGIN_ID}-read`] }, }, async (context, request, response) => { const soClient = context.core.savedObjects.client; - const isSuperUser = osqueryContext.security.authc - .getCurrentUser(request) - ?.roles.includes('superuser'); - - if (!isSuperUser) { - return response.ok({ body: undefined }); - } const packageInfo = await osqueryContext.service .getPackageService() diff --git a/x-pack/plugins/osquery/server/routes/usage/saved_object_mappings.ts b/x-pack/plugins/osquery/server/routes/usage/saved_object_mappings.ts index 92709f92d9e5f..603bcad87cf80 100644 --- a/x-pack/plugins/osquery/server/routes/usage/saved_object_mappings.ts +++ b/x-pack/plugins/osquery/server/routes/usage/saved_object_mappings.ts @@ -23,6 +23,6 @@ export const usageMetricSavedObjectMappings: SavedObjectsType['mappings'] = { export const usageMetricType: SavedObjectsType = { name: usageMetricSavedObjectType, hidden: false, - namespaceType: 'single', + namespaceType: 'agnostic', mappings: usageMetricSavedObjectMappings, }; diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts index 9fffb0726dce6..2fa9ee04ab534 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts @@ -23,7 +23,7 @@ import { OsqueryFactory } from './factory/types'; export const osquerySearchStrategyProvider = ( data: PluginStart ): ISearchStrategy, StrategyResponseType> => { - const es = data.search.getSearchStrategy(ENHANCED_ES_SEARCH_STRATEGY); + let es: typeof data.search.searchAsInternalUser; return { search: (request, options, deps) => { @@ -32,20 +32,35 @@ export const osquerySearchStrategyProvider = ( } const queryFactory: OsqueryFactory = osqueryFactory[request.factoryQueryType]; const dsl = queryFactory.buildDsl(request); - return es.search({ ...request, params: dsl }, options, deps).pipe( - map((response) => { - return { - ...response, - ...{ - rawResponse: shimHitsTotal(response.rawResponse), - }, - }; - }), - mergeMap((esSearchRes) => queryFactory.parse(request, esSearchRes)) - ); + + // use internal user for searching .fleet* indicies + es = dsl.index?.includes('fleet') + ? data.search.searchAsInternalUser + : data.search.getSearchStrategy(ENHANCED_ES_SEARCH_STRATEGY); + + return es + .search( + { + ...request, + params: dsl, + }, + options, + deps + ) + .pipe( + map((response) => { + return { + ...response, + ...{ + rawResponse: shimHitsTotal(response.rawResponse), + }, + }; + }), + mergeMap((esSearchRes) => queryFactory.parse(request, esSearchRes)) + ); }, cancel: async (id, options, deps) => { - if (es.cancel) { + if (es?.cancel) { return es.cancel(id, options, deps); } }, diff --git a/x-pack/plugins/osquery/server/usage/collector.ts b/x-pack/plugins/osquery/server/usage/collector.ts index 4432592a4e063..b04fc34e52453 100644 --- a/x-pack/plugins/osquery/server/usage/collector.ts +++ b/x-pack/plugins/osquery/server/usage/collector.ts @@ -11,11 +11,12 @@ import { getBeatUsage, getLiveQueryUsage, getPolicyLevelUsage } from './fetchers import { CollectorDependencies, usageSchema, UsageData } from './types'; export type RegisterCollector = (deps: CollectorDependencies) => void; -export async function getInternalSavedObjectsClient(core: CoreSetup) { - return core.getStartServices().then(async ([coreStart]) => { - return coreStart.savedObjects.createInternalRepository(); - }); -} +export const getInternalSavedObjectsClient = async ( + getStartServices: CoreSetup['getStartServices'] +) => { + const [coreStart] = await getStartServices(); + return new SavedObjectsClient(coreStart.savedObjects.createInternalRepository()); +}; export const registerCollector: RegisterCollector = ({ core, osqueryContext, usageCollection }) => { if (!usageCollection) { @@ -26,7 +27,8 @@ export const registerCollector: RegisterCollector = ({ core, osqueryContext, usa schema: usageSchema, isReady: () => true, fetch: async ({ esClient }: CollectorFetchContext): Promise => { - const savedObjectsClient = new SavedObjectsClient(await getInternalSavedObjectsClient(core)); + const savedObjectsClient = await getInternalSavedObjectsClient(core.getStartServices); + return { beat_metrics: { usage: await getBeatUsage(esClient), diff --git a/x-pack/test/api_integration/apis/features/features/features.ts b/x-pack/test/api_integration/apis/features/features/features.ts index 275626664bef0..6a6a0e13a1e1e 100644 --- a/x-pack/test/api_integration/apis/features/features/features.ts +++ b/x-pack/test/api_integration/apis/features/features/features.ts @@ -115,6 +115,7 @@ export default function ({ getService }: FtrProviderContext) { 'logs', 'maps', 'observabilityCases', + 'osquery', 'uptime', 'siem', 'fleet', diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index 2576a5eaf9bc9..bbb0fc60cb3ce 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -70,6 +70,19 @@ export default function ({ getService }: FtrProviderContext) { indexPatterns: ['all', 'read'], savedObjectsManagement: ['all', 'read'], timelion: ['all', 'read'], + osquery: [ + 'all', + 'read', + 'minimal_all', + 'minimal_read', + 'live_queries_all', + 'live_queries_read', + 'run_saved_queries', + 'saved_queries_all', + 'saved_queries_read', + 'packs_all', + 'packs_read', + ], }, reserved: ['ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'], }; diff --git a/x-pack/test/api_integration/apis/security/privileges_basic.ts b/x-pack/test/api_integration/apis/security/privileges_basic.ts index 25266da2cdfb3..dc00be028412b 100644 --- a/x-pack/test/api_integration/apis/security/privileges_basic.ts +++ b/x-pack/test/api_integration/apis/security/privileges_basic.ts @@ -37,6 +37,7 @@ export default function ({ getService }: FtrProviderContext) { logs: ['all', 'read'], uptime: ['all', 'read'], apm: ['all', 'read'], + osquery: ['all', 'read'], ml: ['all', 'read'], siem: ['all', 'read'], fleet: ['all', 'read'], diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts index afd8d1fb54cf6..aeaaf7fca1cb7 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/catalogue.ts @@ -53,6 +53,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { catalogueId !== 'ml' && catalogueId !== 'ml_file_data_visualizer' && catalogueId !== 'monitoring' && + catalogueId !== 'osquery' && !esFeatureExceptions.includes(catalogueId) ); expect(uiCapabilities.value!.catalogue).to.eql(expected); @@ -74,6 +75,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { 'appSearch', 'workplaceSearch', 'spaces', + 'osquery', ...esFeatureExceptions, ]; const expected = mapValues( diff --git a/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts b/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts index b87a6475526f7..6a6b618c2c8c8 100644 --- a/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts +++ b/x-pack/test/ui_capabilities/security_and_spaces/tests/nav_links.ts @@ -42,7 +42,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) { expect(uiCapabilities.success).to.be(true); expect(uiCapabilities.value).to.have.property('navLinks'); expect(uiCapabilities.value!.navLinks).to.eql( - navLinksBuilder.except('ml', 'monitoring') + navLinksBuilder.except('ml', 'monitoring', 'osquery') ); break; case 'everything_space_all at everything_space': @@ -57,7 +57,8 @@ export default function navLinksTests({ getService }: FtrProviderContext) { 'monitoring', 'enterpriseSearch', 'appSearch', - 'workplaceSearch' + 'workplaceSearch', + 'osquery' ) ); break; diff --git a/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts b/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts index d64b4f75e20a6..da4b26106afac 100644 --- a/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts +++ b/x-pack/test/ui_capabilities/security_only/tests/catalogue.ts @@ -53,6 +53,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { catalogueId !== 'ml' && catalogueId !== 'monitoring' && catalogueId !== 'ml_file_data_visualizer' && + catalogueId !== 'osquery' && !esFeatureExceptions.includes(catalogueId) ); expect(uiCapabilities.value!.catalogue).to.eql(expected); @@ -70,6 +71,7 @@ export default function catalogueTests({ getService }: FtrProviderContext) { 'enterpriseSearch', 'appSearch', 'workplaceSearch', + 'osquery', ...esFeatureExceptions, ]; const expected = mapValues( diff --git a/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts b/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts index 4d96532b83d4a..6a44b3d8f0b71 100644 --- a/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts +++ b/x-pack/test/ui_capabilities/security_only/tests/nav_links.ts @@ -42,7 +42,7 @@ export default function navLinksTests({ getService }: FtrProviderContext) { expect(uiCapabilities.success).to.be(true); expect(uiCapabilities.value).to.have.property('navLinks'); expect(uiCapabilities.value!.navLinks).to.eql( - navLinksBuilder.except('ml', 'monitoring') + navLinksBuilder.except('ml', 'monitoring', 'osquery') ); break; case 'read': @@ -55,7 +55,8 @@ export default function navLinksTests({ getService }: FtrProviderContext) { 'monitoring', 'enterpriseSearch', 'appSearch', - 'workplaceSearch' + 'workplaceSearch', + 'osquery' ) ); break;