Skip to content

Commit

Permalink
[ML] Changing all calls to ML endpoints to use internal user (#70487)
Browse files Browse the repository at this point in the history
* [ML] Changing all calls to ML endpoints to use internal user

* updating alerting

* updating documentation

* [ML] Changing all calls to ML endpoints to use internal user

* updating alerting

* updating documentation

* fixing missed types

* adding authorization headers to endpoint calls

* correcting has privileges call

* updating security tests

* odd eslint error

* adding auth header to module setup

* fixing missing auth argument

* fixing delete DFA job permission checks

* removing debug test tag

* removing additional ml privilege checks

* adding authorization header to _evaluate

* updating alerting cluster client name

* code clean up

* changing authorizationHeader name

* updating alterting documentation

* fixing secondary credentials

* adding management links

* updating SIEM telemetry

* fixing merge conflicts

* granting access to index patterns

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
jgowdyelastic and elasticmachine authored Jul 14, 2020
1 parent 67f466a commit a1e511a
Show file tree
Hide file tree
Showing 99 changed files with 965 additions and 972 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ This is the primary function for an action type. Whenever the action needs to ex
| config | The decrypted configuration given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. |
| params | Parameters for the execution. These will be given at execution time by either an alert or manually provided when calling the plugin provided execute function. |
| services.callCluster(path, opts) | Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but runs in the context of the user who is calling the action when security is enabled. |
| services.getScopedCallCluster | This function scopes an instance of CallCluster by returning a `callCluster(path, opts)` function that runs in the context of the user who is calling the action when security is enabled. This must only be called with instances of CallCluster provided by core. |
| services.getLegacyScopedClusterClient | This function returns an instance of the LegacyScopedClusterClient scoped to the user who is calling the action when security is enabled. |
| services.savedObjectsClient | This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.<br><br>The scope of the saved objects client is tied to the user in context calling the execute API or the API key provided to the execute plugin function (only when security isenabled). |
| services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) |

Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/actions/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const createServicesMock = () => {
}
> = {
callCluster: elasticsearchServiceMock.createLegacyScopedClusterClient().callAsCurrentUser,
getScopedCallCluster: jest.fn(),
getLegacyScopedClusterClient: jest.fn(),
savedObjectsClient: savedObjectsClientMock.create(),
};
return mock;
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/actions/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,8 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
return (request) => ({
callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
savedObjectsClient: getScopedClient(request),
getScopedCallCluster(clusterClient: ILegacyClusterClient) {
return clusterClient.asScoped(request).callAsCurrentUser;
getLegacyScopedClusterClient(clusterClient: ILegacyClusterClient) {
return clusterClient.asScoped(request);
},
});
}
Expand Down
4 changes: 1 addition & 3 deletions x-pack/plugins/actions/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefine
export interface Services {
callCluster: ILegacyScopedClusterClient['callAsCurrentUser'];
savedObjectsClient: SavedObjectsClientContract;
getScopedCallCluster(
clusterClient: ILegacyClusterClient
): ILegacyScopedClusterClient['callAsCurrentUser'];
getLegacyScopedClusterClient(clusterClient: ILegacyClusterClient): ILegacyScopedClusterClient;
}

declare module 'src/core/server' {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/alerts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ This is the primary function for an alert type. Whenever the alert needs to exec
|---|---|
|services.callCluster(path, opts)|Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but in the context of the user who created the alert when security is enabled.|
|services.savedObjectsClient|This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.<br><br>The scope of the saved objects client is tied to the user who created the alert (only when security isenabled).|
|services.getScopedCallCluster|This function scopes an instance of CallCluster by returning a `callCluster(path, opts)` function that runs in the context of the user who created the alert when security is enabled. This must only be called with instances of CallCluster provided by core.|
|services.getLegacyScopedClusterClient|This function returns an instance of the LegacyScopedClusterClient scoped to the user who created the alert when security is enabled.|
|services.alertInstanceFactory(id)|This [alert instance factory](#alert-instance-factory) creates instances of alerts and must be used in order to execute actions. The id you give to the alert instance factory is a unique identifier to the alert instance.|
|services.log(tags, [data], [timestamp])|Use this to create server logs. (This is the same function as server.log)|
|startedAt|The date and time the alert type started execution.|
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/alerts/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const createAlertServicesMock = () => {
.fn<jest.Mocked<AlertInstance>, [string]>()
.mockReturnValue(alertInstanceFactoryMock),
callCluster: elasticsearchServiceMock.createLegacyScopedClusterClient().callAsCurrentUser,
getScopedCallCluster: jest.fn(),
getLegacyScopedClusterClient: jest.fn(),
savedObjectsClient: savedObjectsClientMock.create(),
};
};
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/alerts/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ export class AlertingPlugin {
return (request) => ({
callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
savedObjectsClient: this.getScopedClientWithAlertSavedObjectType(savedObjects, request),
getScopedCallCluster(clusterClient: ILegacyClusterClient) {
return clusterClient.asScoped(request).callAsCurrentUser;
getLegacyScopedClusterClient(clusterClient: ILegacyClusterClient) {
return clusterClient.asScoped(request);
},
});
}
Expand Down
4 changes: 1 addition & 3 deletions x-pack/plugins/alerts/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ declare module 'src/core/server' {
export interface Services {
callCluster: ILegacyScopedClusterClient['callAsCurrentUser'];
savedObjectsClient: SavedObjectsClientContract;
getScopedCallCluster(
clusterClient: ILegacyClusterClient
): ILegacyScopedClusterClient['callAsCurrentUser'];
getLegacyScopedClusterClient(clusterClient: ILegacyClusterClient): ILegacyScopedClusterClient;
}

export interface AlertServices extends Services {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/apm/server/lib/helpers/setup_request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function getMlSetup(context: APMRequestHandlerContext, request: KibanaRequest) {
return;
}
const ml = context.plugins.ml;
const mlClient = ml.mlClient.asScoped(request).callAsCurrentUser;
const mlClient = ml.mlClient.asScoped(request);
return {
mlSystem: ml.mlSystemProvider(mlClient, request),
anomalyDetectors: ml.anomalyDetectorsProvider(mlClient, request),
Expand Down
7 changes: 2 additions & 5 deletions x-pack/plugins/infra/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,9 @@ export class InfraServerPlugin {
core.http.registerRouteHandlerContext(
'infra',
(context, request): InfraRequestHandlerContext => {
const mlSystem =
context.ml &&
plugins.ml?.mlSystemProvider(context.ml?.mlClient.callAsCurrentUser, request);
const mlSystem = context.ml && plugins.ml?.mlSystemProvider(context.ml?.mlClient, request);
const mlAnomalyDetectors =
context.ml &&
plugins.ml?.anomalyDetectorsProvider(context.ml?.mlClient.callAsCurrentUser, request);
context.ml && plugins.ml?.anomalyDetectorsProvider(context.ml?.mlClient, request);
const spaceId = plugins.spaces?.spacesService.getSpaceId(request) || 'default';

return {
Expand Down
28 changes: 22 additions & 6 deletions x-pack/plugins/ml/common/types/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { KibanaRequest } from 'kibana/server';
import { PLUGIN_ID } from '../constants/app';

export const userMlCapabilities = {
canAccessML: false,
Expand Down Expand Up @@ -69,16 +70,31 @@ export function getDefaultCapabilities(): MlCapabilities {
export function getPluginPrivileges() {
const userMlCapabilitiesKeys = Object.keys(userMlCapabilities);
const adminMlCapabilitiesKeys = Object.keys(adminMlCapabilities);
const allMlCapabilities = [...adminMlCapabilitiesKeys, ...userMlCapabilitiesKeys];
const allMlCapabilitiesKeys = [...adminMlCapabilitiesKeys, ...userMlCapabilitiesKeys];
// TODO: include ML in base privileges for the `8.0` release: https://github.com/elastic/kibana/issues/71422
const privilege = {
app: [PLUGIN_ID, 'kibana'],
excludeFromBasePrivileges: true,
management: {
insightsAndAlerting: ['jobsListLink'],
},
catalogue: [PLUGIN_ID],
savedObject: {
all: [],
read: ['index-pattern', 'search'],
},
};

return {
admin: {
...privilege,
api: allMlCapabilitiesKeys.map((k) => `ml:${k}`),
ui: allMlCapabilitiesKeys,
},
user: {
ui: userMlCapabilitiesKeys,
...privilege,
api: userMlCapabilitiesKeys.map((k) => `ml:${k}`),
},
admin: {
ui: allMlCapabilities,
api: allMlCapabilities.map((k) => `ml:${k}`),
ui: userMlCapabilitiesKeys,
},
};
}
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/ml/common/types/kibana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { IndexPatternAttributes } from 'src/plugins/data/common';

export type IndexPatternTitle = string;

export type callWithRequestType = (action: string, params?: any) => Promise<any>;

export interface Route {
id: string;
k7Breadcrumbs: () => any;
Expand Down
12 changes: 0 additions & 12 deletions x-pack/plugins/ml/jsconfig.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ let _capabilities: MlCapabilities = getDefaultCapabilities();

export function checkGetManagementMlJobsResolver() {
return new Promise<{ mlFeatureEnabledInSpace: boolean }>((resolve, reject) => {
getManageMlCapabilities().then(
({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace }) => {
getManageMlCapabilities()
.then(({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace }) => {
_capabilities = capabilities;
// Loop through all capabilities to ensure they are all set to true.
const isManageML = Object.values(_capabilities).every((p) => p === true);
Expand All @@ -28,62 +28,80 @@ export function checkGetManagementMlJobsResolver() {
window.location.href = ACCESS_DENIED_PATH;
return reject();
}
}
);
})
.catch((e) => {
window.location.href = ACCESS_DENIED_PATH;
return reject();
});
});
}

export function checkGetJobsCapabilitiesResolver(): Promise<MlCapabilities> {
return new Promise((resolve, reject) => {
getCapabilities().then(({ capabilities, isPlatinumOrTrialLicense }) => {
_capabilities = capabilities;
// the minimum privilege for using ML with a platinum or trial license is being able to get the transforms list.
// all other functionality is controlled by the return capabilities object.
// if the license is basic (isPlatinumOrTrialLicense === false) then do not redirect,
// allow the promise to resolve as the separate license check will redirect then user to
// a basic feature
if (_capabilities.canGetJobs || isPlatinumOrTrialLicense === false) {
return resolve(_capabilities);
} else {
getCapabilities()
.then(({ capabilities, isPlatinumOrTrialLicense }) => {
_capabilities = capabilities;
// the minimum privilege for using ML with a platinum or trial license is being able to get the transforms list.
// all other functionality is controlled by the return capabilities object.
// if the license is basic (isPlatinumOrTrialLicense === false) then do not redirect,
// allow the promise to resolve as the separate license check will redirect then user to
// a basic feature
if (_capabilities.canGetJobs || isPlatinumOrTrialLicense === false) {
return resolve(_capabilities);
} else {
window.location.href = '#/access-denied';
return reject();
}
})
.catch((e) => {
window.location.href = '#/access-denied';
return reject();
}
});
});
});
}

export function checkCreateJobsCapabilitiesResolver(): Promise<MlCapabilities> {
return new Promise((resolve, reject) => {
getCapabilities().then(({ capabilities, isPlatinumOrTrialLicense }) => {
_capabilities = capabilities;
// if the license is basic (isPlatinumOrTrialLicense === false) then do not redirect,
// allow the promise to resolve as the separate license check will redirect then user to
// a basic feature
if (_capabilities.canCreateJob || isPlatinumOrTrialLicense === false) {
return resolve(_capabilities);
} else {
// if the user has no permission to create a job,
// redirect them back to the Transforms Management page
getCapabilities()
.then(({ capabilities, isPlatinumOrTrialLicense }) => {
_capabilities = capabilities;
// if the license is basic (isPlatinumOrTrialLicense === false) then do not redirect,
// allow the promise to resolve as the separate license check will redirect then user to
// a basic feature
if (_capabilities.canCreateJob || isPlatinumOrTrialLicense === false) {
return resolve(_capabilities);
} else {
// if the user has no permission to create a job,
// redirect them back to the Transforms Management page
window.location.href = '#/jobs';
return reject();
}
})
.catch((e) => {
window.location.href = '#/jobs';
return reject();
}
});
});
});
}

export function checkFindFileStructurePrivilegeResolver(): Promise<MlCapabilities> {
return new Promise((resolve, reject) => {
getCapabilities().then(({ capabilities }) => {
_capabilities = capabilities;
// the minimum privilege for using ML with a basic license is being able to use the datavisualizer.
// all other functionality is controlled by the return _capabilities object
if (_capabilities.canFindFileStructure) {
return resolve(_capabilities);
} else {
getCapabilities()
.then(({ capabilities }) => {
_capabilities = capabilities;
// the minimum privilege for using ML with a basic license is being able to use the datavisualizer.
// all other functionality is controlled by the return _capabilities object
if (_capabilities.canFindFileStructure) {
return resolve(_capabilities);
} else {
window.location.href = '#/access-denied';
return reject();
}
})
.catch((e) => {
window.location.href = '#/access-denied';
return reject();
}
});
});
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { ml } from './ml_api_service';
import { getIndexPatternAndSavedSearch } from '../util/index_utils';

// called in the angular routing resolve block to initialize the
// called in the routing resolve block to initialize the
// newJobCapsService with the currently selected index pattern
export function loadNewJobCapabilities(
indexPatternId: string,
Expand Down
Loading

0 comments on commit a1e511a

Please sign in to comment.