diff --git a/.changeset/lemon-gifts-crash.md b/.changeset/lemon-gifts-crash.md new file mode 100644 index 0000000000..162b0b3817 --- /dev/null +++ b/.changeset/lemon-gifts-crash.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-scaffolder-common': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-scaffolder': patch +--- + +Add scaffolder permission for accessing the template management features diff --git a/plugins/scaffolder-common/report-alpha.api.md b/plugins/scaffolder-common/report-alpha.api.md index 36b6a7cdb0..47ca033731 100644 --- a/plugins/scaffolder-common/report-alpha.api.md +++ b/plugins/scaffolder-common/report-alpha.api.md @@ -18,6 +18,9 @@ export const RESOURCE_TYPE_SCAFFOLDER_TEMPLATE = 'scaffolder-template'; // @alpha export const scaffolderActionPermissions: ResourcePermission<'scaffolder-action'>[]; +// @alpha +export const scaffolderManagementPermissions: BasicPermission[]; + // @alpha export const scaffolderPermissions: ( | BasicPermission @@ -40,6 +43,9 @@ export const taskCreatePermission: BasicPermission; // @alpha export const taskReadPermission: BasicPermission; +// @alpha +export const templateManagementPermission: BasicPermission; + // @alpha export const templateParameterReadPermission: ResourcePermission<'scaffolder-template'>; diff --git a/plugins/scaffolder-common/src/permissions.ts b/plugins/scaffolder-common/src/permissions.ts index c441b48d5d..a81952a568 100644 --- a/plugins/scaffolder-common/src/permissions.ts +++ b/plugins/scaffolder-common/src/permissions.ts @@ -113,6 +113,16 @@ export const taskCancelPermission = createPermission({ attributes: {}, }); +/** + * This permission is used to authorize template management features. + * + * @alpha + */ +export const templateManagementPermission = createPermission({ + name: 'scaffolder.template.management', + attributes: {}, +}); + /** * List of the scaffolder permissions that are associated with template steps and parameters. * @alpha @@ -138,6 +148,12 @@ export const scaffolderTaskPermissions = [ taskReadPermission, ]; +/** + * List of the scaffolder permissions that are associated with scaffolder management. + * @alpha + */ +export const scaffolderManagementPermissions = [templateManagementPermission]; + /** * List of all the scaffolder permissions * @alpha @@ -146,4 +162,5 @@ export const scaffolderPermissions = [ ...scaffolderTemplatePermissions, ...scaffolderActionPermissions, ...scaffolderTaskPermissions, + ...scaffolderManagementPermissions, ]; diff --git a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx index 12ca4135f0..bbd3ae018e 100644 --- a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx +++ b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx @@ -27,6 +27,8 @@ import Edit from '@material-ui/icons/Edit'; import List from '@material-ui/icons/List'; import MoreVert from '@material-ui/icons/MoreVert'; import React, { useState } from 'react'; +import { usePermission } from '@backstage/plugin-permission-react'; +import { templateManagementPermission } from '@backstage/plugin-scaffolder-common/alpha'; const useStyles = makeStyles(theme => ({ button: { @@ -55,6 +57,10 @@ export function ScaffolderPageContextMenu( const classes = useStyles(); const [anchorEl, setAnchorEl] = useState(); + const { allowed: canManageTemplates } = usePermission({ + permission: templateManagementPermission, + }); + if (!onEditorClicked && !onActionsClicked) { return null; } @@ -100,7 +106,7 @@ export function ScaffolderPageContextMenu( )} - {onEditorClicked && ( + {onEditorClicked && canManageTemplates && ( diff --git a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx index bf056dd3b3..acfdabac5b 100644 --- a/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx +++ b/plugins/scaffolder/src/components/ActionsPage/ActionsPage.test.tsx @@ -23,6 +23,7 @@ import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; import { ApiProvider } from '@backstage/core-app-api'; import { rootRouteRef } from '../../routes'; import { userEvent } from '@testing-library/user-event'; +import { permissionApiRef } from '@backstage/plugin-permission-react'; const scaffolderApiMock: jest.Mocked = { scaffold: jest.fn(), @@ -36,7 +37,11 @@ const scaffolderApiMock: jest.Mocked = { autocomplete: jest.fn(), }; -const apis = TestApiRegistry.from([scaffolderApiRef, scaffolderApiMock]); +const mockPermissionApi = { authorize: jest.fn() }; +const apis = TestApiRegistry.from( + [scaffolderApiRef, scaffolderApiMock], + [permissionApiRef, mockPermissionApi], +); describe('TemplatePage', () => { beforeEach(() => jest.resetAllMocks()); diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx index f7f75bc3e8..ad3ca7fbde 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTaskPage.test.tsx @@ -30,6 +30,7 @@ import { } from '@backstage/plugin-scaffolder-react'; import { act, fireEvent } from '@testing-library/react'; import { rootRouteRef } from '../../routes'; +import { permissionApiRef } from '@backstage/plugin-permission-react'; describe('', () => { const catalogApi: jest.Mocked = { @@ -49,6 +50,8 @@ describe('', () => { listTasks: jest.fn(), } as any; + const mockPermissionApi = { authorize: jest.fn() }; + it('should render the page', async () => { const entity: Entity = { apiVersion: 'v1', @@ -72,6 +75,7 @@ describe('', () => { [catalogApiRef, catalogApi], [identityApiRef, identityApi], [scaffolderApiRef, scaffolderApiMock], + [permissionApiRef, mockPermissionApi], ]} > @@ -132,6 +136,7 @@ describe('', () => { [catalogApiRef, catalogApi], [identityApiRef, identityApi], [scaffolderApiRef, scaffolderApiMock], + [permissionApiRef, mockPermissionApi], ]} > @@ -230,6 +235,7 @@ describe('', () => { [catalogApiRef, catalogApi], [identityApiRef, identityApi], [scaffolderApiRef, scaffolderApiMock], + [permissionApiRef, mockPermissionApi], ]} > diff --git a/plugins/scaffolder/src/components/Router/Router.tsx b/plugins/scaffolder/src/components/Router/Router.tsx index 5766e3d043..4f578d9ff2 100644 --- a/plugins/scaffolder/src/components/Router/Router.tsx +++ b/plugins/scaffolder/src/components/Router/Router.tsx @@ -59,6 +59,8 @@ import { TemplateEditorPage, CustomFieldsPage, } from '../../alpha/components/TemplateEditorPage'; +import { RequirePermission } from '@backstage/plugin-permission-react'; +import { templateManagementPermission } from '@backstage/plugin-scaffolder-common/alpha'; /** * The Props for the Scaffolder Router @@ -170,29 +172,35 @@ export const Router = (props: PropsWithChildren) => { - - + + + + + } /> - - + + + + + } /> - - + + + + + } /> @@ -204,13 +212,15 @@ export const Router = (props: PropsWithChildren) => { - - + + + + + } />