mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AccessControl: add one-dimensional permissions to datasources (#38070)
* AccessControl: add one-dimensional permissions to datasources in the backend * AccessControl: add one-dimensional permissions to datasources in the frontend (#38080) Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
This commit is contained in:
@@ -2,6 +2,14 @@ import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import ButtonRow, { Props } from './ButtonRow';
|
||||
|
||||
jest.mock('app/core/core', () => {
|
||||
return {
|
||||
contextSrv: {
|
||||
hasPermission: () => true,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
isReadOnly: true,
|
||||
|
||||
@@ -4,6 +4,9 @@ import { selectors } from '@grafana/e2e-selectors';
|
||||
import config from 'app/core/config';
|
||||
import { Button, LinkButton } from '@grafana/ui';
|
||||
|
||||
import { AccessControlAction } from 'app/types/';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
|
||||
export interface Props {
|
||||
exploreUrl: string;
|
||||
isReadOnly: boolean;
|
||||
@@ -13,35 +16,39 @@ export interface Props {
|
||||
}
|
||||
|
||||
const ButtonRow: FC<Props> = ({ isReadOnly, onDelete, onSubmit, onTest, exploreUrl }) => {
|
||||
const canEditDataSources = !isReadOnly && contextSrv.hasPermission(AccessControlAction.DataSourcesWrite);
|
||||
const canDeleteDataSources = !isReadOnly && contextSrv.hasPermission(AccessControlAction.DataSourcesDelete);
|
||||
const canExploreDataSources = contextSrv.hasPermission(AccessControlAction.DataSourcesExplore);
|
||||
|
||||
return (
|
||||
<div className="gf-form-button-row">
|
||||
<LinkButton variant="secondary" fill="solid" href={`${config.appSubUrl}/datasources`}>
|
||||
Back
|
||||
</LinkButton>
|
||||
<LinkButton variant="secondary" fill="solid" href={exploreUrl}>
|
||||
<LinkButton variant="secondary" fill="solid" href={exploreUrl} disabled={!canExploreDataSources}>
|
||||
Explore
|
||||
</LinkButton>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
disabled={isReadOnly}
|
||||
disabled={!canDeleteDataSources}
|
||||
onClick={onDelete}
|
||||
aria-label={selectors.pages.DataSource.delete}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
{!isReadOnly && (
|
||||
{canEditDataSources && (
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={isReadOnly}
|
||||
disabled={!canEditDataSources}
|
||||
onClick={(event) => onSubmit(event)}
|
||||
aria-label={selectors.pages.DataSource.saveAndTest}
|
||||
>
|
||||
Save & test
|
||||
</Button>
|
||||
)}
|
||||
{isReadOnly && (
|
||||
{!canEditDataSources && (
|
||||
<Button type="submit" variant="primary" onClick={onTest}>
|
||||
Test
|
||||
</Button>
|
||||
|
||||
@@ -9,6 +9,14 @@ import { screen, render } from '@testing-library/react';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { PluginState } from '@grafana/data';
|
||||
|
||||
jest.mock('app/core/core', () => {
|
||||
return {
|
||||
contextSrv: {
|
||||
hasPermission: () => true,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const getMockNode = () => ({
|
||||
text: 'text',
|
||||
subTitle: 'subtitle',
|
||||
|
||||
@@ -7,6 +7,8 @@ import BasicSettings from './BasicSettings';
|
||||
import ButtonRow from './ButtonRow';
|
||||
// Services & Utils
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
|
||||
// Actions & selectors
|
||||
import { getDataSource, getDataSourceMeta } from '../state/selectors';
|
||||
import {
|
||||
@@ -19,7 +21,7 @@ import {
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
|
||||
// Types
|
||||
import { StoreState } from 'app/types/';
|
||||
import { StoreState, AccessControlAction } from 'app/types/';
|
||||
import { DataSourceSettings, urlUtil } from '@grafana/data';
|
||||
import { Alert, Button, LinkButton } from '@grafana/ui';
|
||||
import { getDataSourceLoadingNav, buildNavModel, getDataSourceNav } from '../state/navModel';
|
||||
@@ -140,6 +142,14 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
renderMissingEditRightsMessage() {
|
||||
return (
|
||||
<Alert severity="info" title="Missing rights">
|
||||
You are not allowed to modify this data source. Please contact your server admin to update this data source.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
testDataSource() {
|
||||
const { dataSource, testDataSource } = this.props;
|
||||
testDataSource(dataSource.name);
|
||||
@@ -228,9 +238,11 @@ export class DataSourceSettingsPage extends PureComponent<Props> {
|
||||
|
||||
renderSettings() {
|
||||
const { dataSourceMeta, setDataSourceName, setIsDefault, dataSource, plugin, testingStatus } = this.props;
|
||||
const canEditDataSources = contextSrv.hasPermission(AccessControlAction.DataSourcesWrite);
|
||||
|
||||
return (
|
||||
<form onSubmit={this.onSubmit}>
|
||||
{!canEditDataSources && this.renderMissingEditRightsMessage()}
|
||||
{this.isReadOnly() && this.renderIsReadOnlyMessage()}
|
||||
{dataSourceMeta.state && (
|
||||
<div className="gf-form">
|
||||
|
||||
@@ -12,6 +12,7 @@ exports[`Render should render component 1`] = `
|
||||
Back
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
disabled={false}
|
||||
fill="solid"
|
||||
href="/explore"
|
||||
variant="secondary"
|
||||
@@ -49,6 +50,7 @@ exports[`Render should render with buttons enabled 1`] = `
|
||||
Back
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
disabled={false}
|
||||
fill="solid"
|
||||
href="/explore"
|
||||
variant="secondary"
|
||||
|
||||
Reference in New Issue
Block a user