Skip to content

Commit

Permalink
Merge branch 'main' into refactor-integrations-to-not-depend-on-main-…
Browse files Browse the repository at this point in the history
…page
  • Loading branch information
achyutjhunjhunwala authored Jul 3, 2024
2 parents 4338fb9 + 988795d commit 543b993
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ export const ERROR_API_KEY_NOT_FOUND = 'api_key_not_found';
export const ERROR_API_KEY_NOT_VALID = 'api_key_not_valid';
export const ERROR_USER_NOT_AUTHORIZED = 'user_not_authorized';
export const ERROR_API_KEY_SERVICE_DISABLED = 'api_key_service_disabled';
export const ERROR_PARTIAL_BUILTIN_INSTALLATION = 'partial_builtin_installation';
export const ERROR_DEFINITION_STOPPED = 'error_definition_stopped';
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { generateLatestIngestPipelineId } from './ingest_pipeline/generate_lates
import { generateHistoryTransformId } from './transform/generate_history_transform_id';
import { generateLatestTransformId } from './transform/generate_latest_transform_id';
import { BUILT_IN_ID_PREFIX } from './built_in';
import { EntityDefinitionWithState } from './types';

export async function findEntityDefinitions({
soClient,
Expand All @@ -29,7 +30,7 @@ export async function findEntityDefinitions({
id?: string;
page?: number;
perPage?: number;
}): Promise<Array<EntityDefinition & { state: { installed: boolean; running: boolean } }>> {
}): Promise<EntityDefinitionWithState[]> {
const filter = compact([
typeof builtIn === 'boolean'
? `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${BUILT_IN_ID_PREFIX}*)`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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 { EntityDefinition } from '@kbn/entities-schema';

export type EntityDefinitionWithState = EntityDefinition & {
state: { installed: boolean; running: boolean };
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,80 @@ import { SetupRouteOptions } from '../types';
import { ENTITY_INTERNAL_API_PREFIX } from '../../../common/constants_entities';
import { ManagedEntityEnabledResponse } from '../../../common/types_api';
import { checkIfEntityDiscoveryAPIKeyIsValid, readEntityDiscoveryAPIKey } from '../../lib/auth';
import { ERROR_API_KEY_NOT_FOUND, ERROR_API_KEY_NOT_VALID } from '../../../common/errors';
import {
ERROR_API_KEY_NOT_FOUND,
ERROR_API_KEY_NOT_VALID,
ERROR_DEFINITION_STOPPED,
ERROR_PARTIAL_BUILTIN_INSTALLATION,
} from '../../../common/errors';
import { findEntityDefinitions } from '../../lib/entities/find_entity_definition';
import { builtInDefinitions } from '../../lib/entities/built_in';

export function checkEntityDiscoveryEnabledRoute<T extends RequestHandlerContext>({
router,
server,
logger,
}: SetupRouteOptions<T>) {
router.get<unknown, unknown, ManagedEntityEnabledResponse>(
{
path: `${ENTITY_INTERNAL_API_PREFIX}/managed/enablement`,
validate: false,
},
async (context, req, res) => {
server.logger.debug('reading entity discovery API key from saved object');
const apiKey = await readEntityDiscoveryAPIKey(server);
try {
logger.debug('reading entity discovery API key from saved object');
const apiKey = await readEntityDiscoveryAPIKey(server);

if (apiKey === undefined) {
return res.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_FOUND } });
}
if (apiKey === undefined) {
return res.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_FOUND } });
}

server.logger.debug('validating existing entity discovery API key');
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, apiKey);
logger.debug('validating existing entity discovery API key');
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, apiKey);

if (!isValid) {
return res.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_VALID } });
}
if (!isValid) {
return res.ok({ body: { enabled: false, reason: ERROR_API_KEY_NOT_VALID } });
}

const fakeRequest = getFakeKibanaRequest({ id: apiKey.id, api_key: apiKey.apiKey });
const soClient = server.core.savedObjects.getScopedClient(fakeRequest);
const esClient = server.core.elasticsearch.client.asScoped(fakeRequest).asCurrentUser;

const fakeRequest = getFakeKibanaRequest({ id: apiKey.id, api_key: apiKey.apiKey });
const soClient = server.core.savedObjects.getScopedClient(fakeRequest);
const esClient = server.core.elasticsearch.client.asScoped(fakeRequest).asCurrentUser;
const entityDiscoveryState = await Promise.all(
builtInDefinitions.map(async (builtInDefinition) => {
const definitions = await findEntityDefinitions({
esClient,
soClient,
id: builtInDefinition.id,
});

const entityDiscoveryEnabled = await Promise.all(
builtInDefinitions.map(async (builtInDefinition) => {
const [definition] = await findEntityDefinitions({
esClient,
soClient,
id: builtInDefinition.id,
});
return definitions[0];
})
).then((results) =>
results.reduce(
(state, definition) => {
return {
installed: Boolean(state.installed && definition?.state.installed),
running: Boolean(state.running && definition?.state.running),
};
},
{ installed: true, running: true }
)
);

return definition && definition.state.installed && definition.state.running;
})
).then((results) => results.every(Boolean));
if (!entityDiscoveryState.installed) {
return res.ok({ body: { enabled: false, reason: ERROR_PARTIAL_BUILTIN_INSTALLATION } });
}

return res.ok({ body: { enabled: entityDiscoveryEnabled } });
if (!entityDiscoveryState.running) {
return res.ok({ body: { enabled: false, reason: ERROR_DEFINITION_STOPPED } });
}

return res.ok({ body: { enabled: true } });
} catch (err) {
logger.error(err);
return res.customError({ statusCode: 500, body: err });
}
}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,37 @@ export function disableEntityDiscoveryRoute<T extends RequestHandlerContext>({
validate: false,
},
async (context, req, res) => {
server.logger.debug('reading entity discovery API key from saved object');
const apiKey = await readEntityDiscoveryAPIKey(server);
try {
server.logger.debug('reading entity discovery API key from saved object');
const apiKey = await readEntityDiscoveryAPIKey(server);

if (apiKey === undefined) {
return res.ok({ body: { success: false, reason: ERROR_API_KEY_NOT_FOUND } });
}
if (apiKey === undefined) {
return res.ok({ body: { success: false, reason: ERROR_API_KEY_NOT_FOUND } });
}

server.logger.debug('validating existing entity discovery API key');
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, apiKey);
server.logger.debug('validating existing entity discovery API key');
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, apiKey);

if (!isValid) {
return res.ok({ body: { success: false, reason: ERROR_API_KEY_NOT_VALID } });
}
if (!isValid) {
return res.ok({ body: { success: false, reason: ERROR_API_KEY_NOT_VALID } });
}

const fakeRequest = getFakeKibanaRequest({ id: apiKey.id, api_key: apiKey.apiKey });
const soClient = server.core.savedObjects.getScopedClient(fakeRequest);
const esClient = server.core.elasticsearch.client.asScoped(fakeRequest).asCurrentUser;
const fakeRequest = getFakeKibanaRequest({ id: apiKey.id, api_key: apiKey.apiKey });
const soClient = server.core.savedObjects.getScopedClient(fakeRequest);
const esClient = server.core.elasticsearch.client.asScoped(fakeRequest).asCurrentUser;

await uninstallBuiltInEntityDefinitions({ soClient, esClient, logger });
await uninstallBuiltInEntityDefinitions({ soClient, esClient, logger });

await deleteEntityDiscoveryAPIKey((await context.core).savedObjects.client);
await server.security.authc.apiKeys.invalidateAsInternalUser({
ids: [apiKey.id],
});
await deleteEntityDiscoveryAPIKey((await context.core).savedObjects.client);
await server.security.authc.apiKeys.invalidateAsInternalUser({
ids: [apiKey.id],
});

return res.ok();
return res.ok({ body: { success: true } });
} catch (err) {
logger.error(err);
return res.customError({ statusCode: 500, body: err });
}
}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,69 +35,74 @@ export function enableEntityDiscoveryRoute<T extends RequestHandlerContext>({
validate: false,
},
async (context, req, res) => {
const apiKeysEnabled = await checkIfAPIKeysAreEnabled(server);
if (!apiKeysEnabled) {
return res.ok({
body: {
success: false,
reason: ERROR_API_KEY_SERVICE_DISABLED,
message:
'API key service is not enabled; try configuring `xpack.security.authc.api_key.enabled` in your elasticsearch config',
},
});
}
try {
const apiKeysEnabled = await checkIfAPIKeysAreEnabled(server);
if (!apiKeysEnabled) {
return res.ok({
body: {
success: false,
reason: ERROR_API_KEY_SERVICE_DISABLED,
message:
'API key service is not enabled; try configuring `xpack.security.authc.api_key.enabled` in your elasticsearch config',
},
});
}

const esClient = (await context.core).elasticsearch.client.asCurrentUser;
const canEnable = await canEnableEntityDiscovery(esClient);
if (!canEnable) {
return res.ok({
body: {
success: false,
reason: ERROR_USER_NOT_AUTHORIZED,
message:
'Current Kibana user does not have the required permissions to enable entity discovery',
},
});
}
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
const canEnable = await canEnableEntityDiscovery(esClient);
if (!canEnable) {
return res.ok({
body: {
success: false,
reason: ERROR_USER_NOT_AUTHORIZED,
message:
'Current Kibana user does not have the required permissions to enable entity discovery',
},
});
}

const soClient = (await context.core).savedObjects.getClient({
includedHiddenTypes: [EntityDiscoveryApiKeyType.name],
});
const soClient = (await context.core).savedObjects.getClient({
includedHiddenTypes: [EntityDiscoveryApiKeyType.name],
});

const existingApiKey = await readEntityDiscoveryAPIKey(server);
const existingApiKey = await readEntityDiscoveryAPIKey(server);

if (existingApiKey !== undefined) {
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, existingApiKey);
if (existingApiKey !== undefined) {
const isValid = await checkIfEntityDiscoveryAPIKeyIsValid(server, existingApiKey);

if (!isValid) {
await deleteEntityDiscoveryAPIKey(soClient);
await server.security.authc.apiKeys.invalidateAsInternalUser({
ids: [existingApiKey.id],
});
if (!isValid) {
await deleteEntityDiscoveryAPIKey(soClient);
await server.security.authc.apiKeys.invalidateAsInternalUser({
ids: [existingApiKey.id],
});
}
}
}

const apiKey = await generateEntityDiscoveryAPIKey(server, req);
const apiKey = await generateEntityDiscoveryAPIKey(server, req);

if (apiKey === undefined) {
throw new Error('could not generate entity discovery API key');
}
if (apiKey === undefined) {
throw new Error('could not generate entity discovery API key');
}

await saveEntityDiscoveryAPIKey(soClient, apiKey);
await saveEntityDiscoveryAPIKey(soClient, apiKey);

const fakeRequest = getFakeKibanaRequest({ id: apiKey.id, api_key: apiKey.apiKey });
const scopedSoClient = server.core.savedObjects.getScopedClient(fakeRequest);
const scopedEsClient = server.core.elasticsearch.client.asScoped(fakeRequest).asCurrentUser;
const fakeRequest = getFakeKibanaRequest({ id: apiKey.id, api_key: apiKey.apiKey });
const scopedSoClient = server.core.savedObjects.getScopedClient(fakeRequest);
const scopedEsClient = server.core.elasticsearch.client.asScoped(fakeRequest).asCurrentUser;

await installBuiltInEntityDefinitions({
logger,
builtInDefinitions,
spaceId: 'default',
esClient: scopedEsClient,
soClient: scopedSoClient,
});
await installBuiltInEntityDefinitions({
logger,
builtInDefinitions,
spaceId: 'default',
esClient: scopedEsClient,
soClient: scopedSoClient,
});

return res.ok({ body: { success: true } });
return res.ok({ body: { success: true } });
} catch (err) {
logger.error(err);
return res.customError({ statusCode: 500, body: err });
}
}
);
}

0 comments on commit 543b993

Please sign in to comment.