mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-03 01:18:43 +02:00
feature: Add support for permission system in unleash frontend
This commit is contained in:
parent
6de8c297fd
commit
1eb8fc0464
@ -22,6 +22,8 @@ import {
|
|||||||
} from 'react-mdl';
|
} from 'react-mdl';
|
||||||
import { IconLink, shorten, styles as commonStyles } from '../common';
|
import { IconLink, shorten, styles as commonStyles } from '../common';
|
||||||
import { formatFullDateTimeWithLocale } from '../common/util';
|
import { formatFullDateTimeWithLocale } from '../common/util';
|
||||||
|
import { CREATE_FEATURE, CREATE_STRATEGY, UPDATE_APPLICATION } from '../../permissions';
|
||||||
|
import PermissionComponent from '../common/permission-container';
|
||||||
|
|
||||||
class StatefulTextfield extends Component {
|
class StatefulTextfield extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -91,11 +93,26 @@ class ClientApplications extends PureComponent {
|
|||||||
{seenToggles.map(
|
{seenToggles.map(
|
||||||
({ name, description, enabled, notFound }, i) =>
|
({ name, description, enabled, notFound }, i) =>
|
||||||
notFound ? (
|
notFound ? (
|
||||||
|
<PermissionComponent
|
||||||
|
permission={CREATE_FEATURE}
|
||||||
|
component={
|
||||||
<ListItem twoLine key={i}>
|
<ListItem twoLine key={i}>
|
||||||
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
|
<ListItemContent
|
||||||
|
icon={'report'}
|
||||||
|
subtitle={'Missing, want to create?'}
|
||||||
|
>
|
||||||
<Link to={`/features/create?name=${name}`}>{name}</Link>
|
<Link to={`/features/create?name=${name}`}>{name}</Link>
|
||||||
</ListItemContent>
|
</ListItemContent>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
}
|
||||||
|
otherwise={
|
||||||
|
<ListItem twoLine key={i}>
|
||||||
|
<ListItemContent icon={'report'} subtitle={'Missing'}>
|
||||||
|
{name}
|
||||||
|
</ListItemContent>
|
||||||
|
</ListItem>
|
||||||
|
}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ListItem twoLine key={i}>
|
<ListItem twoLine key={i}>
|
||||||
<ListItemContent
|
<ListItemContent
|
||||||
@ -120,11 +137,26 @@ class ClientApplications extends PureComponent {
|
|||||||
{strategies.map(
|
{strategies.map(
|
||||||
({ name, description, notFound }, i) =>
|
({ name, description, notFound }, i) =>
|
||||||
notFound ? (
|
notFound ? (
|
||||||
|
<PermissionComponent
|
||||||
|
permission={CREATE_STRATEGY}
|
||||||
|
component={
|
||||||
<ListItem twoLine key={`${name}-${i}`}>
|
<ListItem twoLine key={`${name}-${i}`}>
|
||||||
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
|
<ListItemContent
|
||||||
|
icon={'report'}
|
||||||
|
subtitle={'Missing, want to create?'}
|
||||||
|
>
|
||||||
<Link to={`/strategies/create?name=${name}`}>{name}</Link>
|
<Link to={`/strategies/create?name=${name}`}>{name}</Link>
|
||||||
</ListItemContent>
|
</ListItemContent>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
}
|
||||||
|
otherwise={
|
||||||
|
<ListItem twoLine key={`${name}-${i}`}>
|
||||||
|
<ListItemContent icon={'report'} subtitle={'Missing'}>
|
||||||
|
{name}
|
||||||
|
</ListItemContent>
|
||||||
|
</ListItem>
|
||||||
|
}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ListItem twoLine key={`${name}-${i}`}>
|
<ListItem twoLine key={`${name}-${i}`}>
|
||||||
<ListItemContent icon={'extension'} subtitle={shorten(description, 60)}>
|
<ListItemContent icon={'extension'} subtitle={shorten(description, 60)}>
|
||||||
@ -203,6 +235,9 @@ class ClientApplications extends PureComponent {
|
|||||||
</CardMenu>
|
</CardMenu>
|
||||||
)}
|
)}
|
||||||
<hr />
|
<hr />
|
||||||
|
<PermissionComponent
|
||||||
|
permission={UPDATE_APPLICATION}
|
||||||
|
component={
|
||||||
<Tabs
|
<Tabs
|
||||||
activeTab={this.state.activeTab}
|
activeTab={this.state.activeTab}
|
||||||
onChange={tabId => this.setState({ activeTab: tabId })}
|
onChange={tabId => this.setState({ activeTab: tabId })}
|
||||||
@ -213,6 +248,8 @@ class ClientApplications extends PureComponent {
|
|||||||
<Tab>Details</Tab>
|
<Tab>Details</Tab>
|
||||||
<Tab>Edit</Tab>
|
<Tab>Edit</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{content}
|
{content}
|
||||||
</Card>
|
</Card>
|
||||||
|
47
frontend/src/component/common/permission-component.jsx
Normal file
47
frontend/src/component/common/permission-component.jsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { ADMIN } from '../../permissions';
|
||||||
|
|
||||||
|
class PermissionComponent extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
user: PropTypes.object,
|
||||||
|
component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
||||||
|
others: PropTypes.object,
|
||||||
|
denied: PropTypes.object,
|
||||||
|
granted: PropTypes.object,
|
||||||
|
otherwise: PropTypes.node,
|
||||||
|
permission: PropTypes.string,
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { user, otherwise, component: Component, permission, granted, denied, children, ...others } = this.props;
|
||||||
|
let grantedComponent = Component;
|
||||||
|
let deniedCompoinent = otherwise || '';
|
||||||
|
|
||||||
|
if (granted || denied) {
|
||||||
|
grantedComponent = (
|
||||||
|
<Component {...others} {...granted || {}}>
|
||||||
|
{children}
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
deniedCompoinent = (
|
||||||
|
<Component {...others} {...denied || {}}>
|
||||||
|
{children}
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) return deniedCompoinent;
|
||||||
|
if (
|
||||||
|
!user.permissions ||
|
||||||
|
user.permissions.indexOf(ADMIN) !== -1 ||
|
||||||
|
user.permissions.indexOf(permission) !== -1
|
||||||
|
) {
|
||||||
|
return grantedComponent;
|
||||||
|
}
|
||||||
|
return deniedCompoinent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PermissionComponent;
|
8
frontend/src/component/common/permission-container.js
Normal file
8
frontend/src/component/common/permission-container.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import PermissionComponent from './permission-component';
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({ user: state.user.get('profile') });
|
||||||
|
|
||||||
|
const Container = connect(mapStateToProps)(PermissionComponent);
|
||||||
|
|
||||||
|
export default Container;
|
@ -22,7 +22,7 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
>
|
>
|
||||||
<react-mdl-Switch
|
<react-mdl-Switch
|
||||||
checked={false}
|
checked={false}
|
||||||
disabled={false}
|
disabled={true}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
title="Toggle Another"
|
title="Toggle Another"
|
||||||
/>
|
/>
|
||||||
|
@ -14,6 +14,7 @@ const Feature = ({
|
|||||||
metricsLastHour = { yes: 0, no: 0, isFallback: true },
|
metricsLastHour = { yes: 0, no: 0, isFallback: true },
|
||||||
metricsLastMinute = { yes: 0, no: 0, isFallback: true },
|
metricsLastMinute = { yes: 0, no: 0, isFallback: true },
|
||||||
revive,
|
revive,
|
||||||
|
updateable,
|
||||||
}) => {
|
}) => {
|
||||||
const { name, description, enabled, strategies } = feature;
|
const { name, description, enabled, strategies } = feature;
|
||||||
const { showLastHour = false } = settings;
|
const { showLastHour = false } = settings;
|
||||||
@ -42,7 +43,7 @@ const Feature = ({
|
|||||||
</span>
|
</span>
|
||||||
<span className={styles.listItemToggle}>
|
<span className={styles.listItemToggle}>
|
||||||
<Switch
|
<Switch
|
||||||
disabled={toggleFeature === undefined}
|
disabled={!updateable || toggleFeature === undefined}
|
||||||
title={`Toggle ${name}`}
|
title={`Toggle ${name}`}
|
||||||
key="left-actions"
|
key="left-actions"
|
||||||
onChange={() => toggleFeature(name)}
|
onChange={() => toggleFeature(name)}
|
||||||
@ -59,7 +60,7 @@ const Feature = ({
|
|||||||
{strategyChips}
|
{strategyChips}
|
||||||
{summaryChip}
|
{summaryChip}
|
||||||
</span>
|
</span>
|
||||||
{revive ? (
|
{updateable && revive ? (
|
||||||
<ListItemAction onClick={() => revive(feature.name)}>
|
<ListItemAction onClick={() => revive(feature.name)}>
|
||||||
<Icon name="undo" />
|
<Icon name="undo" />
|
||||||
</ListItemAction>
|
</ListItemAction>
|
||||||
@ -77,6 +78,7 @@ Feature.propTypes = {
|
|||||||
metricsLastHour: PropTypes.object,
|
metricsLastHour: PropTypes.object,
|
||||||
metricsLastMinute: PropTypes.object,
|
metricsLastMinute: PropTypes.object,
|
||||||
revive: PropTypes.func,
|
revive: PropTypes.func,
|
||||||
|
updateable: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Feature;
|
export default Feature;
|
||||||
|
@ -5,6 +5,8 @@ import { Link } from 'react-router-dom';
|
|||||||
import { Icon, FABButton, Textfield, Menu, MenuItem, Card, CardActions, List } from 'react-mdl';
|
import { Icon, FABButton, Textfield, Menu, MenuItem, Card, CardActions, List } from 'react-mdl';
|
||||||
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
|
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
|
||||||
import styles from './feature.scss';
|
import styles from './feature.scss';
|
||||||
|
import { CREATE_FEATURE } from '../../permissions';
|
||||||
|
import PermissionComponent from '../common/permission-container';
|
||||||
|
|
||||||
export default class FeatureListComponent extends React.Component {
|
export default class FeatureListComponent extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -62,11 +64,16 @@ export default class FeatureListComponent extends React.Component {
|
|||||||
label="Search"
|
label="Search"
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
|
<PermissionComponent
|
||||||
|
permission={CREATE_FEATURE}
|
||||||
|
component={
|
||||||
<Link to="/features/create" className={styles.toolbarButton}>
|
<Link to="/features/create" className={styles.toolbarButton}>
|
||||||
<FABButton accent title="Create feature toggle">
|
<FABButton accent title="Create feature toggle">
|
||||||
<Icon name="add" />
|
<Icon name="add" />
|
||||||
</FABButton>
|
</FABButton>
|
||||||
</Link>
|
</Link>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
|
<Card shadow={0} className={commonStyles.fullwidth} style={{ overflow: 'visible' }}>
|
||||||
<CardActions>
|
<CardActions>
|
||||||
|
@ -8,6 +8,8 @@ import MetricComponent from './metric-container';
|
|||||||
import EditFeatureToggle from './form/form-update-feature-container';
|
import EditFeatureToggle from './form/form-update-feature-container';
|
||||||
import ViewFeatureToggle from './form/form-view-feature-container';
|
import ViewFeatureToggle from './form/form-view-feature-container';
|
||||||
import { styles as commonStyles } from '../common';
|
import { styles as commonStyles } from '../common';
|
||||||
|
import { CREATE_FEATURE, DELETE_FEATURE, UPDATE_FEATURE } from '../../permissions';
|
||||||
|
import PermissionComponent from '../common/permission-container';
|
||||||
|
|
||||||
const TABS = {
|
const TABS = {
|
||||||
strategies: 0,
|
strategies: 0,
|
||||||
@ -54,7 +56,17 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
} else if (TABS[activeTab] === TABS.strategies) {
|
} else if (TABS[activeTab] === TABS.strategies) {
|
||||||
if (this.isFeatureView) {
|
if (this.isFeatureView) {
|
||||||
return (
|
return (
|
||||||
<EditFeatureToggle featureToggle={featureToggle} features={features} history={this.props.history} />
|
<PermissionComponent
|
||||||
|
permission={UPDATE_FEATURE}
|
||||||
|
component={
|
||||||
|
<EditFeatureToggle
|
||||||
|
featureToggle={featureToggle}
|
||||||
|
features={features}
|
||||||
|
history={this.props.history}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
otherwise={<ViewFeatureToggle featureToggle={featureToggle} />}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return <ViewFeatureToggle featureToggle={featureToggle} />;
|
return <ViewFeatureToggle featureToggle={featureToggle} />;
|
||||||
@ -87,6 +99,9 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
Could not find the toggle{' '}
|
Could not find the toggle{' '}
|
||||||
|
<PermissionComponent
|
||||||
|
permission={CREATE_FEATURE}
|
||||||
|
component={
|
||||||
<Link
|
<Link
|
||||||
to={{
|
to={{
|
||||||
pathname: '/features/create',
|
pathname: '/features/create',
|
||||||
@ -95,6 +110,9 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
>
|
>
|
||||||
{featureToggleName}
|
{featureToggleName}
|
||||||
</Link>
|
</Link>
|
||||||
|
}
|
||||||
|
otherwise={featureToggleName}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -115,8 +133,8 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
revive(featureToggle.name);
|
revive(featureToggle.name);
|
||||||
this.props.history.push('/features');
|
this.props.history.push('/features');
|
||||||
};
|
};
|
||||||
const updateFeatureToggle = () => {
|
const updateFeatureToggle = e => {
|
||||||
let feature = { ...featureToggle };
|
let feature = { ...featureToggle, description: e.target.value };
|
||||||
if (Array.isArray(feature.strategies)) {
|
if (Array.isArray(feature.strategies)) {
|
||||||
feature.strategies.forEach(s => {
|
feature.strategies.forEach(s => {
|
||||||
delete s.id;
|
delete s.id;
|
||||||
@ -135,15 +153,22 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
<CardTitle style={{ paddingTop: '24px', wordBreak: 'break-all' }}>{featureToggle.name}</CardTitle>
|
<CardTitle style={{ paddingTop: '24px', wordBreak: 'break-all' }}>{featureToggle.name}</CardTitle>
|
||||||
<CardText>
|
<CardText>
|
||||||
{this.isFeatureView ? (
|
{this.isFeatureView ? (
|
||||||
<Textfield
|
<PermissionComponent
|
||||||
|
permission={UPDATE_FEATURE}
|
||||||
|
component={Textfield}
|
||||||
|
granted={{
|
||||||
|
onChange: v => setValue('description', v),
|
||||||
|
onBlur: updateFeatureToggle,
|
||||||
|
}}
|
||||||
|
denied={{
|
||||||
|
disabled: true,
|
||||||
|
}}
|
||||||
floatingLabel
|
floatingLabel
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
rows={1}
|
rows={1}
|
||||||
label="Description"
|
label="Description"
|
||||||
required
|
required
|
||||||
value={featureToggle.description}
|
value={featureToggle.description}
|
||||||
onChange={v => setValue('description', v)}
|
|
||||||
onBlur={updateFeatureToggle}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Textfield
|
<Textfield
|
||||||
@ -167,24 +192,41 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ paddingRight: '24px' }}>
|
<span style={{ paddingRight: '24px' }}>
|
||||||
<Switch
|
<PermissionComponent
|
||||||
disabled={!this.isFeatureView}
|
permission={UPDATE_FEATURE}
|
||||||
|
component={Switch}
|
||||||
|
granted={{
|
||||||
|
disabled: !this.isFeatureView,
|
||||||
|
}}
|
||||||
|
denied={{
|
||||||
|
disabled: true,
|
||||||
|
}}
|
||||||
ripple
|
ripple
|
||||||
checked={featureToggle.enabled}
|
checked={featureToggle.enabled}
|
||||||
onChange={() => toggleFeature(featureToggle.name)}
|
onChange={() => toggleFeature(featureToggle.name)}
|
||||||
>
|
>
|
||||||
{featureToggle.enabled ? 'Enabled' : 'Disabled'}
|
{featureToggle.enabled ? 'Enabled' : 'Disabled'}
|
||||||
</Switch>
|
</PermissionComponent>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{this.isFeatureView ? (
|
{this.isFeatureView ? (
|
||||||
|
<PermissionComponent
|
||||||
|
permission={DELETE_FEATURE}
|
||||||
|
component={
|
||||||
<Button onClick={removeToggle} style={{ flexShrink: 0 }}>
|
<Button onClick={removeToggle} style={{ flexShrink: 0 }}>
|
||||||
Archive
|
Archive
|
||||||
</Button>
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
<PermissionComponent
|
||||||
|
permission={UPDATE_FEATURE}
|
||||||
|
component={
|
||||||
<Button onClick={reviveToggle} style={{ flexShrink: 0 }}>
|
<Button onClick={reviveToggle} style={{ flexShrink: 0 }}>
|
||||||
Revive
|
Revive
|
||||||
</Button>
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</CardActions>
|
</CardActions>
|
||||||
<hr />
|
<hr />
|
||||||
|
@ -4,6 +4,8 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import { List, ListItem, ListItemContent, IconButton, Grid, Cell } from 'react-mdl';
|
import { List, ListItem, ListItemContent, IconButton, Grid, Cell } from 'react-mdl';
|
||||||
import { HeaderTitle } from '../common';
|
import { HeaderTitle } from '../common';
|
||||||
|
import { CREATE_STRATEGY, DELETE_STRATEGY } from '../../permissions';
|
||||||
|
import PermissionComponent from '../common/permission-container';
|
||||||
|
|
||||||
class StrategiesListComponent extends Component {
|
class StrategiesListComponent extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -26,6 +28,9 @@ class StrategiesListComponent extends Component {
|
|||||||
<HeaderTitle
|
<HeaderTitle
|
||||||
title="Strategies"
|
title="Strategies"
|
||||||
actions={
|
actions={
|
||||||
|
<PermissionComponent
|
||||||
|
permission={CREATE_STRATEGY}
|
||||||
|
component={
|
||||||
<IconButton
|
<IconButton
|
||||||
raised
|
raised
|
||||||
name="add"
|
name="add"
|
||||||
@ -34,6 +39,8 @@ class StrategiesListComponent extends Component {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<List>
|
<List>
|
||||||
{strategies.length > 0 ? (
|
{strategies.length > 0 ? (
|
||||||
strategies.map((strategy, i) => (
|
strategies.map((strategy, i) => (
|
||||||
@ -46,7 +53,12 @@ class StrategiesListComponent extends Component {
|
|||||||
{strategy.editable === false ? (
|
{strategy.editable === false ? (
|
||||||
''
|
''
|
||||||
) : (
|
) : (
|
||||||
|
<PermissionComponent
|
||||||
|
permission={DELETE_STRATEGY}
|
||||||
|
component={
|
||||||
<IconButton name="delete" onClick={() => removeStrategy(strategy)} />
|
<IconButton name="delete" onClick={() => removeStrategy(strategy)} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))
|
))
|
||||||
|
@ -4,6 +4,8 @@ import { Tabs, Tab, ProgressBar, Grid, Cell } from 'react-mdl';
|
|||||||
import ShowStrategy from './show-strategy-component';
|
import ShowStrategy from './show-strategy-component';
|
||||||
import EditStrategy from './edit-container';
|
import EditStrategy from './edit-container';
|
||||||
import { HeaderTitle } from '../common';
|
import { HeaderTitle } from '../common';
|
||||||
|
import { UPDATE_STRATEGY } from '../../permissions';
|
||||||
|
import PermissionComponent from '../common/permission-container';
|
||||||
|
|
||||||
const TABS = {
|
const TABS = {
|
||||||
view: 0,
|
view: 0,
|
||||||
@ -69,10 +71,15 @@ export default class StrategyDetails extends Component {
|
|||||||
{strategy.editable === false ? (
|
{strategy.editable === false ? (
|
||||||
''
|
''
|
||||||
) : (
|
) : (
|
||||||
|
<PermissionComponent
|
||||||
|
permission={UPDATE_STRATEGY}
|
||||||
|
component={
|
||||||
<Tabs activeTab={activeTabId} ripple>
|
<Tabs activeTab={activeTabId} ripple>
|
||||||
<Tab onClick={() => this.goToTab('view')}>Details</Tab>
|
<Tab onClick={() => this.goToTab('view')}>Details</Tab>
|
||||||
<Tab onClick={() => this.goToTab('edit')}>Edit</Tab>
|
<Tab onClick={() => this.goToTab('edit')}>Edit</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
@ -23,12 +23,25 @@ export class AuthenticationError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ForbiddenError extends Error {
|
||||||
|
constructor(statusCode, body) {
|
||||||
|
super('You cannot perform this action');
|
||||||
|
this.name = 'ForbiddenError';
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function throwIfNotSuccess(response) {
|
export function throwIfNotSuccess(response) {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
response.json().then(body => reject(new AuthenticationError(response.status, body)));
|
response.json().then(body => reject(new AuthenticationError(response.status, body)));
|
||||||
});
|
});
|
||||||
|
} else if (response.status === 403) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
response.json().then(body => reject(new ForbiddenError(response.status, body)));
|
||||||
|
});
|
||||||
} else if (response.status > 399 && response.status < 404) {
|
} else if (response.status > 399 && response.status < 404) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
response.json().then(body => {
|
response.json().then(body => {
|
||||||
|
8
frontend/src/permissions.js
Normal file
8
frontend/src/permissions.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const ADMIN = 'ADMIN';
|
||||||
|
export const CREATE_FEATURE = 'CREATE_FEATURE';
|
||||||
|
export const UPDATE_FEATURE = 'UPDATE_FEATURE';
|
||||||
|
export const DELETE_FEATURE = 'DELETE_FEATURE';
|
||||||
|
export const CREATE_STRATEGY = 'CREATE_STRATEGY';
|
||||||
|
export const UPDATE_STRATEGY = 'UPDATE_STRATEGY';
|
||||||
|
export const DELETE_STRATEGY = 'DELETE_STRATEGY';
|
||||||
|
export const UPDATE_APPLICATION = 'UPDATE_APPLICATION';
|
Loading…
Reference in New Issue
Block a user