add scaffolder permission for template management

Signed-off-by: Stephen Glass <stephen@stephen.glass>
This commit is contained in:
Stephen Glass
2024-10-02 23:13:55 -04:00
parent 6000c69a25
commit f61d4ccc2f
7 changed files with 79 additions and 22 deletions
+7
View File
@@ -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
@@ -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'>;
@@ -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,
];
@@ -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<HTMLButtonElement>();
const { allowed: canManageTemplates } = usePermission({
permission: templateManagementPermission,
});
if (!onEditorClicked && !onActionsClicked) {
return null;
}
@@ -100,7 +106,7 @@ export function ScaffolderPageContextMenu(
<ListItemText primary="Create" />
</MenuItem>
)}
{onEditorClicked && (
{onEditorClicked && canManageTemplates && (
<MenuItem onClick={onEditorClicked}>
<ListItemIcon>
<Edit fontSize="small" />
@@ -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<ScaffolderApi> = {
scaffold: jest.fn(),
@@ -36,7 +37,11 @@ const scaffolderApiMock: jest.Mocked<ScaffolderApi> = {
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());
@@ -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('<ListTasksPage />', () => {
const catalogApi: jest.Mocked<CatalogApi> = {
@@ -49,6 +50,8 @@ describe('<ListTasksPage />', () => {
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('<ListTasksPage />', () => {
[catalogApiRef, catalogApi],
[identityApiRef, identityApi],
[scaffolderApiRef, scaffolderApiMock],
[permissionApiRef, mockPermissionApi],
]}
>
<ListTasksPage />
@@ -132,6 +136,7 @@ describe('<ListTasksPage />', () => {
[catalogApiRef, catalogApi],
[identityApiRef, identityApi],
[scaffolderApiRef, scaffolderApiMock],
[permissionApiRef, mockPermissionApi],
]}
>
<ListTasksPage />
@@ -230,6 +235,7 @@ describe('<ListTasksPage />', () => {
[catalogApiRef, catalogApi],
[identityApiRef, identityApi],
[scaffolderApiRef, scaffolderApiMock],
[permissionApiRef, mockPermissionApi],
]}
>
<ListTasksPage />
@@ -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<RouterProps>) => {
<Route
path={editRouteRef.path}
element={
<SecretsContextProvider>
<TemplateIntroPage />
</SecretsContextProvider>
<RequirePermission permission={templateManagementPermission}>
<SecretsContextProvider>
<TemplateIntroPage />
</SecretsContextProvider>
</RequirePermission>
}
/>
<Route
path={customFieldsRouteRef.path}
element={
<SecretsContextProvider>
<CustomFieldsPage fieldExtensions={fieldExtensions} />
</SecretsContextProvider>
<RequirePermission permission={templateManagementPermission}>
<SecretsContextProvider>
<CustomFieldsPage fieldExtensions={fieldExtensions} />
</SecretsContextProvider>
</RequirePermission>
}
/>
<Route
path={templateFormRouteRef.path}
element={
<SecretsContextProvider>
<TemplateFormPage
layouts={customLayouts}
formProps={props.formProps}
fieldExtensions={fieldExtensions}
/>
</SecretsContextProvider>
<RequirePermission permission={templateManagementPermission}>
<SecretsContextProvider>
<TemplateFormPage
layouts={customLayouts}
formProps={props.formProps}
fieldExtensions={fieldExtensions}
/>
</SecretsContextProvider>
</RequirePermission>
}
/>
@@ -204,13 +212,15 @@ export const Router = (props: PropsWithChildren<RouterProps>) => {
<Route
path={editorRouteRef.path}
element={
<SecretsContextProvider>
<TemplateEditorPage
layouts={customLayouts}
formProps={props.formProps}
fieldExtensions={fieldExtensions}
/>
</SecretsContextProvider>
<RequirePermission permission={templateManagementPermission}>
<SecretsContextProvider>
<TemplateEditorPage
layouts={customLayouts}
formProps={props.formProps}
fieldExtensions={fieldExtensions}
/>
</SecretsContextProvider>
</RequirePermission>
}
/>
<Route