mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-08 01:15:49 +02:00
feat: add CREATE_TAG_TYPE permission (#5386)
https://linear.app/unleash/issue/2-1164/update-tag-type-covers-both-creation-and-update Adds a new `CREATE_TAG_TYPE` permission instead of using `UPDATE_TAG_TYPE` for both actions.
This commit is contained in:
parent
fac2578922
commit
5dc3e830a8
@ -224,7 +224,7 @@ export const ManageBulkTagsDialog: VFC<IManageBulkTagsDialogProps> = ({
|
|||||||
open={open}
|
open={open}
|
||||||
secondaryButtonText='Cancel'
|
secondaryButtonText='Cancel'
|
||||||
primaryButtonText='Save tags'
|
primaryButtonText='Save tags'
|
||||||
title='Update tags to feature toggle'
|
title='Update feature toggle tags'
|
||||||
onClick={() => onSubmit(payload)}
|
onClick={() => onSubmit(payload)}
|
||||||
disabledPrimaryButton={
|
disabledPrimaryButton={
|
||||||
payload.addedTags.length === 0 &&
|
payload.addedTags.length === 0 &&
|
||||||
|
@ -248,7 +248,7 @@ export const ManageTagsDialog = ({ open, setOpen }: IManageTagsProps) => {
|
|||||||
open={open}
|
open={open}
|
||||||
secondaryButtonText='Cancel'
|
secondaryButtonText='Cancel'
|
||||||
primaryButtonText='Save tags'
|
primaryButtonText='Save tags'
|
||||||
title='Update tags to feature toggle'
|
title='Update feature toggle tags'
|
||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
disabledPrimaryButton={loading || differenceCount === 0}
|
disabledPrimaryButton={loading || differenceCount === 0}
|
||||||
onClose={onCancel}
|
onClose={onCancel}
|
||||||
|
@ -13,8 +13,9 @@ export const DELETE_CONTEXT_FIELD = 'DELETE_CONTEXT_FIELD';
|
|||||||
export const CREATE_PROJECT = 'CREATE_PROJECT';
|
export const CREATE_PROJECT = 'CREATE_PROJECT';
|
||||||
export const UPDATE_PROJECT = 'UPDATE_PROJECT';
|
export const UPDATE_PROJECT = 'UPDATE_PROJECT';
|
||||||
export const DELETE_PROJECT = 'DELETE_PROJECT';
|
export const DELETE_PROJECT = 'DELETE_PROJECT';
|
||||||
export const DELETE_TAG_TYPE = 'DELETE_TAG_TYPE';
|
export const CREATE_TAG_TYPE = 'CREATE_TAG_TYPE';
|
||||||
export const UPDATE_TAG_TYPE = 'UPDATE_TAG_TYPE';
|
export const UPDATE_TAG_TYPE = 'UPDATE_TAG_TYPE';
|
||||||
|
export const DELETE_TAG_TYPE = 'DELETE_TAG_TYPE';
|
||||||
export const CREATE_ADDON = 'CREATE_ADDON';
|
export const CREATE_ADDON = 'CREATE_ADDON';
|
||||||
export const UPDATE_ADDON = 'UPDATE_ADDON';
|
export const UPDATE_ADDON = 'UPDATE_ADDON';
|
||||||
export const DELETE_ADDON = 'DELETE_ADDON';
|
export const DELETE_ADDON = 'DELETE_ADDON';
|
||||||
|
@ -3,7 +3,7 @@ import useTagTypeForm from '../TagTypeForm/useTagTypeForm';
|
|||||||
import TagTypeForm from '../TagTypeForm/TagTypeForm';
|
import TagTypeForm from '../TagTypeForm/TagTypeForm';
|
||||||
import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
import { CreateButton } from 'component/common/CreateButton/CreateButton';
|
||||||
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
|
||||||
import { UPDATE_TAG_TYPE } from 'component/providers/AccessProvider/permissions';
|
import { CREATE_TAG_TYPE } from 'component/providers/AccessProvider/permissions';
|
||||||
import useTagTypesApi from 'hooks/api/actions/useTagTypesApi/useTagTypesApi';
|
import useTagTypesApi from 'hooks/api/actions/useTagTypesApi/useTagTypesApi';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
@ -78,7 +78,7 @@ const CreateTagType = () => {
|
|||||||
clearErrors={clearErrors}
|
clearErrors={clearErrors}
|
||||||
validateNameUniqueness={validateNameUniqueness}
|
validateNameUniqueness={validateNameUniqueness}
|
||||||
>
|
>
|
||||||
<CreateButton name='type' permission={UPDATE_TAG_TYPE} />
|
<CreateButton name='type' permission={CREATE_TAG_TYPE} />
|
||||||
</TagTypeForm>
|
</TagTypeForm>
|
||||||
</FormTemplate>
|
</FormTemplate>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||||
import { UPDATE_TAG_TYPE } from 'component/providers/AccessProvider/permissions';
|
import { CREATE_TAG_TYPE } from 'component/providers/AccessProvider/permissions';
|
||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -18,14 +18,14 @@ export const AddTagTypeButton = () => {
|
|||||||
<PermissionIconButton
|
<PermissionIconButton
|
||||||
onClick={() => navigate('/tag-types/create')}
|
onClick={() => navigate('/tag-types/create')}
|
||||||
size='large'
|
size='large'
|
||||||
permission={UPDATE_TAG_TYPE}
|
permission={CREATE_TAG_TYPE}
|
||||||
>
|
>
|
||||||
<Add />
|
<Add />
|
||||||
</PermissionIconButton>
|
</PermissionIconButton>
|
||||||
}
|
}
|
||||||
elseShow={
|
elseShow={
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
permission={UPDATE_TAG_TYPE}
|
permission={CREATE_TAG_TYPE}
|
||||||
onClick={() => navigate('/tag-types/create')}
|
onClick={() => navigate('/tag-types/create')}
|
||||||
>
|
>
|
||||||
New tag type
|
New tag type
|
||||||
|
@ -373,11 +373,11 @@ test('validate import data', async () => {
|
|||||||
affectedItems: [
|
affectedItems: [
|
||||||
'Create feature toggles',
|
'Create feature toggles',
|
||||||
'Update feature toggles',
|
'Update feature toggles',
|
||||||
'Update tag types',
|
|
||||||
'Create context fields',
|
'Create context fields',
|
||||||
'Create activation strategies',
|
'Create activation strategies',
|
||||||
'Delete activation strategies',
|
'Delete activation strategies',
|
||||||
'Update variants',
|
'Update variants',
|
||||||
|
'Create tag types',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
UPDATE_FEATURE,
|
UPDATE_FEATURE,
|
||||||
UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
|
UPDATE_FEATURE_ENVIRONMENT_VARIANTS,
|
||||||
UPDATE_TAG_TYPE,
|
UPDATE_TAG_TYPE,
|
||||||
|
CREATE_TAG_TYPE,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
import { PermissionError } from '../../error';
|
import { PermissionError } from '../../error';
|
||||||
|
|
||||||
@ -94,7 +95,7 @@ export class ImportPermissionsService {
|
|||||||
]);
|
]);
|
||||||
const permissions = [UPDATE_FEATURE];
|
const permissions = [UPDATE_FEATURE];
|
||||||
if (newTagTypes.length > 0) {
|
if (newTagTypes.length > 0) {
|
||||||
permissions.push(UPDATE_TAG_TYPE);
|
permissions.push(CREATE_TAG_TYPE);
|
||||||
}
|
}
|
||||||
if (Array.isArray(newContextFields) && newContextFields.length > 0) {
|
if (Array.isArray(newContextFields) && newContextFields.length > 0) {
|
||||||
permissions.push(CREATE_CONTEXT_FIELD);
|
permissions.push(CREATE_CONTEXT_FIELD);
|
||||||
|
@ -329,6 +329,34 @@ test('Does not double check permission if not changing project when updating tog
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('CREATE_TAG_TYPE does not need projectId', async () => {
|
||||||
|
const accessService = {
|
||||||
|
hasPermission: jest.fn().mockReturnValue(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
const func = rbacMiddleware(
|
||||||
|
config,
|
||||||
|
{ featureToggleStore, segmentStore },
|
||||||
|
accessService,
|
||||||
|
);
|
||||||
|
const cb = jest.fn();
|
||||||
|
const req: any = {
|
||||||
|
user: new User({ username: 'user', id: 1 }),
|
||||||
|
params: {},
|
||||||
|
body: { name: 'new-tag-type', description: 'New tag type for testing' },
|
||||||
|
};
|
||||||
|
func(req, undefined, cb);
|
||||||
|
|
||||||
|
await req.checkRbac(perms.CREATE_TAG_TYPE);
|
||||||
|
expect(accessService.hasPermission).toHaveBeenCalledTimes(1);
|
||||||
|
expect(accessService.hasPermission).toHaveBeenCalledWith(
|
||||||
|
req.user,
|
||||||
|
[perms.CREATE_TAG_TYPE],
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('UPDATE_TAG_TYPE does not need projectId', async () => {
|
test('UPDATE_TAG_TYPE does not need projectId', async () => {
|
||||||
const accessService = {
|
const accessService = {
|
||||||
hasPermission: jest.fn().mockReturnValue(true),
|
hasPermission: jest.fn().mockReturnValue(true),
|
||||||
|
@ -2,6 +2,7 @@ import { Request, Response } from 'express';
|
|||||||
import Controller from '../controller';
|
import Controller from '../controller';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
CREATE_TAG_TYPE,
|
||||||
DELETE_TAG_TYPE,
|
DELETE_TAG_TYPE,
|
||||||
NONE,
|
NONE,
|
||||||
UPDATE_TAG_TYPE,
|
UPDATE_TAG_TYPE,
|
||||||
@ -72,7 +73,7 @@ class TagTypeController extends Controller {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
path: '',
|
path: '',
|
||||||
handler: this.createTagType,
|
handler: this.createTagType,
|
||||||
permission: UPDATE_TAG_TYPE,
|
permission: CREATE_TAG_TYPE,
|
||||||
middleware: [
|
middleware: [
|
||||||
openApiService.validPath({
|
openApiService.validPath({
|
||||||
tags: ['Tags'],
|
tags: ['Tags'],
|
||||||
@ -91,7 +92,7 @@ class TagTypeController extends Controller {
|
|||||||
method: 'post',
|
method: 'post',
|
||||||
path: '/validate',
|
path: '/validate',
|
||||||
handler: this.validateTagType,
|
handler: this.validateTagType,
|
||||||
permission: UPDATE_TAG_TYPE,
|
permission: NONE,
|
||||||
middleware: [
|
middleware: [
|
||||||
openApiService.validPath({
|
openApiService.validPath({
|
||||||
tags: ['Tags'],
|
tags: ['Tags'],
|
||||||
|
@ -37,6 +37,7 @@ export const CREATE_STRATEGY = 'CREATE_STRATEGY';
|
|||||||
export const UPDATE_STRATEGY = 'UPDATE_STRATEGY';
|
export const UPDATE_STRATEGY = 'UPDATE_STRATEGY';
|
||||||
export const DELETE_STRATEGY = 'DELETE_STRATEGY';
|
export const DELETE_STRATEGY = 'DELETE_STRATEGY';
|
||||||
|
|
||||||
|
export const CREATE_TAG_TYPE = 'CREATE_TAG_TYPE';
|
||||||
export const UPDATE_TAG_TYPE = 'UPDATE_TAG_TYPE';
|
export const UPDATE_TAG_TYPE = 'UPDATE_TAG_TYPE';
|
||||||
export const DELETE_TAG_TYPE = 'DELETE_TAG_TYPE';
|
export const DELETE_TAG_TYPE = 'DELETE_TAG_TYPE';
|
||||||
|
|
||||||
@ -112,6 +113,6 @@ export const ROOT_PERMISSION_CATEGORIES = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Tag type',
|
label: 'Tag type',
|
||||||
permissions: [UPDATE_TAG_TYPE, DELETE_TAG_TYPE],
|
permissions: [CREATE_TAG_TYPE, UPDATE_TAG_TYPE, DELETE_TAG_TYPE],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
exports.up = function (db, cb) {
|
||||||
|
db.runSql(
|
||||||
|
`
|
||||||
|
INSERT INTO permissions (permission, display_name, type) VALUES ('CREATE_TAG_TYPE', 'Create tag types', 'root');
|
||||||
|
SELECT assign_unleash_permission_to_role('CREATE_TAG_TYPE', 'Editor');
|
||||||
|
`,
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function (db, cb) {
|
||||||
|
db.runSql(
|
||||||
|
`
|
||||||
|
DELETE FROM permissions WHERE permission = 'CREATE_TAG_TYPE';
|
||||||
|
`,
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user