mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AccessControl: Remove legacy frontend AC checks (#76187)
* remove legacy ac checks * fix RBAC disabled tests * add permissions for tests to work * fix unifiedalertstatesworker test
This commit is contained in:
@@ -181,6 +181,7 @@ export interface GrafanaConfig {
|
|||||||
autoAssignOrg: boolean;
|
autoAssignOrg: boolean;
|
||||||
verifyEmailEnabled: boolean;
|
verifyEmailEnabled: boolean;
|
||||||
oauth: OAuthSettings;
|
oauth: OAuthSettings;
|
||||||
|
/** @deprecated always set to true. */
|
||||||
rbacEnabled: boolean;
|
rbacEnabled: boolean;
|
||||||
disableUserSignUp: boolean;
|
disableUserSignUp: boolean;
|
||||||
loginHint: string;
|
loginHint: string;
|
||||||
|
|||||||
@@ -97,11 +97,9 @@ export class ContextSrv {
|
|||||||
|
|
||||||
async fetchUserPermissions() {
|
async fetchUserPermissions() {
|
||||||
try {
|
try {
|
||||||
if (this.accessControlEnabled()) {
|
this.user.permissions = await getBackendSrv().get('/api/access-control/user/actions', {
|
||||||
this.user.permissions = await getBackendSrv().get('/api/access-control/user/actions', {
|
reloadcache: true,
|
||||||
reloadcache: true,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@@ -125,31 +123,17 @@ export class ContextSrv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accessControlEnabled(): boolean {
|
|
||||||
return config.rbacEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
licensedAccessControlEnabled(): boolean {
|
licensedAccessControlEnabled(): boolean {
|
||||||
return featureEnabled('accesscontrol') && config.rbacEnabled;
|
return featureEnabled('accesscontrol');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether user has required permission
|
// Checks whether user has required permission
|
||||||
hasPermissionInMetadata(action: AccessControlAction | string, object: WithAccessControlMetadata): boolean {
|
hasPermissionInMetadata(action: AccessControlAction | string, object: WithAccessControlMetadata): boolean {
|
||||||
// Fallback if access control disabled
|
|
||||||
if (!this.accessControlEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!object.accessControl?.[action];
|
return !!object.accessControl?.[action];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether user has required permission
|
// Checks whether user has required permission
|
||||||
hasPermission(action: AccessControlAction | string): boolean {
|
hasPermission(action: AccessControlAction | string): boolean {
|
||||||
// Fallback if access control disabled
|
|
||||||
if (!this.accessControlEnabled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!this.user.permissions?.[action];
|
return !!this.user.permissions?.[action];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,23 +164,14 @@ export class ContextSrv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasAccessToExplore() {
|
hasAccessToExplore() {
|
||||||
if (this.accessControlEnabled()) {
|
return this.hasPermission(AccessControlAction.DataSourcesExplore) && config.exploreEnabled;
|
||||||
return this.hasPermission(AccessControlAction.DataSourcesExplore) && config.exploreEnabled;
|
|
||||||
}
|
|
||||||
return (this.isEditor || config.viewersCanEdit) && config.exploreEnabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAccess(action: string, fallBack: boolean): boolean {
|
hasAccess(action: string, fallBack: boolean): boolean {
|
||||||
if (!this.accessControlEnabled()) {
|
|
||||||
return fallBack;
|
|
||||||
}
|
|
||||||
return this.hasPermission(action);
|
return this.hasPermission(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasAccessInMetadata(action: string, object: WithAccessControlMetadata, fallBack: boolean): boolean {
|
hasAccessInMetadata(action: string, object: WithAccessControlMetadata, fallBack: boolean): boolean {
|
||||||
if (!this.accessControlEnabled()) {
|
|
||||||
return fallBack;
|
|
||||||
}
|
|
||||||
return this.hasPermissionInMetadata(action, object);
|
return this.hasPermissionInMetadata(action, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import config from '../../core/config';
|
|
||||||
|
|
||||||
// accessControlQueryParam adds an additional accesscontrol=true param to params when accesscontrol is enabled
|
// accessControlQueryParam adds an additional accesscontrol=true param to params when accesscontrol is enabled
|
||||||
export function accessControlQueryParam(params = {}) {
|
export function accessControlQueryParam(params = {}) {
|
||||||
if (!config.rbacEnabled) {
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
return { ...params, accesscontrol: true };
|
return { ...params, accesscontrol: true };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -377,21 +377,17 @@ export class AddToOrgModal extends PureComponent<AddToOrgModalProps, AddToOrgMod
|
|||||||
<OrgPicker inputId="new-org-input" onSelected={this.onOrgSelect} excludeOrgs={userOrgs} autoFocus />
|
<OrgPicker inputId="new-org-input" onSelected={this.onOrgSelect} excludeOrgs={userOrgs} autoFocus />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Role" disabled={selectedOrg === null}>
|
<Field label="Role" disabled={selectedOrg === null}>
|
||||||
{contextSrv.accessControlEnabled() ? (
|
<UserRolePicker
|
||||||
<UserRolePicker
|
userId={user?.id || 0}
|
||||||
userId={user?.id || 0}
|
orgId={selectedOrg?.id}
|
||||||
orgId={selectedOrg?.id}
|
basicRole={role}
|
||||||
basicRole={role}
|
onBasicRoleChange={this.onOrgRoleChange}
|
||||||
onBasicRoleChange={this.onOrgRoleChange}
|
basicRoleDisabled={false}
|
||||||
basicRoleDisabled={false}
|
roleOptions={roleOptions}
|
||||||
roleOptions={roleOptions}
|
apply={true}
|
||||||
apply={true}
|
onApplyRoles={this.onRoleUpdate}
|
||||||
onApplyRoles={this.onRoleUpdate}
|
pendingRoles={this.state.pendingRoles}
|
||||||
pendingRoles={this.state.pendingRoles}
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<OrgRolePicker inputId="new-org-role-input" value={role} onChange={this.onOrgRoleChange} />
|
|
||||||
)}
|
|
||||||
</Field>
|
</Field>
|
||||||
<Modal.ButtonRow>
|
<Modal.ButtonRow>
|
||||||
<HorizontalGroup spacing="md" justify="center">
|
<HorizontalGroup spacing="md" justify="center">
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import { byRole, byTestId, byText } from 'testing-library-selector';
|
|||||||
|
|
||||||
import { locationService, setDataSourceSrv } from '@grafana/runtime';
|
import { locationService, setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { AlertManagerCortexConfig, MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
|
import { AlertManagerCortexConfig, MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
|
|
||||||
import MuteTimings from './MuteTimings';
|
import MuteTimings from './MuteTimings';
|
||||||
import { fetchAlertManagerConfig, updateAlertManagerConfig } from './api/alertmanager';
|
import { fetchAlertManagerConfig, updateAlertManagerConfig } from './api/alertmanager';
|
||||||
import { disableRBAC, mockDataSource, MockDataSourceSrv } from './mocks';
|
import { grantUserPermissions, mockDataSource, MockDataSourceSrv } from './mocks';
|
||||||
import { DataSourceType } from './utils/datasource';
|
import { DataSourceType } from './utils/datasource';
|
||||||
|
|
||||||
jest.mock('./api/alertmanager');
|
jest.mock('./api/alertmanager');
|
||||||
@@ -105,10 +106,11 @@ describe('Mute timings', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
||||||
resetMocks();
|
resetMocks();
|
||||||
|
// FIXME: scope down
|
||||||
|
grantUserPermissions(Object.values(AccessControlAction));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a new mute timing', async () => {
|
it('creates a new mute timing', async () => {
|
||||||
disableRBAC();
|
|
||||||
renderMuteTimings();
|
renderMuteTimings();
|
||||||
|
|
||||||
await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled());
|
await waitFor(() => expect(mocks.api.fetchAlertManagerConfig).toHaveBeenCalled());
|
||||||
|
|||||||
@@ -13,13 +13,14 @@ import { toKeyedAction } from 'app/features/variables/state/keyedVariablesReduce
|
|||||||
import { PrometheusDatasource } from 'app/plugins/datasource/prometheus/datasource';
|
import { PrometheusDatasource } from 'app/plugins/datasource/prometheus/datasource';
|
||||||
import { PromOptions } from 'app/plugins/datasource/prometheus/types';
|
import { PromOptions } from 'app/plugins/datasource/prometheus/types';
|
||||||
import { configureStore } from 'app/store/configureStore';
|
import { configureStore } from 'app/store/configureStore';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { PanelAlertTabContent } from './PanelAlertTabContent';
|
import { PanelAlertTabContent } from './PanelAlertTabContent';
|
||||||
import { fetchRules } from './api/prometheus';
|
import { fetchRules } from './api/prometheus';
|
||||||
import { fetchRulerRules } from './api/ruler';
|
import { fetchRulerRules } from './api/ruler';
|
||||||
import {
|
import {
|
||||||
disableRBAC,
|
grantUserPermissions,
|
||||||
mockDataSource,
|
mockDataSource,
|
||||||
MockDataSourceSrv,
|
MockDataSourceSrv,
|
||||||
mockPromAlertingRule,
|
mockPromAlertingRule,
|
||||||
@@ -180,6 +181,14 @@ const ui = {
|
|||||||
describe('PanelAlertTabContent', () => {
|
describe('PanelAlertTabContent', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingRuleRead,
|
||||||
|
AccessControlAction.AlertingRuleUpdate,
|
||||||
|
AccessControlAction.AlertingRuleDelete,
|
||||||
|
AccessControlAction.AlertingRuleCreate,
|
||||||
|
AccessControlAction.AlertingRuleExternalRead,
|
||||||
|
AccessControlAction.AlertingRuleExternalWrite,
|
||||||
|
]);
|
||||||
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));
|
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));
|
||||||
const dsService = new MockDataSourceSrv(dataSources);
|
const dsService = new MockDataSourceSrv(dataSources);
|
||||||
dsService.datasources[dataSources.prometheus.uid] = new PrometheusDatasource(
|
dsService.datasources[dataSources.prometheus.uid] = new PrometheusDatasource(
|
||||||
@@ -187,7 +196,6 @@ describe('PanelAlertTabContent', () => {
|
|||||||
) as DataSourceApi;
|
) as DataSourceApi;
|
||||||
dsService.datasources[dataSources.default.uid] = new PrometheusDatasource(dataSources.default) as DataSourceApi;
|
dsService.datasources[dataSources.default.uid] = new PrometheusDatasource(dataSources.default) as DataSourceApi;
|
||||||
setDataSourceSrv(dsService);
|
setDataSourceSrv(dsService);
|
||||||
disableRBAC();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Will take into account panel maxDataPoints', async () => {
|
it('Will take into account panel maxDataPoints', async () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { byRole, byText } from 'testing-library-selector';
|
|||||||
|
|
||||||
import { setDataSourceSrv } from '@grafana/runtime';
|
import { setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
import { PromApiFeatures, PromApplication } from 'app/types/unified-alerting-dto';
|
import { PromApiFeatures, PromApplication } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { searchFolders } from '../../manage-dashboards/state/actions';
|
import { searchFolders } from '../../manage-dashboards/state/actions';
|
||||||
@@ -13,7 +14,7 @@ import { searchFolders } from '../../manage-dashboards/state/actions';
|
|||||||
import { discoverFeatures } from './api/buildInfo';
|
import { discoverFeatures } from './api/buildInfo';
|
||||||
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
||||||
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
||||||
import { disableRBAC, mockDataSource, MockDataSourceSrv } from './mocks';
|
import { grantUserPermissions, mockDataSource, MockDataSourceSrv } from './mocks';
|
||||||
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
|
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
|
||||||
import * as config from './utils/config';
|
import * as config from './utils/config';
|
||||||
import { DataSourceType } from './utils/datasource';
|
import { DataSourceType } from './utils/datasource';
|
||||||
@@ -139,10 +140,10 @@ describe('RuleEditor cloud: checking editable data sources', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
contextSrv.isEditor = true;
|
contextSrv.isEditor = true;
|
||||||
contextSrv.hasEditPermissionInFolders = true;
|
contextSrv.hasEditPermissionInFolders = true;
|
||||||
|
// grant all permissions in AccessControlActionEnum
|
||||||
|
grantUserPermissions(Object.values(AccessControlAction));
|
||||||
});
|
});
|
||||||
|
|
||||||
disableRBAC();
|
|
||||||
|
|
||||||
it('for cloud alerts, should only allow to select editable rules sources', async () => {
|
it('for cloud alerts, should only allow to select editable rules sources', async () => {
|
||||||
mocks.api.discoverFeatures.mockImplementation(async (dataSourceName) => {
|
mocks.api.discoverFeatures.mockImplementation(async (dataSourceName) => {
|
||||||
if (dataSourceName === 'loki with ruler' || dataSourceName === 'cortex with ruler') {
|
if (dataSourceName === 'loki with ruler' || dataSourceName === 'cortex with ruler') {
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ import { clickSelectOption } from 'test/helpers/selectOptionInTest';
|
|||||||
import { byRole } from 'testing-library-selector';
|
import { byRole } from 'testing-library-selector';
|
||||||
|
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
|
|
||||||
import { searchFolders } from '../../manage-dashboards/state/actions';
|
import { searchFolders } from '../../manage-dashboards/state/actions';
|
||||||
|
|
||||||
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
||||||
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
||||||
import { mockApi, mockFeatureDiscoveryApi, setupMswServer } from './mockApi';
|
import { mockApi, mockFeatureDiscoveryApi, setupMswServer } from './mockApi';
|
||||||
import { disableRBAC, mockDataSource } from './mocks';
|
import { grantUserPermissions, mockDataSource } from './mocks';
|
||||||
import {
|
import {
|
||||||
defaultAlertmanagerChoiceResponse,
|
defaultAlertmanagerChoiceResponse,
|
||||||
emptyExternalAlertmanagersResponse,
|
emptyExternalAlertmanagersResponse,
|
||||||
@@ -82,10 +83,19 @@ describe('RuleEditor cloud', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
contextSrv.isEditor = true;
|
contextSrv.isEditor = true;
|
||||||
contextSrv.hasEditPermissionInFolders = true;
|
contextSrv.hasEditPermissionInFolders = true;
|
||||||
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingRuleRead,
|
||||||
|
AccessControlAction.AlertingRuleUpdate,
|
||||||
|
AccessControlAction.AlertingRuleDelete,
|
||||||
|
AccessControlAction.AlertingRuleCreate,
|
||||||
|
AccessControlAction.DataSourcesRead,
|
||||||
|
AccessControlAction.DataSourcesWrite,
|
||||||
|
AccessControlAction.DataSourcesCreate,
|
||||||
|
AccessControlAction.AlertingRuleExternalRead,
|
||||||
|
AccessControlAction.AlertingRuleExternalWrite,
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
disableRBAC();
|
|
||||||
|
|
||||||
it('can create a new cloud alert', async () => {
|
it('can create a new cloud alert', async () => {
|
||||||
mocks.api.setRulerRuleGroup.mockResolvedValue();
|
mocks.api.setRulerRuleGroup.mockResolvedValue();
|
||||||
mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]);
|
mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import RuleEditor from './RuleEditor';
|
|||||||
import { discoverFeatures } from './api/buildInfo';
|
import { discoverFeatures } from './api/buildInfo';
|
||||||
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
||||||
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
||||||
import { disableRBAC, mockDataSource, MockDataSourceSrv, mockFolder } from './mocks';
|
import { grantUserPermissions, mockDataSource, MockDataSourceSrv, mockFolder } from './mocks';
|
||||||
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
|
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
|
||||||
import * as config from './utils/config';
|
import * as config from './utils/config';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
|
||||||
@@ -79,9 +79,21 @@ describe('RuleEditor grafana managed rules', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
contextSrv.isEditor = true;
|
contextSrv.isEditor = true;
|
||||||
contextSrv.hasEditPermissionInFolders = true;
|
contextSrv.hasEditPermissionInFolders = true;
|
||||||
});
|
|
||||||
|
|
||||||
disableRBAC();
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingRuleRead,
|
||||||
|
AccessControlAction.AlertingRuleUpdate,
|
||||||
|
AccessControlAction.AlertingRuleDelete,
|
||||||
|
AccessControlAction.AlertingRuleCreate,
|
||||||
|
AccessControlAction.DataSourcesRead,
|
||||||
|
AccessControlAction.DataSourcesWrite,
|
||||||
|
AccessControlAction.DataSourcesCreate,
|
||||||
|
AccessControlAction.FoldersWrite,
|
||||||
|
AccessControlAction.FoldersRead,
|
||||||
|
AccessControlAction.AlertingRuleExternalRead,
|
||||||
|
AccessControlAction.AlertingRuleExternalWrite,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('can edit grafana managed rule', async () => {
|
it('can edit grafana managed rule', async () => {
|
||||||
const uid = 'FOOBAR123';
|
const uid = 'FOOBAR123';
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { byRole } from 'testing-library-selector';
|
|||||||
import { setDataSourceSrv } from '@grafana/runtime';
|
import { setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
import { DashboardSearchHit } from 'app/features/search/types';
|
import { DashboardSearchHit } from 'app/features/search/types';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
import { GrafanaAlertStateDecision, PromApplication } from 'app/types/unified-alerting-dto';
|
import { GrafanaAlertStateDecision, PromApplication } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { searchFolders } from '../../../../app/features/manage-dashboards/state/actions';
|
import { searchFolders } from '../../../../app/features/manage-dashboards/state/actions';
|
||||||
@@ -15,7 +16,7 @@ import { searchFolders } from '../../../../app/features/manage-dashboards/state/
|
|||||||
import { discoverFeatures } from './api/buildInfo';
|
import { discoverFeatures } from './api/buildInfo';
|
||||||
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
||||||
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
||||||
import { disableRBAC, mockDataSource, MockDataSourceSrv } from './mocks';
|
import { grantUserPermissions, mockDataSource, MockDataSourceSrv } from './mocks';
|
||||||
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
|
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
|
||||||
import * as config from './utils/config';
|
import * as config from './utils/config';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
|
||||||
@@ -66,10 +67,21 @@ describe('RuleEditor grafana managed rules', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
contextSrv.isEditor = true;
|
contextSrv.isEditor = true;
|
||||||
contextSrv.hasEditPermissionInFolders = true;
|
contextSrv.hasEditPermissionInFolders = true;
|
||||||
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingRuleRead,
|
||||||
|
AccessControlAction.AlertingRuleUpdate,
|
||||||
|
AccessControlAction.AlertingRuleDelete,
|
||||||
|
AccessControlAction.AlertingRuleCreate,
|
||||||
|
AccessControlAction.DataSourcesRead,
|
||||||
|
AccessControlAction.DataSourcesWrite,
|
||||||
|
AccessControlAction.DataSourcesCreate,
|
||||||
|
AccessControlAction.FoldersWrite,
|
||||||
|
AccessControlAction.FoldersRead,
|
||||||
|
AccessControlAction.AlertingRuleExternalRead,
|
||||||
|
AccessControlAction.AlertingRuleExternalWrite,
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
disableRBAC();
|
|
||||||
|
|
||||||
it('can create new grafana managed alert', async () => {
|
it('can create new grafana managed alert', async () => {
|
||||||
const dataSources = {
|
const dataSources = {
|
||||||
default: mockDataSource(
|
default: mockDataSource(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { byRole, byText } from 'testing-library-selector';
|
|||||||
|
|
||||||
import { setDataSourceSrv } from '@grafana/runtime';
|
import { setDataSourceSrv } from '@grafana/runtime';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
import { PromApplication } from 'app/types/unified-alerting-dto';
|
import { PromApplication } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { searchFolders } from '../../manage-dashboards/state/actions';
|
import { searchFolders } from '../../manage-dashboards/state/actions';
|
||||||
@@ -14,7 +15,7 @@ import { searchFolders } from '../../manage-dashboards/state/actions';
|
|||||||
import { discoverFeatures } from './api/buildInfo';
|
import { discoverFeatures } from './api/buildInfo';
|
||||||
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
import { fetchRulerRules, fetchRulerRulesGroup, fetchRulerRulesNamespace, setRulerRuleGroup } from './api/ruler';
|
||||||
import { RecordingRuleEditorProps } from './components/rule-editor/RecordingRuleEditor';
|
import { RecordingRuleEditorProps } from './components/rule-editor/RecordingRuleEditor';
|
||||||
import { disableRBAC, mockDataSource, MockDataSourceSrv } from './mocks';
|
import { grantUserPermissions, mockDataSource, MockDataSourceSrv } from './mocks';
|
||||||
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
|
import { fetchRulerRulesIfNotFetchedYet } from './state/actions';
|
||||||
import * as config from './utils/config';
|
import * as config from './utils/config';
|
||||||
|
|
||||||
@@ -96,9 +97,21 @@ describe('RuleEditor recording rules', () => {
|
|||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
contextSrv.isEditor = true;
|
contextSrv.isEditor = true;
|
||||||
contextSrv.hasEditPermissionInFolders = true;
|
contextSrv.hasEditPermissionInFolders = true;
|
||||||
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingRuleRead,
|
||||||
|
AccessControlAction.AlertingRuleUpdate,
|
||||||
|
AccessControlAction.AlertingRuleDelete,
|
||||||
|
AccessControlAction.AlertingRuleCreate,
|
||||||
|
AccessControlAction.DataSourcesRead,
|
||||||
|
AccessControlAction.DataSourcesWrite,
|
||||||
|
AccessControlAction.DataSourcesCreate,
|
||||||
|
AccessControlAction.FoldersWrite,
|
||||||
|
AccessControlAction.FoldersRead,
|
||||||
|
AccessControlAction.AlertingRuleExternalRead,
|
||||||
|
AccessControlAction.AlertingRuleExternalWrite,
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
disableRBAC();
|
|
||||||
it('can create a new cloud recording rule', async () => {
|
it('can create a new cloud recording rule', async () => {
|
||||||
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
||||||
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));
|
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { discoverFeatures } from './api/buildInfo';
|
|||||||
import { fetchRules } from './api/prometheus';
|
import { fetchRules } from './api/prometheus';
|
||||||
import { deleteNamespace, deleteRulerRulesGroup, fetchRulerRules, setRulerRuleGroup } from './api/ruler';
|
import { deleteNamespace, deleteRulerRulesGroup, fetchRulerRules, setRulerRuleGroup } from './api/ruler';
|
||||||
import {
|
import {
|
||||||
enableRBAC,
|
|
||||||
grantUserPermissions,
|
grantUserPermissions,
|
||||||
mockDataSource,
|
mockDataSource,
|
||||||
MockDataSourceSrv,
|
MockDataSourceSrv,
|
||||||
@@ -686,8 +685,6 @@ describe('RuleList', () => {
|
|||||||
describe('RBAC Enabled', () => {
|
describe('RBAC Enabled', () => {
|
||||||
describe('Export button', () => {
|
describe('Export button', () => {
|
||||||
it('Export button should be visible when the user has alert provisioning read permissions', async () => {
|
it('Export button should be visible when the user has alert provisioning read permissions', async () => {
|
||||||
enableRBAC();
|
|
||||||
|
|
||||||
grantUserPermissions([AccessControlAction.AlertingProvisioningRead]);
|
grantUserPermissions([AccessControlAction.AlertingProvisioningRead]);
|
||||||
|
|
||||||
mocks.getAllDataSourcesMock.mockReturnValue([]);
|
mocks.getAllDataSourcesMock.mockReturnValue([]);
|
||||||
@@ -701,8 +698,6 @@ describe('RuleList', () => {
|
|||||||
expect(ui.exportButton.get()).toBeInTheDocument();
|
expect(ui.exportButton.get()).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
it('Export button should be visible when the user has alert provisioning read secrets permissions', async () => {
|
it('Export button should be visible when the user has alert provisioning read secrets permissions', async () => {
|
||||||
enableRBAC();
|
|
||||||
|
|
||||||
grantUserPermissions([AccessControlAction.AlertingProvisioningReadSecrets]);
|
grantUserPermissions([AccessControlAction.AlertingProvisioningReadSecrets]);
|
||||||
|
|
||||||
mocks.getAllDataSourcesMock.mockReturnValue([]);
|
mocks.getAllDataSourcesMock.mockReturnValue([]);
|
||||||
@@ -716,8 +711,6 @@ describe('RuleList', () => {
|
|||||||
expect(ui.exportButton.get()).toBeInTheDocument();
|
expect(ui.exportButton.get()).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
it('Export button should not be visible when the user has no alert provisioning read permissions', async () => {
|
it('Export button should not be visible when the user has no alert provisioning read permissions', async () => {
|
||||||
enableRBAC();
|
|
||||||
|
|
||||||
grantUserPermissions([AccessControlAction.AlertingRuleCreate, AccessControlAction.FoldersRead]);
|
grantUserPermissions([AccessControlAction.AlertingRuleCreate, AccessControlAction.FoldersRead]);
|
||||||
|
|
||||||
mocks.getAllDataSourcesMock.mockReturnValue([]);
|
mocks.getAllDataSourcesMock.mockReturnValue([]);
|
||||||
@@ -733,8 +726,6 @@ describe('RuleList', () => {
|
|||||||
});
|
});
|
||||||
describe('Grafana Managed Alerts', () => {
|
describe('Grafana Managed Alerts', () => {
|
||||||
it('New alert button should be visible when the user has alert rule create and folder read permissions and no rules exists', async () => {
|
it('New alert button should be visible when the user has alert rule create and folder read permissions and no rules exists', async () => {
|
||||||
enableRBAC();
|
|
||||||
|
|
||||||
grantUserPermissions([
|
grantUserPermissions([
|
||||||
AccessControlAction.FoldersRead,
|
AccessControlAction.FoldersRead,
|
||||||
AccessControlAction.AlertingRuleCreate,
|
AccessControlAction.AlertingRuleCreate,
|
||||||
@@ -753,8 +744,6 @@ describe('RuleList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('New alert button should be visible when the user has alert rule create and folder read permissions and rules already exists', async () => {
|
it('New alert button should be visible when the user has alert rule create and folder read permissions and rules already exists', async () => {
|
||||||
enableRBAC();
|
|
||||||
|
|
||||||
grantUserPermissions([
|
grantUserPermissions([
|
||||||
AccessControlAction.FoldersRead,
|
AccessControlAction.FoldersRead,
|
||||||
AccessControlAction.AlertingRuleCreate,
|
AccessControlAction.AlertingRuleCreate,
|
||||||
@@ -775,8 +764,6 @@ describe('RuleList', () => {
|
|||||||
|
|
||||||
describe('Cloud Alerts', () => {
|
describe('Cloud Alerts', () => {
|
||||||
it('New alert button should be visible when the user has the alert rule external write and datasource read permissions and no rules exists', async () => {
|
it('New alert button should be visible when the user has the alert rule external write and datasource read permissions and no rules exists', async () => {
|
||||||
enableRBAC();
|
|
||||||
|
|
||||||
grantUserPermissions([
|
grantUserPermissions([
|
||||||
// AccessControlAction.AlertingRuleRead,
|
// AccessControlAction.AlertingRuleRead,
|
||||||
AccessControlAction.DataSourcesRead,
|
AccessControlAction.DataSourcesRead,
|
||||||
@@ -803,8 +790,6 @@ describe('RuleList', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('New alert button should be visible when the user has the alert rule external write and data source read permissions and rules already exists', async () => {
|
it('New alert button should be visible when the user has the alert rule external write and data source read permissions and rules already exists', async () => {
|
||||||
enableRBAC();
|
|
||||||
|
|
||||||
grantUserPermissions([
|
grantUserPermissions([
|
||||||
AccessControlAction.DataSourcesRead,
|
AccessControlAction.DataSourcesRead,
|
||||||
AccessControlAction.AlertingRuleExternalRead,
|
AccessControlAction.AlertingRuleExternalRead,
|
||||||
@@ -833,8 +818,6 @@ describe('RuleList', () => {
|
|||||||
|
|
||||||
describe('Analytics', () => {
|
describe('Analytics', () => {
|
||||||
it('Sends log info when creating an alert rule from a scratch', async () => {
|
it('Sends log info when creating an alert rule from a scratch', async () => {
|
||||||
enableRBAC();
|
|
||||||
|
|
||||||
grantUserPermissions([
|
grantUserPermissions([
|
||||||
AccessControlAction.FoldersRead,
|
AccessControlAction.FoldersRead,
|
||||||
AccessControlAction.AlertingRuleCreate,
|
AccessControlAction.AlertingRuleCreate,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
AlertManagerDataSourceJsonData,
|
AlertManagerDataSourceJsonData,
|
||||||
AlertManagerImplementation,
|
AlertManagerImplementation,
|
||||||
} from 'app/plugins/datasource/alertmanager/types';
|
} from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fetchAlertManagerConfig,
|
fetchAlertManagerConfig,
|
||||||
@@ -20,7 +21,7 @@ import {
|
|||||||
fetchStatus,
|
fetchStatus,
|
||||||
} from '../../api/alertmanager';
|
} from '../../api/alertmanager';
|
||||||
import {
|
import {
|
||||||
disableRBAC,
|
grantUserPermissions,
|
||||||
mockDataSource,
|
mockDataSource,
|
||||||
MockDataSourceSrv,
|
MockDataSourceSrv,
|
||||||
someCloudAlertManagerConfig,
|
someCloudAlertManagerConfig,
|
||||||
@@ -88,11 +89,12 @@ const ui = {
|
|||||||
describe('Admin config', () => {
|
describe('Admin config', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
|
// FIXME: scope down
|
||||||
|
grantUserPermissions(Object.values(AccessControlAction));
|
||||||
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));
|
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));
|
||||||
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
||||||
contextSrv.isGrafanaAdmin = true;
|
contextSrv.isGrafanaAdmin = true;
|
||||||
store.delete(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY);
|
store.delete(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY);
|
||||||
disableRBAC();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Reset alertmanager config', async () => {
|
it('Reset alertmanager config', async () => {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { AccessControlAction, ContactPointsState, NotifierDTO, NotifierType } fr
|
|||||||
import { backendSrv } from '../../../../../core/services/backend_srv';
|
import { backendSrv } from '../../../../../core/services/backend_srv';
|
||||||
import * as receiversApi from '../../api/receiversApi';
|
import * as receiversApi from '../../api/receiversApi';
|
||||||
import { mockProvisioningApi, setupMswServer } from '../../mockApi';
|
import { mockProvisioningApi, setupMswServer } from '../../mockApi';
|
||||||
import { enableRBAC, grantUserPermissions } from '../../mocks';
|
import { grantUserPermissions } from '../../mocks';
|
||||||
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
|
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
|
||||||
import { fetchGrafanaNotifiersAction } from '../../state/actions';
|
import { fetchGrafanaNotifiersAction } from '../../state/actions';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
|
||||||
@@ -184,7 +184,6 @@ describe('ReceiversTable', () => {
|
|||||||
const notifiers: NotifierDTO[] = [mockNotifier('googlechat', 'Google Chat'), mockNotifier('sensugo', 'Sensu Go')];
|
const notifiers: NotifierDTO[] = [mockNotifier('googlechat', 'Google Chat'), mockNotifier('sensugo', 'Sensu Go')];
|
||||||
|
|
||||||
it('should be visible when user has permissions to read provisioning', async () => {
|
it('should be visible when user has permissions to read provisioning', async () => {
|
||||||
enableRBAC();
|
|
||||||
grantUserPermissions([AccessControlAction.AlertingProvisioningRead]);
|
grantUserPermissions([AccessControlAction.AlertingProvisioningRead]);
|
||||||
|
|
||||||
await renderReceieversTable(receivers, notifiers, GRAFANA_RULES_SOURCE_NAME);
|
await renderReceieversTable(receivers, notifiers, GRAFANA_RULES_SOURCE_NAME);
|
||||||
@@ -193,7 +192,6 @@ describe('ReceiversTable', () => {
|
|||||||
expect(buttons).toHaveLength(2);
|
expect(buttons).toHaveLength(2);
|
||||||
});
|
});
|
||||||
it('should be visible when user has permissions to read provisioning with secrets', async () => {
|
it('should be visible when user has permissions to read provisioning with secrets', async () => {
|
||||||
enableRBAC();
|
|
||||||
grantUserPermissions([AccessControlAction.AlertingProvisioningReadSecrets]);
|
grantUserPermissions([AccessControlAction.AlertingProvisioningReadSecrets]);
|
||||||
|
|
||||||
await renderReceieversTable(receivers, notifiers, GRAFANA_RULES_SOURCE_NAME);
|
await renderReceieversTable(receivers, notifiers, GRAFANA_RULES_SOURCE_NAME);
|
||||||
@@ -202,7 +200,6 @@ describe('ReceiversTable', () => {
|
|||||||
expect(buttons).toHaveLength(2);
|
expect(buttons).toHaveLength(2);
|
||||||
});
|
});
|
||||||
it('should not be visible when user has no provisioning permissions', async () => {
|
it('should not be visible when user has no provisioning permissions', async () => {
|
||||||
enableRBAC();
|
|
||||||
grantUserPermissions([AccessControlAction.AlertingNotificationsRead]);
|
grantUserPermissions([AccessControlAction.AlertingNotificationsRead]);
|
||||||
|
|
||||||
await renderReceieversTable(receivers, [], GRAFANA_RULES_SOURCE_NAME);
|
await renderReceieversTable(receivers, [], GRAFANA_RULES_SOURCE_NAME);
|
||||||
@@ -235,7 +232,6 @@ describe('ReceiversTable', () => {
|
|||||||
|
|
||||||
const notifiers: NotifierDTO[] = [mockNotifier('googlechat', 'Google Chat'), mockNotifier('sensugo', 'Sensu Go')];
|
const notifiers: NotifierDTO[] = [mockNotifier('googlechat', 'Google Chat'), mockNotifier('sensugo', 'Sensu Go')];
|
||||||
|
|
||||||
enableRBAC();
|
|
||||||
grantUserPermissions([AccessControlAction.AlertingProvisioningRead]);
|
grantUserPermissions([AccessControlAction.AlertingProvisioningRead]);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ const ui = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(true);
|
|
||||||
|
|
||||||
const server = setupServer();
|
const server = setupServer();
|
||||||
|
|
||||||
const alertmanagerChoiceMockedResponse: AlertmanagersChoiceResponse = {
|
const alertmanagerChoiceMockedResponse: AlertmanagersChoiceResponse = {
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ const ui = {
|
|||||||
|
|
||||||
describe('RuleListGroupView', () => {
|
describe('RuleListGroupView', () => {
|
||||||
describe('RBAC', () => {
|
describe('RBAC', () => {
|
||||||
jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(true);
|
|
||||||
|
|
||||||
it('Should display Grafana rules when the user has the alert rule read permission', async () => {
|
it('Should display Grafana rules when the user has the alert rule read permission', async () => {
|
||||||
const grafanaNamespace = getGrafanaNamespace();
|
const grafanaNamespace = getGrafanaNamespace();
|
||||||
const namespaces: CombinedRuleNamespace[] = [grafanaNamespace];
|
const namespaces: CombinedRuleNamespace[] = [grafanaNamespace];
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ import { byRole, byTestId, byText } from 'testing-library-selector';
|
|||||||
import { logInfo } from '@grafana/runtime';
|
import { logInfo } from '@grafana/runtime';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
import { configureStore } from 'app/store/configureStore';
|
import { configureStore } from 'app/store/configureStore';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
import { CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting';
|
import { CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting';
|
||||||
|
|
||||||
import { LogMessages } from '../../Analytics';
|
import { LogMessages } from '../../Analytics';
|
||||||
import { useHasRuler } from '../../hooks/useHasRuler';
|
import { useHasRuler } from '../../hooks/useHasRuler';
|
||||||
import { mockFolderApi, mockProvisioningApi, setupMswServer } from '../../mockApi';
|
import { mockFolderApi, mockProvisioningApi, setupMswServer } from '../../mockApi';
|
||||||
import { disableRBAC, mockCombinedRule, mockDataSource, mockFolder, mockGrafanaRulerRule } from '../../mocks';
|
import { grantUserPermissions, mockCombinedRule, mockDataSource, mockFolder, mockGrafanaRulerRule } from '../../mocks';
|
||||||
|
|
||||||
import { RulesGroup } from './RulesGroup';
|
import { RulesGroup } from './RulesGroup';
|
||||||
|
|
||||||
@@ -46,6 +47,8 @@ function mockUseHasRuler(hasRuler: boolean, rulerRulesLoaded: boolean) {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocks.useHasRuler.mockReset();
|
mocks.useHasRuler.mockReset();
|
||||||
|
// FIXME: scope down
|
||||||
|
grantUserPermissions(Object.values(AccessControlAction));
|
||||||
});
|
});
|
||||||
|
|
||||||
const ui = {
|
const ui = {
|
||||||
@@ -164,8 +167,6 @@ describe('Rules group tests', () => {
|
|||||||
groups: [group],
|
groups: [group],
|
||||||
};
|
};
|
||||||
|
|
||||||
disableRBAC();
|
|
||||||
|
|
||||||
it('When ruler enabled should display delete and edit group buttons', () => {
|
it('When ruler enabled should display delete and edit group buttons', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
mockUseHasRuler(true, true);
|
mockUseHasRuler(true, true);
|
||||||
@@ -223,8 +224,6 @@ describe('Rules group tests', () => {
|
|||||||
groups: [group],
|
groups: [group],
|
||||||
};
|
};
|
||||||
|
|
||||||
disableRBAC();
|
|
||||||
|
|
||||||
it('Should log info when closing the edit group rule modal without saving', async () => {
|
it('Should log info when closing the edit group rule modal without saving', async () => {
|
||||||
mockUseHasRuler(true, true);
|
mockUseHasRuler(true, true);
|
||||||
renderRulesGroup(namespace, group);
|
renderRulesGroup(namespace, group);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Provider } from 'react-redux';
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { byRole } from 'testing-library-selector';
|
import { byRole } from 'testing-library-selector';
|
||||||
|
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
|
||||||
import { configureStore } from 'app/store/configureStore';
|
import { configureStore } from 'app/store/configureStore';
|
||||||
import { CombinedRule } from 'app/types/unified-alerting';
|
import { CombinedRule } from 'app/types/unified-alerting';
|
||||||
|
|
||||||
@@ -31,8 +30,6 @@ const ui = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(true);
|
|
||||||
|
|
||||||
function renderRulesTable(rule: CombinedRule) {
|
function renderRulesTable(rule: CombinedRule) {
|
||||||
const store = configureStore();
|
const store = configureStore();
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,7 @@ import { Provider } from 'react-redux';
|
|||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
import { AccessControlAction, FolderDTO, StoreState } from 'app/types';
|
import { AccessControlAction, FolderDTO, StoreState } from 'app/types';
|
||||||
|
|
||||||
import {
|
import { mockFolder, mockRulerAlertingRule, mockRulerGrafanaRule, mockUnifiedAlertingStore } from '../mocks';
|
||||||
disableRBAC,
|
|
||||||
enableRBAC,
|
|
||||||
mockFolder,
|
|
||||||
mockRulerAlertingRule,
|
|
||||||
mockRulerGrafanaRule,
|
|
||||||
mockUnifiedAlertingStore,
|
|
||||||
} from '../mocks';
|
|
||||||
|
|
||||||
import { useFolder } from './useFolder';
|
import { useFolder } from './useFolder';
|
||||||
import { useIsRuleEditable } from './useIsRuleEditable';
|
import { useIsRuleEditable } from './useIsRuleEditable';
|
||||||
@@ -27,7 +20,6 @@ const mocks = {
|
|||||||
|
|
||||||
describe('useIsRuleEditable', () => {
|
describe('useIsRuleEditable', () => {
|
||||||
describe('RBAC enabled', () => {
|
describe('RBAC enabled', () => {
|
||||||
beforeEach(enableRBAC);
|
|
||||||
describe('Grafana rules', () => {
|
describe('Grafana rules', () => {
|
||||||
// When RBAC is enabled we require appropriate alerting permissions in the folder scope
|
// When RBAC is enabled we require appropriate alerting permissions in the folder scope
|
||||||
it('Should allow editing when the user has the alert rule update permission in the folder', async () => {
|
it('Should allow editing when the user has the alert rule update permission in the folder', async () => {
|
||||||
@@ -130,35 +122,6 @@ describe('useIsRuleEditable', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('RBAC disabled', () => {
|
|
||||||
beforeEach(disableRBAC);
|
|
||||||
describe('Grafana rules', () => {
|
|
||||||
it('Should allow editing and deleting when the user has folder canSave permission', async () => {
|
|
||||||
mockUseFolder({ canSave: true });
|
|
||||||
|
|
||||||
const wrapper = getProviderWrapper();
|
|
||||||
|
|
||||||
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
|
|
||||||
|
|
||||||
await waitFor(() => expect(result.current.loading).toBe(false));
|
|
||||||
expect(result.current.isEditable).toBe(true);
|
|
||||||
expect(result.current.isRemovable).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should forbid editing and deleting when the user has no folder canSave permission', async () => {
|
|
||||||
mockUseFolder({ canSave: false });
|
|
||||||
|
|
||||||
const wrapper = getProviderWrapper();
|
|
||||||
|
|
||||||
const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
|
|
||||||
|
|
||||||
await waitFor(() => expect(result.current.loading).toBe(false));
|
|
||||||
expect(result.current.isEditable).toBe(false);
|
|
||||||
expect(result.current.isRemovable).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function mockUseFolder(partial?: Partial<FolderDTO>) {
|
function mockUseFolder(partial?: Partial<FolderDTO>) {
|
||||||
|
|||||||
@@ -595,14 +595,6 @@ export const mockFolder = (partial?: Partial<FolderDTO>): FolderDTO => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const enableRBAC = () => {
|
|
||||||
jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const disableRBAC = () => {
|
|
||||||
jest.spyOn(contextSrv, 'accessControlEnabled').mockReturnValue(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const grantUserPermissions = (permissions: AccessControlAction[]) => {
|
export const grantUserPermissions = (permissions: AccessControlAction[]) => {
|
||||||
jest
|
jest
|
||||||
.spyOn(contextSrv, 'hasPermission')
|
.spyOn(contextSrv, 'hasPermission')
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import { DashboardMetaChangedEvent } from 'app/types/events';
|
|||||||
import { VariableEditorContainer } from '../../../variables/editor/VariableEditorContainer';
|
import { VariableEditorContainer } from '../../../variables/editor/VariableEditorContainer';
|
||||||
import { DashboardModel } from '../../state/DashboardModel';
|
import { DashboardModel } from '../../state/DashboardModel';
|
||||||
import { AccessControlDashboardPermissions } from '../DashboardPermissions/AccessControlDashboardPermissions';
|
import { AccessControlDashboardPermissions } from '../DashboardPermissions/AccessControlDashboardPermissions';
|
||||||
import { DashboardPermissions } from '../DashboardPermissions/DashboardPermissions';
|
|
||||||
import { SaveDashboardAsButton, SaveDashboardButton } from '../SaveDashboard/SaveDashboardButton';
|
import { SaveDashboardAsButton, SaveDashboardButton } from '../SaveDashboard/SaveDashboardButton';
|
||||||
|
|
||||||
import { AnnotationsSettings } from './AnnotationsSettings';
|
import { AnnotationsSettings } from './AnnotationsSettings';
|
||||||
@@ -148,14 +147,7 @@ function getSettingsPages(dashboard: DashboardModel) {
|
|||||||
const permissionsTitle = t('dashboard-settings.permissions.title', 'Permissions');
|
const permissionsTitle = t('dashboard-settings.permissions.title', 'Permissions');
|
||||||
|
|
||||||
if (dashboard.id && dashboard.meta.canAdmin) {
|
if (dashboard.id && dashboard.meta.canAdmin) {
|
||||||
if (!config.rbacEnabled) {
|
if (contextSrv.hasPermission(AccessControlAction.DashboardsPermissionsRead)) {
|
||||||
pages.push({
|
|
||||||
title: permissionsTitle,
|
|
||||||
id: 'permissions',
|
|
||||||
icon: 'lock',
|
|
||||||
component: DashboardPermissions,
|
|
||||||
});
|
|
||||||
} else if (contextSrv.hasPermission(AccessControlAction.DashboardsPermissionsRead)) {
|
|
||||||
pages.push({
|
pages.push({
|
||||||
title: permissionsTitle,
|
title: permissionsTitle,
|
||||||
id: 'permissions',
|
id: 'permissions',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { keys as _keys } from 'lodash';
|
|||||||
|
|
||||||
import { dateTime, TimeRange, VariableHide } from '@grafana/data';
|
import { dateTime, TimeRange, VariableHide } from '@grafana/data';
|
||||||
import { Dashboard, defaultVariableModel } from '@grafana/schema';
|
import { Dashboard, defaultVariableModel } from '@grafana/schema';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
|
||||||
|
|
||||||
import { getDashboardModel } from '../../../../test/helpers/getDashboardModel';
|
import { getDashboardModel } from '../../../../test/helpers/getDashboardModel';
|
||||||
import { variableAdapters } from '../../variables/adapters';
|
import { variableAdapters } from '../../variables/adapters';
|
||||||
@@ -22,8 +21,6 @@ import {
|
|||||||
|
|
||||||
jest.mock('app/core/services/context_srv');
|
jest.mock('app/core/services/context_srv');
|
||||||
|
|
||||||
const mockContextSrv = jest.mocked(contextSrv);
|
|
||||||
|
|
||||||
variableAdapters.setInit(() => [
|
variableAdapters.setInit(() => [
|
||||||
createQueryVariableAdapter(),
|
createQueryVariableAdapter(),
|
||||||
createAdHocVariableAdapter(),
|
createAdHocVariableAdapter(),
|
||||||
@@ -950,7 +947,6 @@ describe('DashboardModel', () => {
|
|||||||
|
|
||||||
dashboard.meta.canEdit = canEdit;
|
dashboard.meta.canEdit = canEdit;
|
||||||
dashboard.meta.canMakeEditable = canMakeEditable;
|
dashboard.meta.canMakeEditable = canMakeEditable;
|
||||||
mockContextSrv.accessControlEnabled.mockReturnValue(true);
|
|
||||||
const result = dashboard.canAddAnnotations();
|
const result = dashboard.canAddAnnotations();
|
||||||
expect(result).toBe(expected);
|
expect(result).toBe(expected);
|
||||||
}
|
}
|
||||||
@@ -983,7 +979,6 @@ describe('DashboardModel', () => {
|
|||||||
|
|
||||||
dashboard.meta.canEdit = canEdit;
|
dashboard.meta.canEdit = canEdit;
|
||||||
dashboard.meta.canMakeEditable = canMakeEditable;
|
dashboard.meta.canMakeEditable = canMakeEditable;
|
||||||
mockContextSrv.accessControlEnabled.mockReturnValue(true);
|
|
||||||
const result = dashboard.canEditAnnotations();
|
const result = dashboard.canEditAnnotations();
|
||||||
expect(result).toBe(expected);
|
expect(result).toBe(expected);
|
||||||
}
|
}
|
||||||
@@ -1014,7 +1009,6 @@ describe('DashboardModel', () => {
|
|||||||
|
|
||||||
dashboard.meta.canEdit = canEdit;
|
dashboard.meta.canEdit = canEdit;
|
||||||
dashboard.meta.canMakeEditable = canMakeEditable;
|
dashboard.meta.canMakeEditable = canMakeEditable;
|
||||||
mockContextSrv.accessControlEnabled.mockReturnValue(true);
|
|
||||||
const result = dashboard.canEditAnnotations('testDashboardUID');
|
const result = dashboard.canEditAnnotations('testDashboardUID');
|
||||||
expect(result).toBe(expected);
|
expect(result).toBe(expected);
|
||||||
}
|
}
|
||||||
@@ -1047,7 +1041,6 @@ describe('DashboardModel', () => {
|
|||||||
|
|
||||||
dashboard.meta.canEdit = canEdit;
|
dashboard.meta.canEdit = canEdit;
|
||||||
dashboard.meta.canMakeEditable = canMakeEditable;
|
dashboard.meta.canMakeEditable = canMakeEditable;
|
||||||
mockContextSrv.accessControlEnabled.mockReturnValue(true);
|
|
||||||
const result = dashboard.canDeleteAnnotations();
|
const result = dashboard.canDeleteAnnotations();
|
||||||
expect(result).toBe(expected);
|
expect(result).toBe(expected);
|
||||||
}
|
}
|
||||||
@@ -1078,7 +1071,6 @@ describe('DashboardModel', () => {
|
|||||||
|
|
||||||
dashboard.meta.canEdit = canEdit;
|
dashboard.meta.canEdit = canEdit;
|
||||||
dashboard.meta.canMakeEditable = canMakeEditable;
|
dashboard.meta.canMakeEditable = canMakeEditable;
|
||||||
mockContextSrv.accessControlEnabled.mockReturnValue(true);
|
|
||||||
const result = dashboard.canDeleteAnnotations('testDashboardUID');
|
const result = dashboard.canDeleteAnnotations('testDashboardUID');
|
||||||
expect(result).toBe(expected);
|
expect(result).toBe(expected);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1186,14 +1186,11 @@ export class DashboardModel implements TimeModel {
|
|||||||
canEditAnnotations(dashboardUID?: string) {
|
canEditAnnotations(dashboardUID?: string) {
|
||||||
let canEdit = true;
|
let canEdit = true;
|
||||||
|
|
||||||
// if RBAC is enabled there are additional conditions to check
|
// dashboardUID is falsy when it is an organizational annotation
|
||||||
if (contextSrv.accessControlEnabled()) {
|
if (!dashboardUID) {
|
||||||
// dashboardUID is falsy when it is an organizational annotation
|
canEdit = !!this.meta.annotationsPermissions?.organization.canEdit;
|
||||||
if (!dashboardUID) {
|
} else {
|
||||||
canEdit = !!this.meta.annotationsPermissions?.organization.canEdit;
|
canEdit = !!this.meta.annotationsPermissions?.dashboard.canEdit;
|
||||||
} else {
|
|
||||||
canEdit = !!this.meta.annotationsPermissions?.dashboard.canEdit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return this.canEditDashboard() && canEdit;
|
return this.canEditDashboard() && canEdit;
|
||||||
}
|
}
|
||||||
@@ -1201,13 +1198,11 @@ export class DashboardModel implements TimeModel {
|
|||||||
canDeleteAnnotations(dashboardUID?: string) {
|
canDeleteAnnotations(dashboardUID?: string) {
|
||||||
let canDelete = true;
|
let canDelete = true;
|
||||||
|
|
||||||
if (contextSrv.accessControlEnabled()) {
|
// dashboardUID is falsy when it is an organizational annotation
|
||||||
// dashboardUID is falsy when it is an organizational annotation
|
if (!dashboardUID) {
|
||||||
if (!dashboardUID) {
|
canDelete = !!this.meta.annotationsPermissions?.organization.canDelete;
|
||||||
canDelete = !!this.meta.annotationsPermissions?.organization.canDelete;
|
} else {
|
||||||
} else {
|
canDelete = !!this.meta.annotationsPermissions?.dashboard.canDelete;
|
||||||
canDelete = !!this.meta.annotationsPermissions?.dashboard.canDelete;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return canDelete && this.canEditDashboard();
|
return canDelete && this.canEditDashboard();
|
||||||
}
|
}
|
||||||
@@ -1220,7 +1215,7 @@ export class DashboardModel implements TimeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If RBAC is enabled there are additional conditions to check.
|
// If RBAC is enabled there are additional conditions to check.
|
||||||
return !contextSrv.accessControlEnabled() || Boolean(this.meta.annotationsPermissions?.dashboard.canAdd);
|
return Boolean(this.meta.annotationsPermissions?.dashboard.canAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
canEditDashboard() {
|
canEditDashboard() {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { lastValueFrom } from 'rxjs';
|
|||||||
import { AlertState, getDefaultTimeRange, TimeRange } from '@grafana/data';
|
import { AlertState, getDefaultTimeRange, TimeRange } from '@grafana/data';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv';
|
import { backendSrv } from 'app/core/services/backend_srv';
|
||||||
import { disableRBAC, enableRBAC, grantUserPermissions } from 'app/features/alerting/unified/mocks';
|
import { grantUserPermissions } from 'app/features/alerting/unified/mocks';
|
||||||
import { Annotation } from 'app/features/alerting/unified/utils/constants';
|
import { Annotation } from 'app/features/alerting/unified/utils/constants';
|
||||||
import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures';
|
import { createDashboardModelFixture } from 'app/features/dashboard/state/__fixtures__/dashboardFixtures';
|
||||||
import { AccessControlAction } from 'app/types/accessControl';
|
import { AccessControlAction } from 'app/types/accessControl';
|
||||||
@@ -47,10 +47,7 @@ describe('UnifiedAlertStatesWorker', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
config.publicDashboardAccessToken = '';
|
config.publicDashboardAccessToken = '';
|
||||||
});
|
grantUserPermissions(Object.values(AccessControlAction));
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
disableRBAC();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when canWork is called with correct props', () => {
|
describe('when canWork is called with correct props', () => {
|
||||||
@@ -231,7 +228,6 @@ describe('UnifiedAlertStatesWorker', () => {
|
|||||||
|
|
||||||
describe('UnifiedAlertStateWorker with RBAC', () => {
|
describe('UnifiedAlertStateWorker with RBAC', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
enableRBAC();
|
|
||||||
grantUserPermissions([]);
|
grantUserPermissions([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -41,11 +41,9 @@ function mapStateToProps(state: StoreState, props: OwnProps) {
|
|||||||
const teamId = parseInt(props.match.params.id, 10);
|
const teamId = parseInt(props.match.params.id, 10);
|
||||||
const team = getTeam(state.team, teamId);
|
const team = getTeam(state.team, teamId);
|
||||||
let defaultPage = 'members';
|
let defaultPage = 'members';
|
||||||
if (contextSrv.accessControlEnabled()) {
|
// With RBAC the settings page will always be available
|
||||||
// With RBAC the settings page will always be available
|
if (!team || !contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsPermissionsRead, team)) {
|
||||||
if (!team || !contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsPermissionsRead, team)) {
|
defaultPage = 'settings';
|
||||||
defaultPage = 'settings';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const pageName = props.match.params.page ?? defaultPage;
|
const pageName = props.match.params.page ?? defaultPage;
|
||||||
const teamLoadingNav = getTeamLoadingNav(pageName);
|
const teamLoadingNav = getTeamLoadingNav(pageName);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { AnnotationEvent, dateTime } from '@grafana/data';
|
|||||||
import { coreModule } from 'app/angular/core_module';
|
import { coreModule } from 'app/angular/core_module';
|
||||||
import { MetricsPanelCtrl } from 'app/angular/panel/metrics_panel_ctrl';
|
import { MetricsPanelCtrl } from 'app/angular/panel/metrics_panel_ctrl';
|
||||||
|
|
||||||
import { contextSrv } from '../../../core/services/context_srv';
|
|
||||||
import { deleteAnnotation, saveAnnotation, updateAnnotation } from '../../../features/annotations/api';
|
import { deleteAnnotation, saveAnnotation, updateAnnotation } from '../../../features/annotations/api';
|
||||||
import { getDashboardQueryRunner } from '../../../features/query/state/DashboardQueryRunner/DashboardQueryRunner';
|
import { getDashboardQueryRunner } from '../../../features/query/state/DashboardQueryRunner/DashboardQueryRunner';
|
||||||
|
|
||||||
@@ -34,13 +33,10 @@ export class EventEditorCtrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canDelete(): boolean {
|
canDelete(): boolean {
|
||||||
if (contextSrv.accessControlEnabled()) {
|
if (this.event.source?.type === 'dashboard') {
|
||||||
if (this.event.source?.type === 'dashboard') {
|
return !!this.panelCtrl.dashboard.meta.annotationsPermissions?.dashboard.canDelete;
|
||||||
return !!this.panelCtrl.dashboard.meta.annotationsPermissions?.dashboard.canDelete;
|
|
||||||
}
|
|
||||||
return !!this.panelCtrl.dashboard.meta.annotationsPermissions?.organization.canDelete;
|
|
||||||
}
|
}
|
||||||
return true;
|
return !!this.panelCtrl.dashboard.meta.annotationsPermissions?.organization.canDelete;
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user