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

Rbac support #882

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
72 changes: 72 additions & 0 deletions cypress/integration/query_level_monitor_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,78 @@ describe('Query-Level Monitors', () => {
});
});

if (Cypress.env('security_enabled')) {
describe('can be created with backend roles', () => {
before(() => {
cy.deleteAllMonitors();
});

it('by extraction query', () => {
// mock enable backend roles
cy.intercept('GET', '/api/alerting/_settings', {
statusCode: 200,
body: {
ok: true,
resp: {
persistent: {
plugins: {
alerting: {
filter_by_backend_roles: 'true',
},
},
},
transient: {},
},
},
});

// Confirm we loaded empty monitor list
cy.contains('There are no existing monitors');

// Route us to create monitor page
cy.contains('Create monitor').click({ force: true });

// Select the Query-Level Monitor type
cy.get('[data-test-subj="queryLevelMonitorRadioCard"]').click();

// Select extraction query for method of definition
cy.get('[data-test-subj="extractionQueryEditorRadioCard"]').click();

// Wait for input to load and then type in the monitor name
cy.get('input[name="name"]').type(SAMPLE_MONITOR, { force: true });

// Wait for input to load and then type in the index name
cy.get('#index').type('*', { force: true });

// Wait for input to load and then type in the role
cy.get('#roles').click();
cy.contains('admin').click({ force: true });
cy.get('#roles').blur();

// Add a trigger
cy.contains('Add trigger').click({ force: true });

// Type in the trigger name
cy.get('input[name="triggerDefinitions[0].name"]').type(SAMPLE_TRIGGER, { force: true });

// Click the create button
cy.get('button').contains('Create').click({ force: true });

// Confirm we can see only one row in the trigger list by checking <caption> element
cy.contains('This table contains 1 row');

// Confirm we can see the new trigger
cy.contains(SAMPLE_TRIGGER);

// Go back to the Monitors list
cy.get('a').contains('Monitors').click({ force: true });

// Confirm we can see the created monitor in the list
cy.contains(SAMPLE_MONITOR);
});
});
}

describe('can be updated', () => {
beforeEach(() => {
cy.deleteAllMonitors();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ exports[`AddAlertingMonitor renders 1`] = `
\\"match_all\\": {}
}
}",
"roles": Array [],
"searchType": "graph",
"timeField": "",
"timezone": Array [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ exports[`AnomalyDetectors renders 1`] = `
\\"match_all\\": {}
}
}",
"roles": Array [],
"searchType": "graph",
"timeField": "",
"timezone": Array [],
Expand Down Expand Up @@ -126,6 +127,7 @@ exports[`AnomalyDetectors renders 1`] = `
\\"match_all\\": {}
}
}",
"roles": Array [],
"searchType": "graph",
"timeField": "",
"timezone": Array [],
Expand Down Expand Up @@ -261,6 +263,7 @@ exports[`AnomalyDetectors renders 1`] = `
\\"match_all\\": {}
}
}",
"roles": Array [],
"searchType": "graph",
"timeField": "",
"timezone": Array [],
Expand Down Expand Up @@ -354,6 +357,7 @@ exports[`AnomalyDetectors renders 1`] = `
\\"match_all\\": {}
}
}",
"roles": Array [],
"searchType": "graph",
"timeField": "",
"timezone": Array [],
Expand Down Expand Up @@ -495,6 +499,7 @@ exports[`AnomalyDetectors renders 1`] = `
\\"match_all\\": {}
}
}",
"roles": Array [],
"searchType": "graph",
"timeField": "",
"timezone": Array [],
Expand Down Expand Up @@ -588,6 +593,7 @@ exports[`AnomalyDetectors renders 1`] = `
\\"match_all\\": {}
}
}",
"roles": Array [],
"searchType": "graph",
"timeField": "",
"timezone": Array [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
getPerformanceModal,
RECOMMENDED_DURATION,
} from '../../components/QueryPerformance/QueryPerformance';
import MonitorSecurity from '../MonitorSecurity';
import { FILTER_BY_BACKEND_ROLES_SETTING_PATH } from './utils/constants';

export default class CreateMonitor extends Component {
static defaultProps = {
Expand Down Expand Up @@ -57,16 +59,47 @@ export default class CreateMonitor extends Component {
triggerToEdit,
createModalOpen: false,
formikBag: undefined,
filterByBackendRolesEnabled: false,
};

this.onCancel = this.onCancel.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.evaluateSubmission = this.evaluateSubmission.bind(this);
this.getSettings = this.getSettings.bind(this);
}

async getSettings() {
try {
const { httpClient } = this.props;
const response = await httpClient.get('../api/alerting/_settings');
if (response.ok) {
const { defaults, transient, persistent } = response.resp;
let filterByBackendRolesEnabled = _.get(
// If present, take the 'transient' setting.
transient,
FILTER_BY_BACKEND_ROLES_SETTING_PATH,
// Else take the 'persistent' setting.
_.get(
persistent,
FILTER_BY_BACKEND_ROLES_SETTING_PATH,
// Else take the 'default' setting.
_.get(defaults, FILTER_BY_BACKEND_ROLES_SETTING_PATH, false)
)
);
// Boolean settings are returned as strings (e.g., `"true"`, and `"false"`). Constructing boolean value from the string.
if (typeof filterByBackendRolesEnabled === 'string')
filterByBackendRolesEnabled = JSON.parse(filterByBackendRolesEnabled);
this.setState({ filterByBackendRolesEnabled: filterByBackendRolesEnabled });
}
} catch (e) {
console.log('Error while retrieving settings', e);
}
}

componentDidMount() {
const { httpClient } = this.props;

this.getSettings();
const updatePlugins = async () => {
const newPlugins = await getPlugins(httpClient);
this.setState({ plugins: newPlugins });
Expand Down Expand Up @@ -162,7 +195,7 @@ export default class CreateMonitor extends Component {
isDarkMode,
notificationService,
} = this.props;
const { createModalOpen, initialValues, plugins } = this.state;
const { createModalOpen, initialValues, plugins, filterByBackendRolesEnabled } = this.state;

return (
<div style={{ padding: '25px 50px' }}>
Expand Down Expand Up @@ -207,6 +240,13 @@ export default class CreateMonitor extends Component {
</>
) : null}

{isComposite && filterByBackendRolesEnabled ? (
<>
<EuiSpacer />
<MonitorSecurity values={values} httpClient={httpClient} errors={errors} />
</>
) : null}

<EuiSpacer />

{values.searchType !== SEARCH_TYPE.AD &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ exports[`CreateMonitor renders 1`] = `
\\"match_all\\": {}
}
}",
"roles": Array [],
"searchType": "graph",
"timeField": "",
"timezone": Array [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,90 @@ Object {
}
`;

exports[`formikToMonitor can build monitor with roles 1`] = `
Object {
"enabled": false,
"inputs": Array [
Object {
"search": Object {
"indices": Array [
"index1",
"index2",
],
"query": Object {
"aggregations": Object {},
"query": Object {
"bool": Object {
"filter": Array [
Object {
"range": Object {
"": Object {
"format": "epoch_millis",
"gte": "{{period_end}}||-1h",
"lte": "{{period_end}}",
},
},
},
],
},
},
"size": 0,
},
},
},
],
"monitor_type": "query_level_monitor",
"name": "random_name",
"rbac_roles": Array [
"test_bk_role_1",
"test_bk_role_2",
],
"schedule": Object {
"period": Object {
"interval": 1,
"unit": "MINUTES",
},
},
"triggers": Array [],
"type": "monitor",
"ui_metadata": Object {
"monitor_type": "query_level_monitor",
"schedule": Object {
"cronExpression": "0 */1 * * *",
"daily": 0,
"frequency": "interval",
"monthly": Object {
"day": 1,
"type": "day",
},
"period": Object {
"interval": 1,
"unit": "MINUTES",
},
"timezone": "America/Los_Angeles",
"weekly": Object {
"fri": false,
"mon": false,
"sat": false,
"sun": false,
"thur": false,
"tue": false,
"wed": false,
},
},
"search": Object {
"aggregations": Array [],
"bucketUnitOfTime": "h",
"bucketValue": 1,
"filters": Array [],
"groupBy": Array [],
"searchType": "graph",
"timeField": "",
},
},
}
`;

exports[`formikToQuery can build extraction query 1`] = `
Object {
"query": Object {
Expand Down Expand Up @@ -240,6 +324,13 @@ Object {
}
`;

exports[`formikToRoles can build roles 1`] = `
Array [
"test_bk_role_1",
"test_bk_role_2",
]
`;

exports[`formikToUiGraphQuery can build ui graph query 1`] = `
Object {
"aggregations": Object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const FORMIK_INITIAL_VALUES = {
associatedMonitorsList: [],
associatedMonitorsEditor: '',
preventVisualEditor: false,
roles: [],
};

export const FORMIK_INITIAL_AGG_VALUES = {
Expand Down Expand Up @@ -112,6 +113,8 @@ export const DEFAULT_DOCUMENT_LEVEL_QUERY = JSON.stringify(

export const DEFAULT_COMPOSITE_AGG_SIZE = 50;

export const FILTER_BY_BACKEND_ROLES_SETTING_PATH = 'plugins.alerting.filter_by_backend_roles';

export const METRIC_TOOLTIP_TEXT = 'Extracted statistics such as simple calculations of data.';
export const TIME_RANGE_TOOLTIP_TEXT = 'The time frame of data the plugin should monitor.';
export const FILTERS_TOOLTIP_TEXT =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export function formikToMonitor(values) {
monitor_type: values.monitor_type,
...monitorUiMetadata(),
},
...(formikToRoles(values).length && { rbac_roles: formikToRoles(values) }),
};
}

Expand All @@ -79,6 +80,7 @@ export function formikToMonitor(values) {
monitor_type: values.monitor_type,
...monitorUiMetadata(),
},
...(formikToRoles(values).length && { rbac_roles: formikToRoles(values) }),
};
}

Expand Down Expand Up @@ -213,6 +215,10 @@ export function formikToIndices(values) {
return values.index.map(({ label, value }) => (hasRemoteClusters ? value : label));
}

export function formikToRoles(values) {
return values.roles.map(({ label }) => label);
}

export function formikToQuery(values) {
const isGraph = values.searchType === SEARCH_TYPE.GRAPH;
return isGraph ? formikToGraphQuery(values) : formikToExtractionQuery(values);
Expand Down
Loading