Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions x-pack/plugins/fleet/common/constants/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}

Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/fleet/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { FleetPlugin } from './plugin';

export type {
AgentService,
AgentClient,
ESIndexPatternService,
PackageService,
AgentPolicyServiceInterface,
Expand All @@ -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: {
Expand Down
72 changes: 49 additions & 23 deletions x-pack/plugins/fleet/server/mocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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(),
};
}

Expand Down Expand Up @@ -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,
},
};
};
70 changes: 43 additions & 27 deletions x-pack/plugins/fleet/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {
SavedObjectsServiceStart,
HttpServiceSetup,
KibanaRequest,
ElasticsearchClient,
} from 'kibana/server';
import type { UsageCollectionSetup } from 'src/plugins/usage_collection/server';

Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -184,6 +180,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;
Expand All @@ -209,7 +207,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'],
Expand All @@ -234,7 +232,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: {
Expand All @@ -244,7 +242,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: {
Expand All @@ -257,19 +255,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>();
Expand Down Expand Up @@ -362,12 +374,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,
Expand All @@ -390,4 +397,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;
}
}
2 changes: 1 addition & 1 deletion x-pack/plugins/fleet/server/routes/security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
9 changes: 7 additions & 2 deletions x-pack/plugins/fleet/server/routes/setup/handlers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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(),
},
Expand Down
25 changes: 25 additions & 0 deletions x-pack/plugins/fleet/server/services/agents/agent_service.mock.ts
Original file line number Diff line number Diff line change
@@ -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,
};
Loading