mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Co-authored-by: Paul Tavares <paul.tavares@elastic.co> Co-authored-by: Josh Dover <1813008+joshdover@users.noreply.github.com> Co-authored-by: Paul Tavares <paul.tavares@elastic.co>
This commit is contained in:
parent
32cd3f4c20
commit
bb960f1b04
43 changed files with 736 additions and 240 deletions
|
@ -5,5 +5,5 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export const PLUGIN_ID = 'fleet';
|
||||
export const INTEGRATIONS_PLUGIN_ID = 'integrations';
|
||||
export const PLUGIN_ID = 'fleet' as const;
|
||||
export const INTEGRATIONS_PLUGIN_ID = 'integrations' as const;
|
||||
|
|
|
@ -52,6 +52,7 @@ export class HostedAgentPolicyRestrictionRelatedError extends IngestManagerError
|
|||
|
||||
export class FleetSetupError extends IngestManagerError {}
|
||||
export class GenerateServiceTokenError extends IngestManagerError {}
|
||||
export class FleetUnauthorizedError extends IngestManagerError {}
|
||||
|
||||
export class OutputUnauthorizedError extends IngestManagerError {}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import { FleetPlugin } from './plugin';
|
|||
|
||||
export type {
|
||||
AgentService,
|
||||
AgentClient,
|
||||
ESIndexPatternService,
|
||||
PackageService,
|
||||
AgentPolicyServiceInterface,
|
||||
|
@ -34,8 +35,9 @@ export type {
|
|||
PutPackagePolicyUpdateCallback,
|
||||
PostPackagePolicyDeleteCallback,
|
||||
PostPackagePolicyCreateCallback,
|
||||
FleetRequestHandlerContext,
|
||||
} from './types';
|
||||
export { AgentNotFoundError } from './errors';
|
||||
export { AgentNotFoundError, FleetUnauthorizedError } from './errors';
|
||||
|
||||
export const config: PluginConfigDescriptor = {
|
||||
exposeToBrowser: {
|
||||
|
|
|
@ -11,16 +11,19 @@ import {
|
|||
loggingSystemMock,
|
||||
savedObjectsServiceMock,
|
||||
coreMock,
|
||||
savedObjectsClientMock,
|
||||
} from '../../../../../src/core/server/mocks';
|
||||
import { dataPluginMock } from '../../../../../src/plugins/data/server/mocks';
|
||||
import { licensingMock } from '../../../../plugins/licensing/server/mocks';
|
||||
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
|
||||
import { securityMock } from '../../../security/server/mocks';
|
||||
import type { PackagePolicyServiceInterface } from '../services/package_policy';
|
||||
import type { AgentPolicyServiceInterface, AgentService } from '../services';
|
||||
import type { AgentPolicyServiceInterface, PackageService } from '../services';
|
||||
import type { FleetAppContext } from '../plugin';
|
||||
import { createMockTelemetryEventsSender } from '../telemetry/__mocks__';
|
||||
import type { FleetAuthz } from '../../common';
|
||||
import { agentServiceMock } from '../services/agents/agent_service.mock';
|
||||
import type { FleetRequestHandlerContext } from '../types';
|
||||
|
||||
// Export all mocks from artifacts
|
||||
export * from '../services/artifacts/mocks';
|
||||
|
@ -65,10 +68,26 @@ export const createAppContextStartContractMock = (): MockedFleetAppContext => {
|
|||
};
|
||||
};
|
||||
|
||||
export const createFleetRequestHandlerContextMock = (): jest.Mocked<
|
||||
FleetRequestHandlerContext['fleet']
|
||||
> => {
|
||||
return {
|
||||
authz: createFleetAuthzMock(),
|
||||
agentClient: {
|
||||
asCurrentUser: agentServiceMock.createClient(),
|
||||
asInternalUser: agentServiceMock.createClient(),
|
||||
},
|
||||
epm: {
|
||||
internalSoClient: savedObjectsClientMock.create(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
function createCoreRequestHandlerContextMock() {
|
||||
return {
|
||||
core: coreMock.createRequestHandlerContext(),
|
||||
licensing: licensingMock.createRequestHandlerContext(),
|
||||
fleet: createFleetRequestHandlerContextMock(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -113,33 +132,40 @@ export const createMockAgentPolicyService = (): jest.Mocked<AgentPolicyServiceIn
|
|||
/**
|
||||
* Creates a mock AgentService
|
||||
*/
|
||||
export const createMockAgentService = (): jest.Mocked<AgentService> => {
|
||||
export const createMockAgentService = () => agentServiceMock.create();
|
||||
|
||||
/**
|
||||
* Creates a mock AgentClient
|
||||
*/
|
||||
export const createMockAgentClient = () => agentServiceMock.createClient();
|
||||
|
||||
export const createMockPackageService = (): PackageService => {
|
||||
return {
|
||||
getAgentStatusById: jest.fn(),
|
||||
getAgentStatusForAgentPolicy: jest.fn(),
|
||||
getAgent: jest.fn(),
|
||||
listAgents: jest.fn(),
|
||||
getInstallation: jest.fn(),
|
||||
ensureInstalledPackage: jest.fn(),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates mock `authz` object
|
||||
*/
|
||||
export const fleetAuthzMock: FleetAuthz = {
|
||||
fleet: {
|
||||
all: true,
|
||||
setup: true,
|
||||
readEnrollmentTokens: true,
|
||||
},
|
||||
integrations: {
|
||||
readPackageInfo: true,
|
||||
readInstalledPackages: true,
|
||||
installPackages: true,
|
||||
upgradePackages: true,
|
||||
removePackages: true,
|
||||
readPackageSettings: true,
|
||||
writePackageSettings: true,
|
||||
readIntegrationPolicies: true,
|
||||
writeIntegrationPolicies: true,
|
||||
},
|
||||
export const createFleetAuthzMock = (): FleetAuthz => {
|
||||
return {
|
||||
fleet: {
|
||||
all: true,
|
||||
setup: true,
|
||||
readEnrollmentTokens: true,
|
||||
},
|
||||
integrations: {
|
||||
readPackageInfo: true,
|
||||
readInstalledPackages: true,
|
||||
installPackages: true,
|
||||
upgradePackages: true,
|
||||
removePackages: true,
|
||||
readPackageSettings: true,
|
||||
writePackageSettings: true,
|
||||
readIntegrationPolicies: true,
|
||||
writeIntegrationPolicies: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ import type {
|
|||
SavedObjectsServiceStart,
|
||||
HttpServiceSetup,
|
||||
KibanaRequest,
|
||||
ElasticsearchClient,
|
||||
} from 'kibana/server';
|
||||
import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
|
||||
|
||||
|
@ -71,13 +72,8 @@ import {
|
|||
ESIndexPatternSavedObjectService,
|
||||
agentPolicyService,
|
||||
packagePolicyService,
|
||||
AgentServiceImpl,
|
||||
} from './services';
|
||||
import {
|
||||
getAgentStatusById,
|
||||
getAgentStatusForAgentPolicy,
|
||||
getAgentsByKuery,
|
||||
getAgentById,
|
||||
} from './services/agents';
|
||||
import { registerFleetUsageCollector } from './collectors/register';
|
||||
import { getInstallation, ensureInstalledPackage } from './services/epm/packages';
|
||||
import { getAuthzFromRequest, RouterWrappers } from './routes/security';
|
||||
|
@ -187,6 +183,8 @@ export class FleetPlugin
|
|||
private encryptedSavedObjectsSetup?: EncryptedSavedObjectsPluginSetup;
|
||||
private readonly telemetryEventsSender: TelemetryEventsSender;
|
||||
|
||||
private agentService?: AgentService;
|
||||
|
||||
constructor(private readonly initializerContext: PluginInitializerContext) {
|
||||
this.config$ = this.initializerContext.config.create<FleetConfigType>();
|
||||
this.isProductionMode = this.initializerContext.env.mode.prod;
|
||||
|
@ -212,7 +210,7 @@ export class FleetPlugin
|
|||
// TODO: Flesh out privileges
|
||||
if (deps.features) {
|
||||
deps.features.registerKibanaFeature({
|
||||
id: 'fleet',
|
||||
id: PLUGIN_ID,
|
||||
name: 'Fleet and Integrations',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
app: [PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, 'kibana'],
|
||||
|
@ -237,7 +235,7 @@ export class FleetPlugin
|
|||
},
|
||||
privileges: {
|
||||
all: {
|
||||
api: [`fleet-read`, `fleet-all`, `integrations-all`, `integrations-read`],
|
||||
api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-all`, `integrations-all`, `integrations-read`],
|
||||
app: [PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, 'kibana'],
|
||||
catalogue: ['fleet'],
|
||||
savedObject: {
|
||||
|
@ -247,7 +245,7 @@ export class FleetPlugin
|
|||
ui: ['show', 'read', 'write'],
|
||||
},
|
||||
read: {
|
||||
api: [`fleet-read`, `integrations-read`],
|
||||
api: [`${PLUGIN_ID}-read`, `integrations-read`],
|
||||
app: [PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, 'kibana'],
|
||||
catalogue: ['fleet'], // TODO: check if this is actually available to read user
|
||||
savedObject: {
|
||||
|
@ -260,19 +258,33 @@ export class FleetPlugin
|
|||
});
|
||||
}
|
||||
|
||||
core.http.registerRouteHandlerContext<FleetRequestHandlerContext, 'fleet'>(
|
||||
'fleet',
|
||||
async (coreContext, request) => ({
|
||||
authz: await getAuthzFromRequest(request),
|
||||
epm: {
|
||||
// Use a lazy getter to avoid constructing this client when not used by a request handler
|
||||
get internalSoClient() {
|
||||
return appContextService
|
||||
.getSavedObjects()
|
||||
.getScopedClient(request, { excludedWrappers: ['security'] });
|
||||
core.http.registerRouteHandlerContext<FleetRequestHandlerContext, typeof PLUGIN_ID>(
|
||||
PLUGIN_ID,
|
||||
async (context, request) => {
|
||||
const plugin = this;
|
||||
|
||||
return {
|
||||
get agentClient() {
|
||||
const agentService = plugin.setupAgentService(
|
||||
context.core.elasticsearch.client.asInternalUser
|
||||
);
|
||||
|
||||
return {
|
||||
asCurrentUser: agentService.asScoped(request),
|
||||
asInternalUser: agentService.asInternalUser,
|
||||
};
|
||||
},
|
||||
},
|
||||
})
|
||||
authz: await getAuthzFromRequest(request),
|
||||
epm: {
|
||||
// Use a lazy getter to avoid constructing this client when not used by a request handler
|
||||
get internalSoClient() {
|
||||
return appContextService
|
||||
.getSavedObjects()
|
||||
.getScopedClient(request, { excludedWrappers: ['security'] });
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const router: FleetRouter = core.http.createRouter<FleetRequestHandlerContext>();
|
||||
|
@ -365,12 +377,7 @@ export class FleetPlugin
|
|||
getInstallation,
|
||||
ensureInstalledPackage,
|
||||
},
|
||||
agentService: {
|
||||
getAgent: getAgentById,
|
||||
listAgents: getAgentsByKuery,
|
||||
getAgentStatusById,
|
||||
getAgentStatusForAgentPolicy,
|
||||
},
|
||||
agentService: this.setupAgentService(core.elasticsearch.client.asInternalUser),
|
||||
agentPolicyService: {
|
||||
get: agentPolicyService.get,
|
||||
list: agentPolicyService.list,
|
||||
|
@ -394,4 +401,13 @@ export class FleetPlugin
|
|||
licenseService.stop();
|
||||
this.telemetryEventsSender.stop();
|
||||
}
|
||||
|
||||
private setupAgentService(internalEsClient: ElasticsearchClient): AgentService {
|
||||
if (this.agentService) {
|
||||
return this.agentService;
|
||||
}
|
||||
|
||||
this.agentService = new AgentServiceImpl(internalEsClient);
|
||||
return this.agentService;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ function checkSecurityEnabled() {
|
|||
return appContextService.hasSecurity() && appContextService.getSecurityLicense().isEnabled();
|
||||
}
|
||||
|
||||
function checkSuperuser(req: KibanaRequest) {
|
||||
export function checkSuperuser(req: KibanaRequest) {
|
||||
if (!checkSecurityEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ import { httpServerMock, savedObjectsClientMock } from 'src/core/server/mocks';
|
|||
|
||||
import type { PostFleetSetupResponse } from '../../../common';
|
||||
import { RegistryError } from '../../errors';
|
||||
import { createAppContextStartContractMock, xpackMocks, fleetAuthzMock } from '../../mocks';
|
||||
import { createAppContextStartContractMock, xpackMocks, createFleetAuthzMock } from '../../mocks';
|
||||
import { agentServiceMock } from '../../services/agents/agent_service.mock';
|
||||
import { appContextService } from '../../services/app_context';
|
||||
import { setupFleet } from '../../services/setup';
|
||||
import type { FleetRequestHandlerContext } from '../../types';
|
||||
|
@ -34,7 +35,11 @@ describe('FleetSetupHandler', () => {
|
|||
context = {
|
||||
...xpackMocks.createRequestHandlerContext(),
|
||||
fleet: {
|
||||
authz: fleetAuthzMock,
|
||||
agentClient: {
|
||||
asCurrentUser: agentServiceMock.createClient(),
|
||||
asInternalUser: agentServiceMock.createClient(),
|
||||
},
|
||||
authz: createFleetAuthzMock(),
|
||||
epm: {
|
||||
internalSoClient: savedObjectsClientMock.create(),
|
||||
},
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 type { AgentClient, AgentService } from './agent_service';
|
||||
|
||||
const createClientMock = (): jest.Mocked<AgentClient> => ({
|
||||
getAgent: jest.fn(),
|
||||
getAgentStatusById: jest.fn(),
|
||||
getAgentStatusForAgentPolicy: jest.fn(),
|
||||
listAgents: jest.fn(),
|
||||
});
|
||||
|
||||
const createServiceMock = (): jest.Mocked<AgentService> => ({
|
||||
asInternalUser: createClientMock(),
|
||||
asScoped: jest.fn().mockReturnValue(createClientMock()),
|
||||
});
|
||||
|
||||
export const agentServiceMock = {
|
||||
createClient: createClientMock,
|
||||
create: createServiceMock,
|
||||
};
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
jest.mock('../../routes/security');
|
||||
jest.mock('./crud');
|
||||
jest.mock('./status');
|
||||
|
||||
import type { ElasticsearchClient } from '../../../../../../src/core/server';
|
||||
import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks';
|
||||
import { FleetUnauthorizedError } from '../../errors';
|
||||
|
||||
import { checkSuperuser } from '../../routes/security';
|
||||
|
||||
import type { AgentClient } from './agent_service';
|
||||
import { AgentServiceImpl } from './agent_service';
|
||||
import { getAgentsByKuery, getAgentById } from './crud';
|
||||
import { getAgentStatusById, getAgentStatusForAgentPolicy } from './status';
|
||||
|
||||
const mockCheckSuperuser = checkSuperuser as jest.Mock<boolean>;
|
||||
const mockGetAgentsByKuery = getAgentsByKuery as jest.Mock;
|
||||
const mockGetAgentById = getAgentById as jest.Mock;
|
||||
const mockGetAgentStatusById = getAgentStatusById as jest.Mock;
|
||||
const mockGetAgentStatusForAgentPolicy = getAgentStatusForAgentPolicy as jest.Mock;
|
||||
|
||||
describe('AgentService', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('asScoped', () => {
|
||||
describe('without required privilege', () => {
|
||||
const agentClient = new AgentServiceImpl(
|
||||
elasticsearchServiceMock.createElasticsearchClient()
|
||||
).asScoped(httpServerMock.createKibanaRequest());
|
||||
|
||||
beforeEach(() => mockCheckSuperuser.mockReturnValue(false));
|
||||
|
||||
it('rejects on listAgents', async () => {
|
||||
await expect(agentClient.listAgents({ showInactive: true })).rejects.toThrowError(
|
||||
new FleetUnauthorizedError(
|
||||
`User does not have adequate permissions to access Fleet agents.`
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects on getAgent', async () => {
|
||||
await expect(agentClient.getAgent('foo')).rejects.toThrowError(
|
||||
new FleetUnauthorizedError(
|
||||
`User does not have adequate permissions to access Fleet agents.`
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects on getAgentStatusById', async () => {
|
||||
await expect(agentClient.getAgentStatusById('foo')).rejects.toThrowError(
|
||||
new FleetUnauthorizedError(
|
||||
`User does not have adequate permissions to access Fleet agents.`
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects on getAgentStatusForAgentPolicy', async () => {
|
||||
await expect(agentClient.getAgentStatusForAgentPolicy()).rejects.toThrowError(
|
||||
new FleetUnauthorizedError(
|
||||
`User does not have adequate permissions to access Fleet agents.`
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with required privilege', () => {
|
||||
const mockEsClient = elasticsearchServiceMock.createElasticsearchClient();
|
||||
const agentClient = new AgentServiceImpl(mockEsClient).asScoped(
|
||||
httpServerMock.createKibanaRequest()
|
||||
);
|
||||
|
||||
beforeEach(() => mockCheckSuperuser.mockReturnValue(true));
|
||||
|
||||
expectApisToCallServicesSuccessfully(mockEsClient, agentClient);
|
||||
});
|
||||
});
|
||||
|
||||
describe('asInternalUser', () => {
|
||||
const mockEsClient = elasticsearchServiceMock.createElasticsearchClient();
|
||||
const agentClient = new AgentServiceImpl(mockEsClient).asInternalUser;
|
||||
|
||||
expectApisToCallServicesSuccessfully(mockEsClient, agentClient);
|
||||
});
|
||||
});
|
||||
|
||||
function expectApisToCallServicesSuccessfully(
|
||||
mockEsClient: ElasticsearchClient,
|
||||
agentClient: AgentClient
|
||||
) {
|
||||
test('client.listAgents calls getAgentsByKuery and returns results', async () => {
|
||||
mockGetAgentsByKuery.mockResolvedValue('getAgentsByKuery success');
|
||||
await expect(agentClient.listAgents({ showInactive: true })).resolves.toEqual(
|
||||
'getAgentsByKuery success'
|
||||
);
|
||||
expect(mockGetAgentsByKuery).toHaveBeenCalledWith(mockEsClient, { showInactive: true });
|
||||
});
|
||||
|
||||
test('client.getAgent calls getAgentById and returns results', async () => {
|
||||
mockGetAgentById.mockResolvedValue('getAgentById success');
|
||||
await expect(agentClient.getAgent('foo-id')).resolves.toEqual('getAgentById success');
|
||||
expect(mockGetAgentById).toHaveBeenCalledWith(mockEsClient, 'foo-id');
|
||||
});
|
||||
|
||||
test('client.getAgentStatusById calls getAgentStatusById and returns results', async () => {
|
||||
mockGetAgentStatusById.mockResolvedValue('getAgentStatusById success');
|
||||
await expect(agentClient.getAgentStatusById('foo-id')).resolves.toEqual(
|
||||
'getAgentStatusById success'
|
||||
);
|
||||
expect(mockGetAgentStatusById).toHaveBeenCalledWith(mockEsClient, 'foo-id');
|
||||
});
|
||||
|
||||
test('client.getAgentStatusForAgentPolicy calls getAgentStatusForAgentPolicy and returns results', async () => {
|
||||
mockGetAgentStatusForAgentPolicy.mockResolvedValue('getAgentStatusForAgentPolicy success');
|
||||
await expect(agentClient.getAgentStatusForAgentPolicy('foo-id', 'foo-filter')).resolves.toEqual(
|
||||
'getAgentStatusForAgentPolicy success'
|
||||
);
|
||||
expect(mockGetAgentStatusForAgentPolicy).toHaveBeenCalledWith(
|
||||
mockEsClient,
|
||||
'foo-id',
|
||||
'foo-filter'
|
||||
);
|
||||
});
|
||||
}
|
140
x-pack/plugins/fleet/server/services/agents/agent_service.ts
Normal file
140
x-pack/plugins/fleet/server/services/agents/agent_service.ts
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import type { ElasticsearchClient, KibanaRequest } from 'kibana/server';
|
||||
|
||||
import type { AgentStatus, ListWithKuery } from '../../types';
|
||||
import type { Agent, GetAgentStatusResponse } from '../../../common';
|
||||
|
||||
import { checkSuperuser } from '../../routes/security';
|
||||
|
||||
import { FleetUnauthorizedError } from '../../errors';
|
||||
|
||||
import { getAgentsByKuery, getAgentById } from './crud';
|
||||
import { getAgentStatusById, getAgentStatusForAgentPolicy } from './status';
|
||||
|
||||
/**
|
||||
* A service for interacting with Agent data. See {@link AgentClient} for more information.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AgentService {
|
||||
/**
|
||||
* Should be used for end-user requests to Kibana. APIs will return errors if user does not have appropriate access.
|
||||
*/
|
||||
asScoped(req: KibanaRequest): AgentClient;
|
||||
|
||||
/**
|
||||
* Only use for server-side usages (eg. telemetry), should not be used for end users unless an explicit authz check is
|
||||
* done.
|
||||
*/
|
||||
asInternalUser: AgentClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* A client for interacting with data about an Agent
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AgentClient {
|
||||
/**
|
||||
* Get an Agent by id
|
||||
*/
|
||||
getAgent(agentId: string): Promise<Agent>;
|
||||
|
||||
/**
|
||||
* Return the status by the Agent's id
|
||||
*/
|
||||
getAgentStatusById(agentId: string): Promise<AgentStatus>;
|
||||
|
||||
/**
|
||||
* Return the status by the Agent's Policy id
|
||||
*/
|
||||
getAgentStatusForAgentPolicy(
|
||||
agentPolicyId?: string,
|
||||
filterKuery?: string
|
||||
): Promise<GetAgentStatusResponse['results']>;
|
||||
|
||||
/**
|
||||
* List agents
|
||||
*/
|
||||
listAgents(
|
||||
options: ListWithKuery & {
|
||||
showInactive: boolean;
|
||||
}
|
||||
): Promise<{
|
||||
agents: Agent[];
|
||||
total: number;
|
||||
page: number;
|
||||
perPage: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class AgentClientImpl implements AgentClient {
|
||||
constructor(
|
||||
private readonly internalEsClient: ElasticsearchClient,
|
||||
private readonly preflightCheck?: () => void | Promise<void>
|
||||
) {}
|
||||
|
||||
public async listAgents(
|
||||
options: ListWithKuery & {
|
||||
showInactive: boolean;
|
||||
}
|
||||
) {
|
||||
await this.#runPreflight();
|
||||
return getAgentsByKuery(this.internalEsClient, options);
|
||||
}
|
||||
|
||||
public async getAgent(agentId: string) {
|
||||
await this.#runPreflight();
|
||||
return getAgentById(this.internalEsClient, agentId);
|
||||
}
|
||||
|
||||
public async getAgentStatusById(agentId: string) {
|
||||
await this.#runPreflight();
|
||||
return getAgentStatusById(this.internalEsClient, agentId);
|
||||
}
|
||||
|
||||
public async getAgentStatusForAgentPolicy(agentPolicyId?: string, filterKuery?: string) {
|
||||
await this.#runPreflight();
|
||||
return getAgentStatusForAgentPolicy(this.internalEsClient, agentPolicyId, filterKuery);
|
||||
}
|
||||
|
||||
#runPreflight = async () => {
|
||||
if (this.preflightCheck) {
|
||||
return this.preflightCheck();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class AgentServiceImpl implements AgentService {
|
||||
constructor(private readonly internalEsClient: ElasticsearchClient) {}
|
||||
|
||||
public asScoped(req: KibanaRequest) {
|
||||
const preflightCheck = () => {
|
||||
if (!checkSuperuser(req)) {
|
||||
throw new FleetUnauthorizedError(
|
||||
`User does not have adequate permissions to access Fleet agents.`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return new AgentClientImpl(this.internalEsClient, preflightCheck);
|
||||
}
|
||||
|
||||
public get asInternalUser() {
|
||||
return new AgentClientImpl(this.internalEsClient);
|
||||
}
|
||||
}
|
|
@ -13,3 +13,5 @@ export * from './update';
|
|||
export * from './actions';
|
||||
export * from './reassign';
|
||||
export * from './setup';
|
||||
export { AgentServiceImpl } from './agent_service';
|
||||
export type { AgentClient, AgentService } from './agent_service';
|
||||
|
|
|
@ -5,13 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server';
|
||||
import type { SavedObjectsClientContract } from 'kibana/server';
|
||||
|
||||
import type { AgentStatus } from '../types';
|
||||
|
||||
import type { GetAgentStatusResponse } from '../../common';
|
||||
|
||||
import type { getAgentById, getAgentsByKuery } from './agents';
|
||||
import type { agentPolicyService } from './agent_policy';
|
||||
import * as settingsService from './settings';
|
||||
import type { getInstallation, ensureInstalledPackage } from './epm/packages';
|
||||
|
@ -39,32 +34,6 @@ export interface PackageService {
|
|||
ensureInstalledPackage: typeof ensureInstalledPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* A service that provides exported functions that return information about an Agent
|
||||
*/
|
||||
export interface AgentService {
|
||||
/**
|
||||
* Get an Agent by id
|
||||
*/
|
||||
getAgent: typeof getAgentById;
|
||||
/**
|
||||
* Return the status by the Agent's id
|
||||
*/
|
||||
getAgentStatusById(esClient: ElasticsearchClient, agentId: string): Promise<AgentStatus>;
|
||||
/**
|
||||
* Return the status by the Agent's Policy id
|
||||
*/
|
||||
getAgentStatusForAgentPolicy(
|
||||
esClient: ElasticsearchClient,
|
||||
agentPolicyId?: string,
|
||||
filterKuery?: string
|
||||
): Promise<GetAgentStatusResponse['results']>;
|
||||
/**
|
||||
* List agents
|
||||
*/
|
||||
listAgents: typeof getAgentsByKuery;
|
||||
}
|
||||
|
||||
export interface AgentPolicyServiceInterface {
|
||||
get: typeof agentPolicyService['get'];
|
||||
list: typeof agentPolicyService['list'];
|
||||
|
@ -73,6 +42,10 @@ export interface AgentPolicyServiceInterface {
|
|||
getByIds: typeof agentPolicyService['getByIDs'];
|
||||
}
|
||||
|
||||
// Agent services
|
||||
export { AgentServiceImpl } from './agents';
|
||||
export type { AgentClient, AgentService } from './agents';
|
||||
|
||||
// Saved object services
|
||||
export { agentPolicyService } from './agent_policy';
|
||||
export { packagePolicyService } from './package_policy';
|
||||
|
|
|
@ -14,11 +14,18 @@ import type {
|
|||
IRouter,
|
||||
} from '../../../../../src/core/server';
|
||||
import type { FleetAuthz } from '../../common/authz';
|
||||
import type { AgentClient } from '../services';
|
||||
|
||||
/** @internal */
|
||||
export interface FleetRequestHandlerContext extends RequestHandlerContext {
|
||||
fleet: {
|
||||
/** {@link FleetAuthz} */
|
||||
authz: FleetAuthz;
|
||||
/** {@link AgentClient} */
|
||||
agentClient: {
|
||||
asCurrentUser: AgentClient;
|
||||
asInternalUser: AgentClient;
|
||||
};
|
||||
epm: {
|
||||
/**
|
||||
* Saved Objects client configured to use kibana_system privileges instead of end-user privileges. Should only be
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { uniq } from 'lodash';
|
||||
import type { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server';
|
||||
import type { KibanaRequest, SavedObjectsClientContract } from 'src/core/server';
|
||||
import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../fleet/common';
|
||||
import { OSQUERY_INTEGRATION_NAME } from '../../common';
|
||||
import { OsqueryAppContext } from './osquery_app_context_services';
|
||||
|
@ -34,7 +34,7 @@ const aggregateResults = async (
|
|||
};
|
||||
|
||||
export const parseAgentSelection = async (
|
||||
esClient: ElasticsearchClient,
|
||||
request: KibanaRequest,
|
||||
soClient: SavedObjectsClientContract,
|
||||
context: OsqueryAppContext,
|
||||
agentSelection: AgentSelection
|
||||
|
@ -42,7 +42,7 @@ export const parseAgentSelection = async (
|
|||
const selectedAgents: Set<string> = new Set();
|
||||
const addAgent = selectedAgents.add.bind(selectedAgents);
|
||||
const { allAgentsSelected, platformsSelected, policiesSelected, agents } = agentSelection;
|
||||
const agentService = context.service.getAgentService();
|
||||
const agentService = context.service.getAgentService()?.asScoped(request);
|
||||
const packagePolicyService = context.service.getPackagePolicyService();
|
||||
const kueryFragments = [];
|
||||
|
||||
|
@ -59,7 +59,7 @@ export const parseAgentSelection = async (
|
|||
if (allAgentsSelected) {
|
||||
const kuery = kueryFragments.join(' and ');
|
||||
const fetchedAgents = await aggregateResults(async (page, perPage) => {
|
||||
const res = await agentService.listAgents(esClient, {
|
||||
const res = await agentService.listAgents({
|
||||
perPage,
|
||||
page,
|
||||
kuery,
|
||||
|
@ -80,7 +80,7 @@ export const parseAgentSelection = async (
|
|||
kueryFragments.push(`(${groupFragments.join(' or ')})`);
|
||||
const kuery = kueryFragments.join(' and ');
|
||||
const fetchedAgents = await aggregateResults(async (page, perPage) => {
|
||||
const res = await agentService.listAgents(esClient, {
|
||||
const res = await agentService.listAgents({
|
||||
perPage,
|
||||
page,
|
||||
kuery,
|
||||
|
|
|
@ -46,7 +46,7 @@ export const createActionRoute = (router: IRouter, osqueryContext: OsqueryAppCon
|
|||
|
||||
const { agentSelection } = request.body as { agentSelection: AgentSelection };
|
||||
const selectedAgents = await parseAgentSelection(
|
||||
esClient,
|
||||
request,
|
||||
soClient,
|
||||
osqueryContext,
|
||||
agentSelection
|
||||
|
|
|
@ -30,7 +30,6 @@ export const getAgentPoliciesRoute = (router: IRouter, osqueryContext: OsqueryAp
|
|||
},
|
||||
async (context, request, response) => {
|
||||
const soClient = context.core.savedObjects.client;
|
||||
const esClient = context.core.elasticsearch.client.asInternalUser;
|
||||
const agentService = osqueryContext.service.getAgentService();
|
||||
const agentPolicyService = osqueryContext.service.getAgentPolicyService();
|
||||
const packagePolicyService = osqueryContext.service.getPackagePolicyService();
|
||||
|
@ -51,7 +50,8 @@ export const getAgentPoliciesRoute = (router: IRouter, osqueryContext: OsqueryAp
|
|||
agentPolicies,
|
||||
(agentPolicy: GetAgentPoliciesResponseItem) =>
|
||||
agentService
|
||||
?.getAgentStatusForAgentPolicy(esClient, agentPolicy.id)
|
||||
?.asScoped(request)
|
||||
.getAgentStatusForAgentPolicy(agentPolicy.id)
|
||||
.then(({ total: agentTotal }) => (agentPolicy.agents = agentTotal)),
|
||||
{ concurrency: 10 }
|
||||
);
|
||||
|
|
|
@ -28,11 +28,10 @@ export const getAgentStatusForAgentPolicyRoute = (
|
|||
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);
|
||||
?.asScoped(request)
|
||||
.getAgentStatusForAgentPolicy(request.query.policyId, request.query.kuery);
|
||||
|
||||
if (!results) {
|
||||
return response.ok({ body: {} });
|
||||
|
|
|
@ -20,14 +20,13 @@ export const getAgentsRoute = (router: IRouter, osqueryContext: OsqueryAppContex
|
|||
options: { tags: [`access:${PLUGIN_ID}-read`] },
|
||||
},
|
||||
async (context, request, response) => {
|
||||
const esClient = context.core.elasticsearch.client.asInternalUser;
|
||||
|
||||
let agents;
|
||||
try {
|
||||
agents = await osqueryContext.service
|
||||
.getAgentService()
|
||||
?.asScoped(request)
|
||||
// @ts-expect-error update types
|
||||
?.listAgents(esClient, request.query);
|
||||
.listAgents(request.query);
|
||||
} catch (error) {
|
||||
return response.badRequest({ body: error });
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ import {
|
|||
EndpointAppContentServicesNotSetUpError,
|
||||
EndpointAppContentServicesNotStartedError,
|
||||
} from './errors';
|
||||
import {
|
||||
EndpointFleetServicesFactory,
|
||||
EndpointScopedFleetServicesInterface,
|
||||
} from './services/endpoint_fleet_services';
|
||||
|
||||
export interface EndpointAppContextServiceSetupContract {
|
||||
securitySolutionRequestContextFactory: IRequestContextFactory;
|
||||
|
@ -64,6 +68,7 @@ export type EndpointAppContextServiceStartContract = Partial<
|
|||
export class EndpointAppContextService {
|
||||
private setupDependencies: EndpointAppContextServiceSetupContract | null = null;
|
||||
private startDependencies: EndpointAppContextServiceStartContract | null = null;
|
||||
private fleetServicesFactory: EndpointFleetServicesFactory | null = null;
|
||||
public security: SecurityPluginStart | undefined;
|
||||
|
||||
public setup(dependencies: EndpointAppContextServiceSetupContract) {
|
||||
|
@ -78,6 +83,17 @@ export class EndpointAppContextService {
|
|||
this.startDependencies = dependencies;
|
||||
this.security = dependencies.security;
|
||||
|
||||
// let's try to avoid turning off eslint's Forbidden non-null assertion rule
|
||||
const { agentService, agentPolicyService, packagePolicyService, packageService } =
|
||||
dependencies as Required<EndpointAppContextServiceStartContract>;
|
||||
|
||||
this.fleetServicesFactory = new EndpointFleetServicesFactory({
|
||||
agentService,
|
||||
agentPolicyService,
|
||||
packagePolicyService,
|
||||
packageService,
|
||||
});
|
||||
|
||||
if (dependencies.registerIngestCallback && dependencies.manifestManager) {
|
||||
dependencies.registerIngestCallback(
|
||||
'packagePolicyCreate',
|
||||
|
@ -119,10 +135,20 @@ export class EndpointAppContextService {
|
|||
return this.startDependencies.endpointMetadataService;
|
||||
}
|
||||
|
||||
public getScopedFleetServices(req: KibanaRequest): EndpointScopedFleetServicesInterface {
|
||||
if (this.fleetServicesFactory === null) {
|
||||
throw new EndpointAppContentServicesNotStartedError();
|
||||
}
|
||||
|
||||
return this.fleetServicesFactory.asScoped(req);
|
||||
}
|
||||
|
||||
/** @deprecated use `getScopedFleetServices()` instead */
|
||||
public getAgentService(): AgentService | undefined {
|
||||
return this.startDependencies?.agentService;
|
||||
}
|
||||
|
||||
/** @deprecated use `getScopedFleetServices()` instead */
|
||||
public getPackagePolicyService(): PackagePolicyServiceInterface {
|
||||
if (!this.startDependencies?.packagePolicyService) {
|
||||
throw new EndpointAppContentServicesNotStartedError();
|
||||
|
@ -130,6 +156,7 @@ export class EndpointAppContextService {
|
|||
return this.startDependencies?.packagePolicyService;
|
||||
}
|
||||
|
||||
/** @deprecated use `getScopedFleetServices()` instead */
|
||||
public getAgentPolicyService(): AgentPolicyServiceInterface | undefined {
|
||||
return this.startDependencies?.agentPolicyService;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
createMockAgentPolicyService,
|
||||
createMockAgentService,
|
||||
createArtifactsClientMock,
|
||||
fleetAuthzMock,
|
||||
createFleetAuthzMock,
|
||||
} from '../../../fleet/server/mocks';
|
||||
import { createMockConfig } from '../lib/detection_engine/routes/__mocks__';
|
||||
import {
|
||||
|
@ -96,7 +96,6 @@ export const createMockEndpointAppContextServiceStartContract =
|
|||
const packagePolicyService = createPackagePolicyServiceMock();
|
||||
const endpointMetadataService = new EndpointMetadataService(
|
||||
savedObjectsStart,
|
||||
agentService,
|
||||
agentPolicyService,
|
||||
packagePolicyService,
|
||||
logger
|
||||
|
@ -155,7 +154,7 @@ export const createMockPackageService = (): jest.Mocked<PackageService> => {
|
|||
export const createMockFleetStartContract = (indexPattern: string): FleetStartContract => {
|
||||
return {
|
||||
authz: {
|
||||
fromRequest: jest.fn().mockResolvedValue(fleetAuthzMock),
|
||||
fromRequest: jest.fn().mockResolvedValue(createFleetAuthzMock()),
|
||||
},
|
||||
fleetSetupCompleted: jest.fn().mockResolvedValue(undefined),
|
||||
esIndexPatternService: {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { HostStatus } from '../../../../common/endpoint/types';
|
|||
import { createMockMetadataRequestContext } from '../../mocks';
|
||||
import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data';
|
||||
import { enrichHostMetadata, MetadataRequestContext } from './handlers';
|
||||
import { AgentClient } from '../../../../../fleet/server';
|
||||
|
||||
describe('test document enrichment', () => {
|
||||
let metaReqCtx: jest.Mocked<MetadataRequestContext>;
|
||||
|
@ -23,11 +24,9 @@ describe('test document enrichment', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
statusFn = jest.fn();
|
||||
(metaReqCtx.endpointAppContextService.getAgentService as jest.Mock).mockImplementation(() => {
|
||||
return {
|
||||
getAgentStatusById: statusFn,
|
||||
};
|
||||
});
|
||||
metaReqCtx.requestHandlerContext!.fleet!.agentClient.asCurrentUser = {
|
||||
getAgentStatusById: statusFn,
|
||||
} as unknown as AgentClient;
|
||||
});
|
||||
|
||||
it('should return host healthy for online agent', async () => {
|
||||
|
@ -87,12 +86,10 @@ describe('test document enrichment', () => {
|
|||
beforeEach(() => {
|
||||
agentMock = jest.fn();
|
||||
agentPolicyMock = jest.fn();
|
||||
(metaReqCtx.endpointAppContextService.getAgentService as jest.Mock).mockImplementation(() => {
|
||||
return {
|
||||
getAgent: agentMock,
|
||||
getAgentStatusById: jest.fn(),
|
||||
};
|
||||
});
|
||||
metaReqCtx.requestHandlerContext!.fleet!.agentClient.asCurrentUser = {
|
||||
getAgent: agentMock,
|
||||
getAgentStatusById: jest.fn(),
|
||||
} as unknown as AgentClient;
|
||||
(metaReqCtx.endpointAppContextService.getAgentPolicyService as jest.Mock).mockImplementation(
|
||||
() => {
|
||||
return {
|
||||
|
|
|
@ -35,7 +35,6 @@ import { EndpointAppContextService } from '../../endpoint_app_context_services';
|
|||
import { fleetAgentStatusToEndpointHostStatus } from '../../utils';
|
||||
import { queryResponseToHostListResult } from './support/query_strategies';
|
||||
import { NotFoundError } from '../../errors';
|
||||
import { EndpointError } from '../../../../common/endpoint/errors';
|
||||
import { EndpointHostUnEnrolledError } from '../../services/metadata';
|
||||
import { CustomHttpRequestError } from '../../../utils/custom_http_request_error';
|
||||
import { GetMetadataListRequestQuery } from '../../../../common/endpoint/schema/metadata';
|
||||
|
@ -43,6 +42,7 @@ import {
|
|||
ENDPOINT_DEFAULT_PAGE,
|
||||
ENDPOINT_DEFAULT_PAGE_SIZE,
|
||||
} from '../../../../common/endpoint/constants';
|
||||
import { EndpointFleetServicesInterface } from '../../services/endpoint_fleet_services';
|
||||
|
||||
export interface MetadataRequestContext {
|
||||
esClient?: IScopedClusterClient;
|
||||
|
@ -93,9 +93,7 @@ export const getMetadataListRequestHandler = function (
|
|||
> {
|
||||
return async (context, request, response) => {
|
||||
const endpointMetadataService = endpointAppContext.service.getEndpointMetadataService();
|
||||
if (!endpointMetadataService) {
|
||||
throw new EndpointError('endpoint metadata service not available');
|
||||
}
|
||||
const fleetServices = endpointAppContext.service.getScopedFleetServices(request);
|
||||
|
||||
let doesUnitedIndexExist = false;
|
||||
let didUnitedIndexError = false;
|
||||
|
@ -118,18 +116,25 @@ export const getMetadataListRequestHandler = function (
|
|||
// If no unified Index present, then perform a search using the legacy approach
|
||||
if (!doesUnitedIndexExist || didUnitedIndexError) {
|
||||
const endpointPolicies = await getAllEndpointPackagePolicies(
|
||||
endpointAppContext.service.getPackagePolicyService(),
|
||||
fleetServices.packagePolicy,
|
||||
context.core.savedObjects.client
|
||||
);
|
||||
|
||||
const pagingProperties = await getPagingProperties(request, endpointAppContext);
|
||||
|
||||
body = await legacyListMetadataQuery(context, endpointAppContext, logger, endpointPolicies, {
|
||||
page: pagingProperties.pageIndex,
|
||||
pageSize: pagingProperties.pageSize,
|
||||
kuery: request?.body?.filters?.kql || '',
|
||||
hostStatuses: request?.body?.filters?.host_status || [],
|
||||
});
|
||||
body = await legacyListMetadataQuery(
|
||||
context,
|
||||
endpointAppContext,
|
||||
fleetServices,
|
||||
logger,
|
||||
endpointPolicies,
|
||||
{
|
||||
page: pagingProperties.pageIndex,
|
||||
pageSize: pagingProperties.pageSize,
|
||||
kuery: request?.body?.filters?.kql || '',
|
||||
hostStatuses: request?.body?.filters?.host_status || [],
|
||||
}
|
||||
);
|
||||
return response.ok({ body });
|
||||
}
|
||||
|
||||
|
@ -138,6 +143,7 @@ export const getMetadataListRequestHandler = function (
|
|||
const pagingProperties = await getPagingProperties(request, endpointAppContext);
|
||||
const { data, total } = await endpointMetadataService.getHostMetadataList(
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
fleetServices,
|
||||
{
|
||||
page: pagingProperties.pageIndex,
|
||||
pageSize: pagingProperties.pageSize,
|
||||
|
@ -171,6 +177,7 @@ export function getMetadataListRequestHandlerV2(
|
|||
> {
|
||||
return async (context, request, response) => {
|
||||
const endpointMetadataService = endpointAppContext.service.getEndpointMetadataService();
|
||||
const fleetServices = endpointAppContext.service.getScopedFleetServices(request);
|
||||
|
||||
let doesUnitedIndexExist = false;
|
||||
let didUnitedIndexError = false;
|
||||
|
@ -193,13 +200,14 @@ export function getMetadataListRequestHandlerV2(
|
|||
// If no unified Index present, then perform a search using the legacy approach
|
||||
if (!doesUnitedIndexExist || didUnitedIndexError) {
|
||||
const endpointPolicies = await getAllEndpointPackagePolicies(
|
||||
endpointAppContext.service.getPackagePolicyService(),
|
||||
fleetServices.packagePolicy,
|
||||
context.core.savedObjects.client
|
||||
);
|
||||
|
||||
const legacyResponse = await legacyListMetadataQuery(
|
||||
context,
|
||||
endpointAppContext,
|
||||
fleetServices,
|
||||
logger,
|
||||
endpointPolicies,
|
||||
request.query
|
||||
|
@ -217,6 +225,7 @@ export function getMetadataListRequestHandlerV2(
|
|||
try {
|
||||
const { data, total } = await endpointMetadataService.getHostMetadataList(
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
fleetServices,
|
||||
request.query
|
||||
);
|
||||
|
||||
|
@ -250,6 +259,7 @@ export const getMetadataRequestHandler = function (
|
|||
return response.ok({
|
||||
body: await endpointMetadataService.getEnrichedHostMetadata(
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
endpointAppContext.service.getScopedFleetServices(request),
|
||||
request.params.id
|
||||
),
|
||||
});
|
||||
|
@ -314,10 +324,6 @@ export async function enrichHostMetadata(
|
|||
throw e;
|
||||
}
|
||||
|
||||
const esClient = (metadataRequestContext?.esClient ??
|
||||
metadataRequestContext.requestHandlerContext?.core.elasticsearch
|
||||
.client) as IScopedClusterClient;
|
||||
|
||||
const esSavedObjectClient =
|
||||
metadataRequestContext?.savedObjectsClient ??
|
||||
(metadataRequestContext.requestHandlerContext?.core.savedObjects
|
||||
|
@ -333,9 +339,10 @@ export async function enrichHostMetadata(
|
|||
log.warn(`Missing elastic agent id, using host id instead ${elasticAgentId}`);
|
||||
}
|
||||
|
||||
const status = await metadataRequestContext.endpointAppContextService
|
||||
?.getAgentService()
|
||||
?.getAgentStatusById(esClient.asCurrentUser, elasticAgentId);
|
||||
const status =
|
||||
await metadataRequestContext.requestHandlerContext?.fleet?.agentClient.asCurrentUser.getAgentStatusById(
|
||||
elasticAgentId
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
hostStatus = fleetAgentStatusToEndpointHostStatus(status!);
|
||||
} catch (e) {
|
||||
|
@ -349,9 +356,10 @@ export async function enrichHostMetadata(
|
|||
|
||||
let policyInfo: HostInfo['policy_info'];
|
||||
try {
|
||||
const agent = await metadataRequestContext.endpointAppContextService
|
||||
?.getAgentService()
|
||||
?.getAgent(esClient.asCurrentUser, elasticAgentId);
|
||||
const agent =
|
||||
await metadataRequestContext.requestHandlerContext?.fleet?.agentClient.asCurrentUser.getAgent(
|
||||
elasticAgentId
|
||||
);
|
||||
const agentPolicy = await metadataRequestContext.endpointAppContextService
|
||||
.getAgentPolicyService()
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
|
@ -393,15 +401,12 @@ export async function enrichHostMetadata(
|
|||
async function legacyListMetadataQuery(
|
||||
context: SecuritySolutionRequestHandlerContext,
|
||||
endpointAppContext: EndpointAppContext,
|
||||
fleetServices: EndpointFleetServicesInterface,
|
||||
logger: Logger,
|
||||
endpointPolicies: PackagePolicy[],
|
||||
queryOptions: GetMetadataListRequestQuery
|
||||
): Promise<HostResultList> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const agentService = endpointAppContext.service.getAgentService()!;
|
||||
if (agentService === undefined) {
|
||||
throw new Error('agentService not available');
|
||||
}
|
||||
const fleetAgentClient = fleetServices.agent;
|
||||
|
||||
const metadataRequestContext: MetadataRequestContext = {
|
||||
esClient: context.core.elasticsearch.client,
|
||||
|
@ -412,14 +417,15 @@ async function legacyListMetadataQuery(
|
|||
};
|
||||
|
||||
const endpointPolicyIds = endpointPolicies.map((policy) => policy.policy_id);
|
||||
|
||||
const unenrolledAgentIds = await findAllUnenrolledAgentIds(
|
||||
agentService,
|
||||
fleetAgentClient,
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
endpointPolicyIds
|
||||
);
|
||||
|
||||
const statusAgentIds = await findAgentIdsByStatus(
|
||||
agentService,
|
||||
fleetAgentClient,
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
queryOptions?.hostStatuses || []
|
||||
);
|
||||
|
|
|
@ -43,7 +43,7 @@ import {
|
|||
legacyMetadataSearchResponseMock,
|
||||
unitedMetadataSearchResponseMock,
|
||||
} from './support/test_support';
|
||||
import { PackageService } from '../../../../../fleet/server/services';
|
||||
import { AgentClient, PackageService } from '../../../../../fleet/server/services';
|
||||
import {
|
||||
HOST_METADATA_GET_ROUTE,
|
||||
HOST_METADATA_LIST_ROUTE,
|
||||
|
@ -60,6 +60,7 @@ import {
|
|||
} from '../../../../../../../src/core/server/elasticsearch/client/mocks';
|
||||
import { EndpointHostNotFoundError } from '../../services/metadata';
|
||||
import { FleetAgentGenerator } from '../../../../common/endpoint/data_generators/fleet_agent_generator';
|
||||
import { createMockAgentClient } from '../../../../../fleet/server/mocks';
|
||||
|
||||
class IndexNotFoundException extends Error {
|
||||
meta: { body: { error: { type: string } } };
|
||||
|
@ -88,6 +89,7 @@ describe('test endpoint routes', () => {
|
|||
let mockAgentPolicyService: Required<
|
||||
ReturnType<typeof createMockEndpointAppContextServiceStartContract>
|
||||
>['agentPolicyService'];
|
||||
let mockAgentClient: jest.Mocked<AgentClient>;
|
||||
let endpointAppContextService: EndpointAppContextService;
|
||||
let startContract: EndpointAppContextServiceStartContract;
|
||||
const noUnenrolledAgent = {
|
||||
|
@ -151,6 +153,8 @@ describe('test endpoint routes', () => {
|
|||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
mockAgentClient = createMockAgentClient();
|
||||
mockAgentService.asScoped = () => mockAgentClient;
|
||||
mockAgentPolicyService = startContract.agentPolicyService!;
|
||||
|
||||
registerEndpointRoutes(routerMock, {
|
||||
|
@ -176,8 +180,8 @@ describe('test endpoint routes', () => {
|
|||
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(HOST_METADATA_LIST_ROUTE)
|
||||
)!;
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
|
@ -220,8 +224,8 @@ describe('test endpoint routes', () => {
|
|||
},
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
mockAgentPolicyService.getByIds = jest.fn().mockResolvedValueOnce([]);
|
||||
const metadata = new EndpointDocGenerator().generateHostMetadata();
|
||||
const esSearchMock = mockScopedClient.asCurrentUser.search as jest.Mock;
|
||||
|
@ -415,6 +419,8 @@ describe('test endpoint routes', () => {
|
|||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
mockAgentClient = createMockAgentClient();
|
||||
mockAgentService.asScoped = () => mockAgentClient;
|
||||
|
||||
registerEndpointRoutes(routerMock, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
|
@ -439,8 +445,8 @@ describe('test endpoint routes', () => {
|
|||
[routeConfig, routeHandler] = routerMock.post.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(HOST_METADATA_LIST_ROUTE)
|
||||
)!;
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
|
@ -474,8 +480,8 @@ describe('test endpoint routes', () => {
|
|||
},
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock)
|
||||
.mockImplementationOnce(() => {
|
||||
throw new IndexNotFoundException();
|
||||
|
@ -536,8 +542,8 @@ describe('test endpoint routes', () => {
|
|||
},
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock)
|
||||
.mockImplementationOnce(() => {
|
||||
throw new IndexNotFoundException();
|
||||
|
@ -653,6 +659,8 @@ describe('test endpoint routes', () => {
|
|||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
mockAgentClient = createMockAgentClient();
|
||||
mockAgentService.asScoped = () => mockAgentClient;
|
||||
mockAgentPolicyService = startContract.agentPolicyService!;
|
||||
|
||||
registerEndpointRoutes(routerMock, {
|
||||
|
@ -683,8 +691,8 @@ describe('test endpoint routes', () => {
|
|||
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(HOST_METADATA_LIST_ROUTE)
|
||||
)!;
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
|
@ -718,8 +726,8 @@ describe('test endpoint routes', () => {
|
|||
},
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
mockAgentPolicyService.getByIds = jest.fn().mockResolvedValueOnce([]);
|
||||
const metadata = new EndpointDocGenerator().generateHostMetadata();
|
||||
const esSearchMock = mockScopedClient.asCurrentUser.search as jest.Mock;
|
||||
|
@ -913,6 +921,8 @@ describe('test endpoint routes', () => {
|
|||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
mockAgentClient = createMockAgentClient();
|
||||
mockAgentService.asScoped = () => mockAgentClient;
|
||||
|
||||
registerEndpointRoutes(routerMock, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
|
@ -942,8 +952,8 @@ describe('test endpoint routes', () => {
|
|||
[routeConfig, routeHandler] = routerMock.get.mock.calls.find(([{ path }]) =>
|
||||
path.startsWith(HOST_METADATA_LIST_ROUTE)
|
||||
)!;
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
await routeHandler(
|
||||
createRouteHandlerContext(mockScopedClient, mockSavedObjectClient),
|
||||
mockRequest,
|
||||
|
@ -971,8 +981,8 @@ describe('test endpoint routes', () => {
|
|||
},
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock)
|
||||
.mockImplementationOnce(() => {
|
||||
throw new IndexNotFoundException();
|
||||
|
@ -1026,8 +1036,8 @@ describe('test endpoint routes', () => {
|
|||
},
|
||||
});
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.listAgents = jest.fn().mockReturnValue(noUnenrolledAgent);
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.listAgents.mockResolvedValue(noUnenrolledAgent);
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock)
|
||||
.mockImplementationOnce(() => {
|
||||
throw new IndexNotFoundException();
|
||||
|
@ -1142,6 +1152,8 @@ describe('test endpoint routes', () => {
|
|||
endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract());
|
||||
endpointAppContextService.start({ ...startContract, packageService: mockPackageService });
|
||||
mockAgentService = startContract.agentService!;
|
||||
mockAgentClient = createMockAgentClient();
|
||||
mockAgentService.asScoped = () => mockAgentClient;
|
||||
|
||||
registerEndpointRoutes(routerMock, {
|
||||
logFactory: loggingSystemMock.create(),
|
||||
|
@ -1160,8 +1172,8 @@ describe('test endpoint routes', () => {
|
|||
Promise.resolve({ body: legacyMetadataSearchResponseMock() })
|
||||
);
|
||||
|
||||
mockAgentService.getAgentStatusById = jest.fn().mockReturnValue('error');
|
||||
mockAgentService.getAgent = jest.fn().mockReturnValue({
|
||||
mockAgentClient.getAgentStatusById.mockResolvedValue('error');
|
||||
mockAgentClient.getAgent.mockResolvedValue({
|
||||
active: true,
|
||||
} as unknown as Agent);
|
||||
|
||||
|
@ -1192,9 +1204,7 @@ describe('test endpoint routes', () => {
|
|||
params: { id: response.hits.hits[0]._id },
|
||||
});
|
||||
|
||||
mockAgentService.getAgent = jest
|
||||
.fn()
|
||||
.mockReturnValue(agentGenerator.generate({ status: 'online' }));
|
||||
mockAgentClient.getAgent.mockResolvedValue(agentGenerator.generate({ status: 'online' }));
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
|
@ -1229,7 +1239,7 @@ describe('test endpoint routes', () => {
|
|||
params: { id: response.hits.hits[0]._id },
|
||||
});
|
||||
|
||||
mockAgentService.getAgent = jest.fn().mockRejectedValue(new AgentNotFoundError('not found'));
|
||||
mockAgentClient.getAgent.mockRejectedValue(new AgentNotFoundError('not found'));
|
||||
|
||||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
|
@ -1264,7 +1274,7 @@ describe('test endpoint routes', () => {
|
|||
params: { id: response.hits.hits[0]._id },
|
||||
});
|
||||
|
||||
mockAgentService.getAgent = jest.fn().mockReturnValue(
|
||||
mockAgentClient.getAgent.mockResolvedValue(
|
||||
agentGenerator.generate({
|
||||
status: 'error',
|
||||
})
|
||||
|
@ -1304,7 +1314,7 @@ describe('test endpoint routes', () => {
|
|||
(mockScopedClient.asCurrentUser.search as jest.Mock).mockImplementationOnce(() =>
|
||||
Promise.resolve({ body: response })
|
||||
);
|
||||
mockAgentService.getAgent = jest.fn().mockReturnValue({
|
||||
mockAgentClient.getAgent.mockResolvedValue({
|
||||
active: false,
|
||||
} as unknown as Agent);
|
||||
|
||||
|
|
|
@ -8,21 +8,21 @@
|
|||
import { ElasticsearchClient } from 'kibana/server';
|
||||
import { buildStatusesKuery, findAgentIdsByStatus } from './agent_status';
|
||||
import { elasticsearchServiceMock } from '../../../../../../../../src/core/server/mocks';
|
||||
import { AgentService } from '../../../../../../fleet/server/services';
|
||||
import { createMockAgentService } from '../../../../../../fleet/server/mocks';
|
||||
import { AgentClient } from '../../../../../../fleet/server/services';
|
||||
import { createMockAgentClient } from '../../../../../../fleet/server/mocks';
|
||||
import { Agent } from '../../../../../../fleet/common/types/models';
|
||||
import { AgentStatusKueryHelper } from '../../../../../../fleet/common/services';
|
||||
|
||||
describe('test filtering endpoint hosts by agent status', () => {
|
||||
let mockElasticsearchClient: jest.Mocked<ElasticsearchClient>;
|
||||
let mockAgentService: jest.Mocked<AgentService>;
|
||||
let mockAgentClient: jest.Mocked<AgentClient>;
|
||||
beforeEach(() => {
|
||||
mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
mockAgentService = createMockAgentService();
|
||||
mockAgentClient = createMockAgentClient();
|
||||
});
|
||||
|
||||
it('will accept a valid status condition', async () => {
|
||||
mockAgentService.listAgents.mockImplementationOnce(() =>
|
||||
mockAgentClient.listAgents.mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
agents: [],
|
||||
total: 0,
|
||||
|
@ -31,14 +31,14 @@ describe('test filtering endpoint hosts by agent status', () => {
|
|||
})
|
||||
);
|
||||
|
||||
const result = await findAgentIdsByStatus(mockAgentService, mockElasticsearchClient, [
|
||||
const result = await findAgentIdsByStatus(mockAgentClient, mockElasticsearchClient, [
|
||||
'healthy',
|
||||
]);
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it('will filter for offline hosts', async () => {
|
||||
mockAgentService.listAgents
|
||||
mockAgentClient.listAgents
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
agents: [{ id: 'id1' } as unknown as Agent, { id: 'id2' } as unknown as Agent],
|
||||
|
@ -56,11 +56,11 @@ describe('test filtering endpoint hosts by agent status', () => {
|
|||
})
|
||||
);
|
||||
|
||||
const result = await findAgentIdsByStatus(mockAgentService, mockElasticsearchClient, [
|
||||
const result = await findAgentIdsByStatus(mockAgentClient, mockElasticsearchClient, [
|
||||
'offline',
|
||||
]);
|
||||
const offlineKuery = AgentStatusKueryHelper.buildKueryForOfflineAgents();
|
||||
expect(mockAgentService.listAgents.mock.calls[0][1].kuery).toEqual(
|
||||
expect(mockAgentClient.listAgents.mock.calls[0][0].kuery).toEqual(
|
||||
expect.stringContaining(offlineKuery)
|
||||
);
|
||||
expect(result).toBeDefined();
|
||||
|
@ -68,7 +68,7 @@ describe('test filtering endpoint hosts by agent status', () => {
|
|||
});
|
||||
|
||||
it('will filter for multiple statuses', async () => {
|
||||
mockAgentService.listAgents
|
||||
mockAgentClient.listAgents
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
agents: [{ id: 'A' } as unknown as Agent, { id: 'B' } as unknown as Agent],
|
||||
|
@ -86,13 +86,13 @@ describe('test filtering endpoint hosts by agent status', () => {
|
|||
})
|
||||
);
|
||||
|
||||
const result = await findAgentIdsByStatus(mockAgentService, mockElasticsearchClient, [
|
||||
const result = await findAgentIdsByStatus(mockAgentClient, mockElasticsearchClient, [
|
||||
'updating',
|
||||
'unhealthy',
|
||||
]);
|
||||
const unenrollKuery = AgentStatusKueryHelper.buildKueryForUpdatingAgents();
|
||||
const errorKuery = AgentStatusKueryHelper.buildKueryForErrorAgents();
|
||||
expect(mockAgentService.listAgents.mock.calls[0][1].kuery).toEqual(
|
||||
expect(mockAgentClient.listAgents.mock.calls[0][0].kuery).toEqual(
|
||||
expect.stringContaining(`${unenrollKuery} OR ${errorKuery}`)
|
||||
);
|
||||
expect(result).toBeDefined();
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { ElasticsearchClient } from 'kibana/server';
|
||||
import { AgentService } from '../../../../../../fleet/server';
|
||||
import { AgentClient } from '../../../../../../fleet/server';
|
||||
import { AgentStatusKueryHelper } from '../../../../../../fleet/common/services';
|
||||
import { Agent } from '../../../../../../fleet/common/types/models';
|
||||
import { HostStatus } from '../../../../../common/endpoint/types';
|
||||
|
@ -34,7 +34,7 @@ export function buildStatusesKuery(statusesToFilter: string[]): string | undefin
|
|||
}
|
||||
|
||||
export async function findAgentIdsByStatus(
|
||||
agentService: AgentService,
|
||||
agentClient: AgentClient,
|
||||
esClient: ElasticsearchClient,
|
||||
statuses: string[],
|
||||
pageSize: number = 1000
|
||||
|
@ -59,7 +59,7 @@ export async function findAgentIdsByStatus(
|
|||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const agents = await agentService.listAgents(esClient, searchOptions(page++));
|
||||
const agents = await agentClient.listAgents(searchOptions(page++));
|
||||
result.push(...agents.agents.map((agent: Agent) => agent.id));
|
||||
hasMore = agents.agents.length > 0;
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
import { ElasticsearchClient } from 'kibana/server';
|
||||
import { findAllUnenrolledAgentIds } from './unenroll';
|
||||
import { elasticsearchServiceMock } from '../../../../../../../../src/core/server/mocks';
|
||||
import { AgentService } from '../../../../../../fleet/server/services';
|
||||
import { AgentClient } from '../../../../../../fleet/server/services';
|
||||
import {
|
||||
createMockAgentService,
|
||||
createMockAgentClient,
|
||||
createPackagePolicyServiceMock,
|
||||
} from '../../../../../../fleet/server/mocks';
|
||||
import { Agent, PackagePolicy } from '../../../../../../fleet/common/types/models';
|
||||
|
@ -18,12 +18,12 @@ import { PackagePolicyServiceInterface } from '../../../../../../fleet/server';
|
|||
|
||||
describe('test find all unenrolled Agent id', () => {
|
||||
let mockElasticsearchClient: jest.Mocked<ElasticsearchClient>;
|
||||
let mockAgentService: jest.Mocked<AgentService>;
|
||||
let mockAgentClient: jest.Mocked<AgentClient>;
|
||||
let mockPackagePolicyService: jest.Mocked<PackagePolicyServiceInterface>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
|
||||
mockAgentService = createMockAgentService();
|
||||
mockAgentClient = createMockAgentClient();
|
||||
mockPackagePolicyService = createPackagePolicyServiceMock();
|
||||
});
|
||||
|
||||
|
@ -46,7 +46,7 @@ describe('test find all unenrolled Agent id', () => {
|
|||
perPage: 10,
|
||||
page: 1,
|
||||
});
|
||||
mockAgentService.listAgents
|
||||
mockAgentClient.listAgents
|
||||
.mockImplementationOnce(() =>
|
||||
Promise.resolve({
|
||||
agents: [
|
||||
|
@ -81,7 +81,7 @@ describe('test find all unenrolled Agent id', () => {
|
|||
);
|
||||
const endpointPolicyIds = ['test-endpoint-policy-id'];
|
||||
const agentIds = await findAllUnenrolledAgentIds(
|
||||
mockAgentService,
|
||||
mockAgentClient,
|
||||
mockElasticsearchClient,
|
||||
endpointPolicyIds
|
||||
);
|
||||
|
@ -89,7 +89,7 @@ describe('test find all unenrolled Agent id', () => {
|
|||
expect(agentIds).toBeTruthy();
|
||||
expect(agentIds).toEqual(['id1', 'id2']);
|
||||
|
||||
expect(mockAgentService.listAgents).toHaveBeenNthCalledWith(1, mockElasticsearchClient, {
|
||||
expect(mockAgentClient.listAgents).toHaveBeenNthCalledWith(1, {
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
showInactive: true,
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
*/
|
||||
|
||||
import { ElasticsearchClient } from 'kibana/server';
|
||||
import { AgentService } from '../../../../../../fleet/server';
|
||||
import type { AgentClient } from '../../../../../../fleet/server';
|
||||
import { Agent } from '../../../../../../fleet/common/types/models';
|
||||
|
||||
export async function findAllUnenrolledAgentIds(
|
||||
agentService: AgentService,
|
||||
agentClient: AgentClient,
|
||||
esClient: ElasticsearchClient,
|
||||
endpointPolicyIds: string[],
|
||||
pageSize: number = 1000
|
||||
|
@ -41,7 +41,7 @@ export async function findAllUnenrolledAgentIds(
|
|||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const unenrolledAgents = await agentService.listAgents(esClient, searchOptions(page++));
|
||||
const unenrolledAgents = await agentClient.listAgents(searchOptions(page++));
|
||||
result.push(...unenrolledAgents.agents.map((agent: Agent) => agent.id));
|
||||
hasMore = unenrolledAgents.agents.length > 0;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
createMockEndpointAppContextServiceStartContract,
|
||||
createRouteHandlerContext,
|
||||
} from '../../mocks';
|
||||
import { createMockAgentService } from '../../../../../fleet/server/mocks';
|
||||
import { createMockAgentClient, createMockAgentService } from '../../../../../fleet/server/mocks';
|
||||
import { getHostPolicyResponseHandler, getAgentPolicySummaryHandler } from './handlers';
|
||||
import {
|
||||
KibanaResponseFactory,
|
||||
|
@ -29,7 +29,7 @@ import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'
|
|||
import { parseExperimentalConfigValue } from '../../../../common/experimental_features';
|
||||
import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__';
|
||||
import { Agent } from '../../../../../fleet/common/types/models';
|
||||
import { AgentService } from '../../../../../fleet/server/services';
|
||||
import { AgentClient, AgentService } from '../../../../../fleet/server/services';
|
||||
import { get } from 'lodash';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { ScopedClusterClientMock } from '../../../../../../../src/core/server/elasticsearch/client/mocks';
|
||||
|
@ -101,6 +101,7 @@ describe('test policy response handler', () => {
|
|||
|
||||
describe('test agent policy summary handler', () => {
|
||||
let mockAgentService: jest.Mocked<AgentService>;
|
||||
let mockAgentClient: jest.Mocked<AgentClient>;
|
||||
|
||||
let agentListResult: {
|
||||
agents: Agent[];
|
||||
|
@ -122,6 +123,8 @@ describe('test policy response handler', () => {
|
|||
mockResponse = httpServerMock.createResponseFactory();
|
||||
endpointAppContextService = new EndpointAppContextService();
|
||||
mockAgentService = createMockAgentService();
|
||||
mockAgentClient = createMockAgentClient();
|
||||
mockAgentService.asScoped.mockReturnValue(mockAgentClient);
|
||||
emptyAgentListResult = {
|
||||
agents: [],
|
||||
total: 2,
|
||||
|
@ -173,7 +176,7 @@ describe('test policy response handler', () => {
|
|||
afterEach(() => endpointAppContextService.stop());
|
||||
|
||||
it('should return the summary of all the agent with the given policy name', async () => {
|
||||
mockAgentService.listAgents
|
||||
mockAgentClient.listAgents
|
||||
.mockImplementationOnce(() => Promise.resolve(agentListResult))
|
||||
.mockImplementationOnce(() => Promise.resolve(emptyAgentListResult));
|
||||
|
||||
|
@ -204,7 +207,7 @@ describe('test policy response handler', () => {
|
|||
});
|
||||
|
||||
it('should return the agent summary', async () => {
|
||||
mockAgentService.listAgents
|
||||
mockAgentClient.listAgents
|
||||
.mockImplementationOnce(() => Promise.resolve(agentListResult))
|
||||
.mockImplementationOnce(() => Promise.resolve(emptyAgentListResult));
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ export const getAgentPolicySummaryHandler = function (
|
|||
endpointAppContext,
|
||||
context.core.savedObjects.client,
|
||||
context.core.elasticsearch.client.asCurrentUser,
|
||||
request,
|
||||
request.query.package_name,
|
||||
request.query?.policy_id || undefined
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import {
|
||||
ElasticsearchClient,
|
||||
IScopedClusterClient,
|
||||
KibanaRequest,
|
||||
SavedObjectsClientContract,
|
||||
} from '../../../../../../../src/core/server';
|
||||
import { GetHostPolicyResponse, HostPolicyResponse } from '../../../../common/endpoint/types';
|
||||
|
@ -78,6 +79,7 @@ export async function getAgentPolicySummary(
|
|||
endpointAppContext: EndpointAppContext,
|
||||
soClient: SavedObjectsClientContract,
|
||||
esClient: ElasticsearchClient,
|
||||
request: KibanaRequest,
|
||||
packageName: string,
|
||||
policyId?: string,
|
||||
pageSize: number = 1000
|
||||
|
@ -89,6 +91,7 @@ export async function getAgentPolicySummary(
|
|||
endpointAppContext,
|
||||
soClient,
|
||||
esClient,
|
||||
request,
|
||||
`${agentQuery} AND policy_id:${policyId}`,
|
||||
pageSize
|
||||
)
|
||||
|
@ -96,7 +99,7 @@ export async function getAgentPolicySummary(
|
|||
}
|
||||
|
||||
return transformAgentVersionMap(
|
||||
await agentVersionsMap(endpointAppContext, soClient, esClient, agentQuery, pageSize)
|
||||
await agentVersionsMap(endpointAppContext, soClient, esClient, request, agentQuery, pageSize)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -104,6 +107,7 @@ export async function agentVersionsMap(
|
|||
endpointAppContext: EndpointAppContext,
|
||||
soClient: SavedObjectsClientContract,
|
||||
esClient: ElasticsearchClient,
|
||||
request: KibanaRequest,
|
||||
kqlQuery: string,
|
||||
pageSize: number = 1000
|
||||
): Promise<Map<string, number>> {
|
||||
|
@ -123,7 +127,8 @@ export async function agentVersionsMap(
|
|||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const queryResult = await endpointAppContext.service
|
||||
.getAgentService()!
|
||||
.listAgents(esClient, searchOptions(page++));
|
||||
.asScoped(request)
|
||||
.listAgents(searchOptions(page++));
|
||||
queryResult.agents.forEach((agent: Agent) => {
|
||||
const agentVersion = agent.local_metadata?.elastic?.agent?.version;
|
||||
if (result.has(agentVersion)) {
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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 { KibanaRequest } from 'kibana/server';
|
||||
import type {
|
||||
AgentClient,
|
||||
AgentPolicyServiceInterface,
|
||||
FleetStartContract,
|
||||
PackagePolicyServiceInterface,
|
||||
PackageService,
|
||||
} from '../../../../fleet/server';
|
||||
|
||||
export interface EndpointFleetServicesFactoryInterface {
|
||||
asScoped(req: KibanaRequest): EndpointScopedFleetServicesInterface;
|
||||
|
||||
asInternalUser(): EndpointInternalFleetServicesInterface;
|
||||
}
|
||||
|
||||
export class EndpointFleetServicesFactory implements EndpointFleetServicesFactoryInterface {
|
||||
constructor(
|
||||
private readonly fleetDependencies: Pick<
|
||||
FleetStartContract,
|
||||
'agentService' | 'packageService' | 'packagePolicyService' | 'agentPolicyService'
|
||||
>
|
||||
) {}
|
||||
|
||||
asScoped(req: KibanaRequest): EndpointScopedFleetServicesInterface {
|
||||
const {
|
||||
agentPolicyService: agentPolicy,
|
||||
packagePolicyService: packagePolicy,
|
||||
agentService,
|
||||
packageService: packages,
|
||||
} = this.fleetDependencies;
|
||||
|
||||
return {
|
||||
agent: agentService.asScoped(req),
|
||||
agentPolicy,
|
||||
packages,
|
||||
packagePolicy,
|
||||
|
||||
asInternal: this.asInternalUser.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
asInternalUser(): EndpointInternalFleetServicesInterface {
|
||||
const {
|
||||
agentPolicyService: agentPolicy,
|
||||
packagePolicyService: packagePolicy,
|
||||
agentService,
|
||||
packageService: packages,
|
||||
} = this.fleetDependencies;
|
||||
|
||||
return {
|
||||
agent: agentService.asInternalUser,
|
||||
agentPolicy,
|
||||
packages,
|
||||
packagePolicy,
|
||||
|
||||
asScoped: this.asScoped.bind(this),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of Fleet services used by Endpoint
|
||||
*/
|
||||
export interface EndpointFleetServicesInterface {
|
||||
agent: AgentClient;
|
||||
agentPolicy: AgentPolicyServiceInterface;
|
||||
packages: PackageService;
|
||||
packagePolicy: PackagePolicyServiceInterface;
|
||||
}
|
||||
|
||||
export interface EndpointScopedFleetServicesInterface extends EndpointFleetServicesInterface {
|
||||
/**
|
||||
* get internal fleet services instance
|
||||
*/
|
||||
asInternal: EndpointFleetServicesFactoryInterface['asInternalUser'];
|
||||
}
|
||||
|
||||
export interface EndpointInternalFleetServicesInterface extends EndpointFleetServicesInterface {
|
||||
/**
|
||||
* get scoped endpoint fleet services instance
|
||||
*/
|
||||
asScoped: EndpointFleetServicesFactoryInterface['asScoped'];
|
||||
}
|
|
@ -117,12 +117,16 @@ describe('EndpointMetadataService', () => {
|
|||
it('should throw wrapped error if es error', async () => {
|
||||
const esMockResponse = elasticsearchServiceMock.createErrorTransportRequestPromise({});
|
||||
esClient.search.mockResolvedValue(esMockResponse);
|
||||
const metadataListResponse = metadataService.getHostMetadataList(esClient, {
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
kuery: '',
|
||||
hostStatuses: [],
|
||||
});
|
||||
const metadataListResponse = metadataService.getHostMetadataList(
|
||||
esClient,
|
||||
testMockedContext.fleetServices,
|
||||
{
|
||||
page: 0,
|
||||
pageSize: 10,
|
||||
kuery: '',
|
||||
hostStatuses: [],
|
||||
}
|
||||
);
|
||||
await expect(metadataListResponse).rejects.toThrow(EndpointError);
|
||||
});
|
||||
|
||||
|
@ -176,6 +180,7 @@ describe('EndpointMetadataService', () => {
|
|||
const queryOptions = { page: 1, pageSize: 10, kuery: '', hostStatuses: [] };
|
||||
const metadataListResponse = await metadataService.getHostMetadataList(
|
||||
esClient,
|
||||
testMockedContext.fleetServices,
|
||||
queryOptions
|
||||
);
|
||||
const unitedIndexQuery = await buildUnitedIndexQuery(queryOptions, packagePolicyIds);
|
||||
|
|
|
@ -26,7 +26,6 @@ import { Agent, AgentPolicy, PackagePolicy } from '../../../../../fleet/common';
|
|||
import {
|
||||
AgentNotFoundError,
|
||||
AgentPolicyServiceInterface,
|
||||
AgentService,
|
||||
PackagePolicyServiceInterface,
|
||||
} from '../../../../../fleet/server';
|
||||
import {
|
||||
|
@ -57,6 +56,7 @@ import { getAllEndpointPackagePolicies } from '../../routes/metadata/support/end
|
|||
import { getAgentStatus } from '../../../../../fleet/common/services/agent_status';
|
||||
import { GetMetadataListRequestQuery } from '../../../../common/endpoint/schema/metadata';
|
||||
import { EndpointError } from '../../../../common/endpoint/errors';
|
||||
import { EndpointFleetServicesInterface } from '../endpoint_fleet_services';
|
||||
|
||||
type AgentPolicyWithPackagePolicies = Omit<AgentPolicy, 'package_policies'> & {
|
||||
package_policies: PackagePolicy[];
|
||||
|
@ -84,7 +84,6 @@ export class EndpointMetadataService {
|
|||
|
||||
constructor(
|
||||
private savedObjectsStart: SavedObjectsServiceStart,
|
||||
private readonly agentService: AgentService,
|
||||
private readonly agentPolicyService: AgentPolicyServiceInterface,
|
||||
private readonly packagePolicyService: PackagePolicyServiceInterface,
|
||||
private readonly logger?: Logger
|
||||
|
@ -156,12 +155,14 @@ export class EndpointMetadataService {
|
|||
* Retrieve a single endpoint host metadata along with fleet information
|
||||
*
|
||||
* @param esClient Elasticsearch Client (usually scoped to the user's context)
|
||||
* @param fleetServices
|
||||
* @param endpointId the endpoint id (from `agent.id`)
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
async getEnrichedHostMetadata(
|
||||
esClient: ElasticsearchClient,
|
||||
fleetServices: EndpointFleetServicesInterface,
|
||||
endpointId: string
|
||||
): Promise<HostInfo> {
|
||||
const endpointMetadata = await this.getHostMetadata(esClient, endpointId);
|
||||
|
@ -176,7 +177,7 @@ export class EndpointMetadataService {
|
|||
this.logger?.warn(`Missing elastic agent id, using host id instead ${fleetAgentId}`);
|
||||
}
|
||||
|
||||
fleetAgent = await this.getFleetAgent(esClient, fleetAgentId);
|
||||
fleetAgent = await this.getFleetAgent(fleetServices.agent, fleetAgentId);
|
||||
} catch (error) {
|
||||
if (error instanceof FleetAgentNotFoundError) {
|
||||
this.logger?.warn(`agent with id ${fleetAgentId} not found`);
|
||||
|
@ -192,12 +193,12 @@ export class EndpointMetadataService {
|
|||
);
|
||||
}
|
||||
|
||||
return this.enrichHostMetadata(esClient, endpointMetadata, fleetAgent);
|
||||
return this.enrichHostMetadata(fleetServices, endpointMetadata, fleetAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enriches a host metadata document with data from fleet
|
||||
* @param esClient
|
||||
* @param fleetServices
|
||||
* @param endpointMetadata
|
||||
* @param _fleetAgent
|
||||
* @param _fleetAgentPolicy
|
||||
|
@ -206,7 +207,7 @@ export class EndpointMetadataService {
|
|||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
private async enrichHostMetadata(
|
||||
esClient: ElasticsearchClient,
|
||||
fleetServices: EndpointFleetServicesInterface,
|
||||
endpointMetadata: HostMetadata,
|
||||
/**
|
||||
* If undefined, it will be retrieved from Fleet using the ID in the endpointMetadata.
|
||||
|
@ -242,7 +243,7 @@ export class EndpointMetadataService {
|
|||
);
|
||||
}
|
||||
|
||||
fleetAgent = await this.getFleetAgent(esClient, fleetAgentId);
|
||||
fleetAgent = await this.getFleetAgent(fleetServices.agent, fleetAgentId);
|
||||
} catch (error) {
|
||||
if (error instanceof FleetAgentNotFoundError) {
|
||||
this.logger?.warn(`agent with id ${fleetAgentId} not found`);
|
||||
|
@ -310,12 +311,15 @@ export class EndpointMetadataService {
|
|||
/**
|
||||
* Retrieve a single Fleet Agent data
|
||||
*
|
||||
* @param esClient Elasticsearch Client (usually scoped to the user's context)
|
||||
* @param fleetAgentService
|
||||
* @param agentId The elastic agent id (`from `elastic.agent.id`)
|
||||
*/
|
||||
async getFleetAgent(esClient: ElasticsearchClient, agentId: string): Promise<Agent> {
|
||||
async getFleetAgent(
|
||||
fleetAgentService: EndpointFleetServicesInterface['agent'],
|
||||
agentId: string
|
||||
): Promise<Agent> {
|
||||
try {
|
||||
return await this.agentService.getAgent(esClient, agentId);
|
||||
return await fleetAgentService.getAgent(agentId);
|
||||
} catch (error) {
|
||||
if (error instanceof AgentNotFoundError) {
|
||||
throw new FleetAgentNotFoundError(`agent with id ${agentId} not found`, error);
|
||||
|
@ -402,6 +406,7 @@ export class EndpointMetadataService {
|
|||
*/
|
||||
async getHostMetadataList(
|
||||
esClient: ElasticsearchClient,
|
||||
fleetServices: EndpointFleetServicesInterface,
|
||||
queryOptions: GetMetadataListRequestQuery
|
||||
): Promise<Pick<MetadataListResponse, 'data' | 'total'>> {
|
||||
const endpointPolicies = await getAllEndpointPackagePolicies(
|
||||
|
@ -468,7 +473,7 @@ export class EndpointMetadataService {
|
|||
const endpointPolicy = endpointPoliciesMap[agent.policy_id!];
|
||||
|
||||
hosts.push(
|
||||
await this.enrichHostMetadata(esClient, metadata, agent, agentPolicy, endpointPolicy)
|
||||
await this.enrichHostMetadata(fleetServices, metadata, agent, agentPolicy, endpointPolicy)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,14 @@ import { savedObjectsServiceMock } from '../../../../../../../src/core/server/mo
|
|||
import {
|
||||
createMockAgentPolicyService,
|
||||
createMockAgentService,
|
||||
createMockPackageService,
|
||||
createPackagePolicyServiceMock,
|
||||
} from '../../../../../fleet/server/mocks';
|
||||
import { AgentPolicyServiceInterface, AgentService } from '../../../../../fleet/server';
|
||||
import {
|
||||
EndpointFleetServicesFactory,
|
||||
EndpointFleetServicesInterface,
|
||||
} from '../endpoint_fleet_services';
|
||||
|
||||
const createCustomizedPackagePolicyService = () => {
|
||||
const service = createPackagePolicyServiceMock();
|
||||
|
@ -38,6 +43,7 @@ export interface EndpointMetadataServiceTestContextMock {
|
|||
agentPolicyService: jest.Mocked<AgentPolicyServiceInterface>;
|
||||
packagePolicyService: ReturnType<typeof createPackagePolicyServiceMock>;
|
||||
endpointMetadataService: EndpointMetadataService;
|
||||
fleetServices: EndpointFleetServicesInterface;
|
||||
}
|
||||
|
||||
export const createEndpointMetadataServiceTestContextMock = (
|
||||
|
@ -46,11 +52,18 @@ export const createEndpointMetadataServiceTestContextMock = (
|
|||
agentPolicyService: jest.Mocked<AgentPolicyServiceInterface> = createMockAgentPolicyService(),
|
||||
packagePolicyService: ReturnType<
|
||||
typeof createPackagePolicyServiceMock
|
||||
> = createCustomizedPackagePolicyService()
|
||||
> = createCustomizedPackagePolicyService(),
|
||||
packageService: ReturnType<typeof createMockPackageService> = createMockPackageService()
|
||||
): EndpointMetadataServiceTestContextMock => {
|
||||
const fleetServices = new EndpointFleetServicesFactory({
|
||||
agentService,
|
||||
packageService,
|
||||
packagePolicyService,
|
||||
agentPolicyService,
|
||||
}).asInternalUser();
|
||||
|
||||
const endpointMetadataService = new EndpointMetadataService(
|
||||
savedObjectsStart,
|
||||
agentService,
|
||||
agentPolicyService,
|
||||
packagePolicyService
|
||||
);
|
||||
|
@ -61,5 +74,6 @@ export const createEndpointMetadataServiceTestContextMock = (
|
|||
agentPolicyService,
|
||||
packagePolicyService,
|
||||
endpointMetadataService,
|
||||
fleetServices,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,12 +6,14 @@
|
|||
*/
|
||||
|
||||
import { coreMock } from '../../../../src/core/server/mocks';
|
||||
import { createFleetRequestHandlerContextMock } from '../../fleet/server/mocks';
|
||||
import { licensingMock } from '../../licensing/server/mocks';
|
||||
|
||||
function createCoreRequestHandlerContextMock() {
|
||||
return {
|
||||
core: coreMock.createRequestHandlerContext(),
|
||||
licensing: licensingMock.createRequestHandlerContext(),
|
||||
fleet: createFleetRequestHandlerContextMock(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from 'src/core/server';
|
||||
import { SearchRequest } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { getTrustedAppsList } from '../../endpoint/routes/trusted_apps/service';
|
||||
import { AgentService, AgentPolicyServiceInterface } from '../../../../fleet/server';
|
||||
import { AgentClient, AgentPolicyServiceInterface } from '../../../../fleet/server';
|
||||
import { ExceptionListClient } from '../../../../lists/server';
|
||||
import { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services';
|
||||
import { TELEMETRY_MAX_BUFFER_SIZE } from './constants';
|
||||
|
@ -32,7 +32,7 @@ import {
|
|||
|
||||
export class TelemetryReceiver {
|
||||
private readonly logger: Logger;
|
||||
private agentService?: AgentService;
|
||||
private agentClient?: AgentClient;
|
||||
private agentPolicyService?: AgentPolicyServiceInterface;
|
||||
private esClient?: ElasticsearchClient;
|
||||
private exceptionListClient?: ExceptionListClient;
|
||||
|
@ -52,7 +52,7 @@ export class TelemetryReceiver {
|
|||
exceptionListClient?: ExceptionListClient
|
||||
) {
|
||||
this.kibanaIndex = kibanaIndex;
|
||||
this.agentService = endpointContextService?.getAgentService();
|
||||
this.agentClient = endpointContextService?.getAgentService()?.asInternalUser;
|
||||
this.agentPolicyService = endpointContextService?.getAgentPolicyService();
|
||||
this.esClient = core?.elasticsearch.client.asInternalUser;
|
||||
this.exceptionListClient = exceptionListClient;
|
||||
|
@ -70,7 +70,7 @@ export class TelemetryReceiver {
|
|||
throw Error('elasticsearch client is unavailable: cannot retrieve fleet policy responses');
|
||||
}
|
||||
|
||||
return this.agentService?.listAgents(this.esClient, {
|
||||
return this.agentClient?.listAgents({
|
||||
perPage: this.max_records,
|
||||
showInactive: true,
|
||||
sortField: 'enrolled_at',
|
||||
|
|
|
@ -402,8 +402,6 @@ export class Plugin implements ISecuritySolutionPlugin {
|
|||
endpointMetadataService: new EndpointMetadataService(
|
||||
core.savedObjects,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
plugins.fleet?.agentService!,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
plugins.fleet?.agentPolicyService!,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
plugins.fleet?.packagePolicyService!,
|
||||
|
|
|
@ -9,6 +9,7 @@ import { set } from '@elastic/safer-lodash-set/fp';
|
|||
import { get, has, head } from 'lodash/fp';
|
||||
import {
|
||||
IScopedClusterClient,
|
||||
KibanaRequest,
|
||||
SavedObjectsClientContract,
|
||||
} from '../../../../../../../../../src/core/server';
|
||||
import { hostFieldsMap } from '../../../../../../common/ecs/ecs_fields';
|
||||
|
@ -180,35 +181,32 @@ export const getHostEndpoint = async (
|
|||
esClient: IScopedClusterClient;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
endpointContext: EndpointAppContext;
|
||||
request: KibanaRequest;
|
||||
}
|
||||
): Promise<EndpointFields | null> => {
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { esClient, endpointContext } = deps;
|
||||
const { esClient, endpointContext, request } = deps;
|
||||
const logger = endpointContext.logFactory.get('metadata');
|
||||
|
||||
try {
|
||||
const agentService = endpointContext.service.getAgentService();
|
||||
const fleetServices = endpointContext.service.getScopedFleetServices(request);
|
||||
const endpointMetadataService = endpointContext.service.getEndpointMetadataService();
|
||||
|
||||
if (!agentService) {
|
||||
throw new Error('agentService not available');
|
||||
}
|
||||
|
||||
const endpointData = await endpointContext.service
|
||||
.getEndpointMetadataService()
|
||||
const endpointData = await endpointMetadataService
|
||||
// Using `internalUser` ES client below due to the fact that Fleet data has been moved to
|
||||
// system indices (`.fleet*`). Because this is a readonly action, this should be ok to do
|
||||
// here until proper RBOC controls are implemented
|
||||
.getEnrichedHostMetadata(esClient.asInternalUser, id);
|
||||
.getEnrichedHostMetadata(esClient.asInternalUser, fleetServices, id);
|
||||
|
||||
const fleetAgentId = endpointData.metadata.elastic.agent.id;
|
||||
|
||||
const pendingActions = fleetAgentId
|
||||
? getPendingActionCounts(
|
||||
esClient.asInternalUser,
|
||||
endpointContext.service.getEndpointMetadataService(),
|
||||
endpointMetadataService,
|
||||
[fleetAgentId],
|
||||
endpointContext.experimentalFeatures.pendingActionResponsesWithAck
|
||||
)
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from './__mocks__';
|
||||
import {
|
||||
IScopedClusterClient,
|
||||
KibanaRequest,
|
||||
SavedObjectsClientContract,
|
||||
} from '../../../../../../../../../src/core/server';
|
||||
import { EndpointAppContext } from '../../../../../endpoint/types';
|
||||
|
@ -35,6 +36,7 @@ const mockDeps = {
|
|||
},
|
||||
service: {} as EndpointAppContextService,
|
||||
} as EndpointAppContext,
|
||||
request: {} as KibanaRequest,
|
||||
};
|
||||
|
||||
describe('hostDetails search strategy', () => {
|
||||
|
|
|
@ -23,6 +23,7 @@ import { formatHostItem, getHostEndpoint } from './helpers';
|
|||
import { EndpointAppContext } from '../../../../../endpoint/types';
|
||||
import {
|
||||
IScopedClusterClient,
|
||||
KibanaRequest,
|
||||
SavedObjectsClientContract,
|
||||
} from '../../../../../../../../../src/core/server';
|
||||
|
||||
|
@ -35,6 +36,7 @@ export const hostDetails: SecuritySolutionFactory<HostsQueries.details> = {
|
|||
esClient: IScopedClusterClient;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
endpointContext: EndpointAppContext;
|
||||
request: KibanaRequest;
|
||||
}
|
||||
): Promise<HostDetailsStrategyResponse> => {
|
||||
const aggregations = get('aggregations', response.rawResponse);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import type {
|
||||
IScopedClusterClient,
|
||||
KibanaRequest,
|
||||
SavedObjectsClientContract,
|
||||
} from '../../../../../../../src/core/server';
|
||||
import type {
|
||||
|
@ -29,6 +30,7 @@ export interface SecuritySolutionFactory<T extends FactoryQueryTypes> {
|
|||
esClient: IScopedClusterClient;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
endpointContext: EndpointAppContext;
|
||||
request: KibanaRequest;
|
||||
}
|
||||
) => Promise<StrategyResponseType<T>>;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ export const securitySolutionSearchStrategyProvider = <T extends FactoryQueryTyp
|
|||
esClient: deps.esClient,
|
||||
savedObjectsClient: deps.savedObjectsClient,
|
||||
endpointContext,
|
||||
request: deps.request,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import type { IRouter, RequestHandlerContext } from 'src/core/server';
|
||||
import type { ActionsApiRequestHandlerContext } from '../../actions/server';
|
||||
import type { AlertingApiRequestHandlerContext } from '../../alerting/server';
|
||||
import type { FleetRequestHandlerContext } from '../../fleet/server';
|
||||
import type { LicensingApiRequestHandlerContext } from '../../licensing/server';
|
||||
import type { ListsApiRequestHandlerContext, ExceptionListClient } from '../../lists/server';
|
||||
import type { IRuleDataService } from '../../rule_registry/server';
|
||||
|
@ -35,6 +36,7 @@ export interface SecuritySolutionRequestHandlerContext extends RequestHandlerCon
|
|||
alerting: AlertingApiRequestHandlerContext;
|
||||
licensing: LicensingApiRequestHandlerContext;
|
||||
lists?: ListsApiRequestHandlerContext;
|
||||
fleet?: FleetRequestHandlerContext['fleet'];
|
||||
}
|
||||
|
||||
export type SecuritySolutionPluginRouter = IRouter<SecuritySolutionRequestHandlerContext>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue