Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [ML] Changing all calls to ML endpoints to use internal user (#70487) #71667

Merged
merged 1 commit into from
Jul 14, 2020
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
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