diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index 26243bff51..b5091021c6 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -36,6 +36,11 @@ "dependencies": { "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-model": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", + "@backstage/plugin-permission-node": "workspace:^", "express-promise-router": "^4.1.1", "knex": "^3.0.0" } diff --git a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts index 72713178e8..51e3f06894 100644 --- a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts +++ b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts @@ -23,6 +23,15 @@ import { import { Knex } from 'knex'; import { HttpRouterService } from '@backstage/backend-plugin-api'; import Router from 'express-promise-router'; +import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node'; +import { + AuthorizeResult, + BasicPermission, + PermissionEvaluator, +} from '@backstage/plugin-permission-common'; +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; +import { unprocessedEntitiesDeletePermission } from '@backstage/plugin-catalog-unprocessed-entities-common'; +import { NotAllowedError } from '@backstage/errors'; /** * Module providing Unprocessed Entities API endpoints @@ -103,7 +112,31 @@ export class UnprocessedEntitiesModule { return res; } - registerRoutes() { + registerRoutes({ permissions }: { permissions: PermissionEvaluator }) { + const permissionIntegrationRouter = createPermissionIntegrationRouter({ + permissions: [unprocessedEntitiesDeletePermission], + }); + + const isRequestAuthorized = async ( + req: Request, + permission: BasicPermission, + ): Promise => { + const token = getBearerTokenFromAuthorizationHeader( + // @ts-ignore + req.header('authorization'), + ); + + const decision = ( + await permissions.authorize([{ permission }], { + token, + }) + )[0]; + + return decision.result !== AuthorizeResult.DENY; + }; + + this.router.use(permissionIntegrationRouter); + this.moduleRouter .get('/entities/unprocessed/failed', async (req, res) => { return res.json( @@ -124,6 +157,15 @@ export class UnprocessedEntitiesModule { .delete( '/entities/unprocessed/delete/:entity_id', async (request, response) => { + const authorized = await isRequestAuthorized( + request as any as Request, + unprocessedEntitiesDeletePermission, + ); + + if (!authorized) { + throw new NotAllowedError('Unauthorized'); + } + await this.database('refresh_state') .where({ entity_id: request.params.entity_id }) .delete(); diff --git a/plugins/catalog-unprocessed-entities-common/.eslintrc.js b/plugins/catalog-unprocessed-entities-common/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/catalog-unprocessed-entities-common/README.md b/plugins/catalog-unprocessed-entities-common/README.md new file mode 100644 index 0000000000..490c5e8f6e --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/README.md @@ -0,0 +1,5 @@ +# @backstage/plugin-catalog-unprocessed-entities-common + +Welcome to the common package for the catalog-unprocessed-entities plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/catalog-unprocessed-entities-common/package.json b/plugins/catalog-unprocessed-entities-common/package.json new file mode 100644 index 0000000000..b90f3d9182 --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/package.json @@ -0,0 +1,35 @@ +{ + "name": "@backstage/plugin-catalog-unprocessed-entities-common", + "description": "Common functionalities for the catalog-unprocessed-entities plugin", + "version": "0.0.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "common-library" + }, + "sideEffects": false, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ], + "dependencies": { + "@backstage/plugin-permission-common": "workspace:^" + } +} diff --git a/plugins/catalog-unprocessed-entities-common/src/index.ts b/plugins/catalog-unprocessed-entities-common/src/index.ts new file mode 100644 index 0000000000..cd5a39c731 --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common functionalities for the catalog-unprocessed-entities plugin. + * + * @packageDocumentation + */ + +export * from './permissions'; diff --git a/plugins/catalog-unprocessed-entities-common/src/permissions.ts b/plugins/catalog-unprocessed-entities-common/src/permissions.ts new file mode 100644 index 0000000000..c6ac44fc0b --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/src/permissions.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createPermission } from '@backstage/plugin-permission-common'; + +export const unprocessedEntitiesDeletePermission = createPermission({ + name: 'unprocessed-entities.delete', + attributes: { action: 'delete' }, +}); + +export const unprocessedEntitiesPermissions = { + unprocessedEntitiesDeletePermission, +}; diff --git a/plugins/catalog-unprocessed-entities-common/src/setupTests.ts b/plugins/catalog-unprocessed-entities-common/src/setupTests.ts new file mode 100644 index 0000000000..c7ce5c0988 --- /dev/null +++ b/plugins/catalog-unprocessed-entities-common/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export {}; diff --git a/yarn.lock b/yarn.lock index 69bf765b58..3f0bcdef53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,6 +1,3 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - __metadata: version: 6 cacheKey: 8 @@ -5775,6 +5772,11 @@ __metadata: "@backstage/backend-plugin-api": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" + "@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" + "@backstage/plugin-permission-node": "workspace:^" express-promise-router: ^4.1.1 knex: ^3.0.0 languageName: unknown @@ -6037,6 +6039,15 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-catalog-unprocessed-entities-common@workspace:^, @backstage/plugin-catalog-unprocessed-entities-common@workspace:plugins/catalog-unprocessed-entities-common": + version: 0.0.0-use.local + resolution: "@backstage/plugin-catalog-unprocessed-entities-common@workspace:plugins/catalog-unprocessed-entities-common" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" + languageName: unknown + linkType: soft + "@backstage/plugin-catalog-unprocessed-entities@workspace:^, @backstage/plugin-catalog-unprocessed-entities@workspace:plugins/catalog-unprocessed-entities": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-unprocessed-entities@workspace:plugins/catalog-unprocessed-entities"