Skip to content

Commit

Permalink
Add RBAC checks to the env var editor
Browse files Browse the repository at this point in the history
  • Loading branch information
spadgett committed May 30, 2019
1 parent 6c9892f commit cc753cf
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 12 deletions.
42 changes: 35 additions & 7 deletions frontend/__tests__/components/environment.spec.tsx
Expand Up @@ -3,24 +3,49 @@ import { shallow } from 'enzyme';
import { FieldLevelHelp } from 'patternfly-react';

import { EnvironmentPage } from '../../public/components/environment';
import { DeploymentModel } from '../../public/models';
import * as k8s from '../../public/module/k8s';

describe(EnvironmentPage.name, () => {

const configMaps={}, secrets = {}, objects = {'metadata': {'namespace': 'test'}};
const configMaps={}, secrets = {}, obj = {'metadata': {'namespace': 'test'}};

let wrapper, wrapperRO;
let environmentPage, environmentPageRO;

describe('When readOnly attribute is "true"', () => {
beforeEach(() => {
environmentPageRO=<EnvironmentPage.WrappedComponent
obj={objects}
obj={obj}
model={DeploymentModel}
rawEnvData={[ { 'env': [ { 'name': 'test', 'value': ':0', 'ID': 0 } ] } ]}
envPath={[]}
readOnly={true}
/>;
wrapperRO = shallow(environmentPageRO);
wrapperRO.setState({allowed: true});
});

it('does not show field level help', () => {
expect(wrapperRO.find(FieldLevelHelp).exists()).toEqual(false);
});

it('does not render save and reload buttons', () => {
expect(wrapperRO.find('.environment-buttons button').exists()).toEqual(false);
});
});

describe('When user does not have permission', () => {
beforeEach(() => {
environmentPageRO=<EnvironmentPage.WrappedComponent
obj={obj}
model={DeploymentModel}
rawEnvData={[ { 'env': [ { 'name': 'test', 'value': ':0', 'ID': 0 } ] } ]}
envPath={[]}
readOnly={false}
/>;
wrapperRO = shallow(environmentPageRO);
wrapperRO.setState({allowed: false});
});

it('does not show field level help', () => {
Expand All @@ -36,13 +61,14 @@ describe(EnvironmentPage.name, () => {
beforeEach(() => {
spyOn(k8s, 'k8sGet').and.callFake(() => Promise.resolve());
environmentPage=<EnvironmentPage.WrappedComponent
obj={objects}
obj={obj}
model={DeploymentModel}
rawEnvData={[ { 'env': [ { 'name': 'test', 'value': ':0', 'ID': 0 } ] } ]}
envPath={[]}
readOnly={false}
/>;
wrapper = shallow(environmentPage);
wrapper.setState({secrets, configMaps});
wrapper.setState({secrets, configMaps, allowed: true});
});

it('shows field level help component', () => {
Expand All @@ -57,13 +83,14 @@ describe(EnvironmentPage.name, () => {
describe('When page has error messages or alerts', () => {
beforeEach(() => {
environmentPage=<EnvironmentPage.WrappedComponent
obj={objects}
obj={obj}
model={DeploymentModel}
rawEnvData={[ { 'env': [ { 'name': 'test', 'value': ':0', 'ID': 0 } ] } ]}
envPath={[]}
readOnly={true}
/>;
wrapper = shallow(environmentPage);
wrapper.setState({secrets, configMaps});
wrapper.setState({secrets, configMaps, allowed: true});
});

it('renders error message when error in state', () => {
Expand All @@ -85,7 +112,8 @@ describe(EnvironmentPage.name, () => {
describe('When page does not have error messages or alerts', () => {
beforeEach(() => {
environmentPage=<EnvironmentPage.WrappedComponent
obj={objects}
obj={obj}
model={DeploymentModel}
rawEnvData={[ { 'env': [ { 'name': 'test', 'value': ':0', 'ID': 0 } ] } ]}
envPath={[]}
readOnly={true}
Expand Down
52 changes: 47 additions & 5 deletions frontend/public/components/environment.jsx
Expand Up @@ -6,7 +6,18 @@ import { FieldLevelHelp, Alert } from 'patternfly-react';
import * as classNames from 'classnames';

import { k8sPatch, k8sGet, referenceFor, referenceForOwnerRef } from '../module/k8s';
import { PromiseComponent, NameValueEditorPair, EnvType, EnvFromPair, LoadingInline, LoadingBox, AsyncComponent, ContainerDropdown, ResourceLink } from './utils';
import {
AsyncComponent,
checkAccess,
ContainerDropdown,
EnvFromPair,
EnvType,
LoadingBox,
LoadingInline,
NameValueEditorPair,
PromiseComponent,
ResourceLink,
} from './utils';
import { ConfigMapModel, SecretModel } from '../models';

/**
Expand Down Expand Up @@ -84,13 +95,15 @@ const getContainersObjectForDropdown = (containerArray) => {
};

/** @type {(state: any, props: {obj?: object, rawEnvData?: any, readOnly: boolean, envPath: any, onChange?: (env: any) => void, addConfigMapSecret?: boolean, useLoadingInline?: boolean}) => {model: K8sKind}} */
const stateToProps = ({k8s}, {obj}) => ({
const stateToProps = ({k8s, UI}, {obj}) => ({
model: k8s.getIn(['RESOURCES', 'models', referenceFor(obj)]) || k8s.getIn(['RESOURCES', 'models', obj.kind]),
impersonate: UI.get('impersonate'),
});

class CurrentEnvVars {
constructor() {
this.currentEnvVars = {};
this.state = { allowed: true };
}

setRawData(rawEnvData) {
Expand Down Expand Up @@ -257,6 +270,7 @@ export const EnvironmentPage = connect(stateToProps)(
}

componentDidMount() {
this._checkEditAccess();
const {addConfigMapSecret, readOnly} = this.props;
if (!addConfigMapSecret || readOnly) {
const configMaps = {}, secrets = {};
Expand Down Expand Up @@ -285,7 +299,34 @@ export const EnvironmentPage = connect(stateToProps)(
};
}),
])
.then(_.spread((configMaps, secrets) => this.setState({configMaps, secrets})));
.then((([configMaps, secrets]) => this.setState({configMaps, secrets})));
}

componentDidUpdate(prevProps) {
const { obj, model, impersonate, readOnly } = this.props;
if (_.get(prevProps.obj, 'metadata.uid') !== _.get(obj, 'metadata.uid') ||
_.get(prevProps.model, 'apiGroup') !== _.get(model, 'apiGroup') ||
_.get(prevProps.model, 'kind') !== _.get(model, 'kind') ||
prevProps.impersonate !== impersonate ||
prevProps.readOnly !== readOnly) {
this._checkEditAccess();
}
}

_checkEditAccess() {
const { obj, model, impersonate, readOnly } = this.props;
if (_.isEmpty(obj) || !model || readOnly) {
return;
}
const { name, namespace } = obj.metadata;
const resourceAttributes = {
group: model.apiGroup,
resource: model.path,
verb: 'patch',
name,
namespace,
};
checkAccess(resourceAttributes, impersonate).then(resp => this.setState({ allowed: resp.status.allowed }));
}

/**
Expand Down Expand Up @@ -401,8 +442,9 @@ export const EnvironmentPage = connect(stateToProps)(
}

render() {
const {errorMessage, success, inProgress, currentEnvVars, stale, configMaps, secrets, containerIndex, containerType} = this.state;
const {rawEnvData, readOnly, obj, addConfigMapSecret, useLoadingInline} = this.props;
const {errorMessage, success, inProgress, currentEnvVars, stale, configMaps, secrets, containerIndex, containerType, allowed} = this.state;
const {rawEnvData, obj, addConfigMapSecret, useLoadingInline} = this.props;
const readOnly = this.props.readOnly || !allowed;

if (!configMaps || !currentEnvVars || !secrets) {
if (useLoadingInline) {
Expand Down

0 comments on commit cc753cf

Please sign in to comment.