fix permissions being overwritten by the unprocessed entities module

Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2024-03-22 11:41:57 +01:00
parent 623eaf602f
commit f3e2e86c33
14 changed files with 64 additions and 14 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-catalog-backend': minor
'@backstage/plugin-catalog-node': minor
---
Added the ability to inject custom permissions from modules, on `CatalogBuilder` and `CatalogPermissionExtensionPoint`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-unprocessed': patch
---
Internal update that injects custom permissions into the catalog using its extension point
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-permission-backend': patch
---
Properly forward causes of errors from upstream backends in the `PermissionIntegrationClient`
@@ -40,6 +40,7 @@
"@backstage/catalog-model": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/plugin-auth-node": "workspace:^",
"@backstage/plugin-catalog-node": "workspace:^",
"@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^",
"@backstage/plugin-permission-common": "workspace:^",
"@backstage/plugin-permission-node": "workspace:^",
@@ -34,7 +34,6 @@ import {
AuthorizeResult,
BasicPermission,
} 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';
import { createLegacyAuthAdapters } from '@backstage/backend-common';
@@ -145,10 +144,6 @@ export class UnprocessedEntitiesModule {
}
registerRoutes() {
const permissionIntegrationRouter = createPermissionIntegrationRouter({
permissions: [unprocessedEntitiesDeletePermission],
});
const isRequestAuthorized = async (
req: Request,
permission: BasicPermission,
@@ -162,8 +157,6 @@ export class UnprocessedEntitiesModule {
return decision.result !== AuthorizeResult.DENY;
};
this.router.use(permissionIntegrationRouter);
this.moduleRouter
.get('/entities/unprocessed/failed', async (req, res) => {
return res.json(
@@ -19,6 +19,8 @@ import {
createBackendModule,
} from '@backstage/backend-plugin-api';
import { UnprocessedEntitiesModule } from './UnprocessedEntitiesModule';
import { catalogPermissionExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
import { unprocessedEntitiesDeletePermission } from '@backstage/plugin-catalog-unprocessed-entities-common';
/**
* Catalog Module for Unprocessed Entities
@@ -37,6 +39,7 @@ export const catalogModuleUnprocessedEntities = createBackendModule({
httpAuth: coreServices.httpAuth,
discovery: coreServices.discovery,
permissions: coreServices.permissions,
catalogPermissions: catalogPermissionExtensionPoint,
},
async init({
database,
@@ -45,6 +48,7 @@ export const catalogModuleUnprocessedEntities = createBackendModule({
permissions,
httpAuth,
discovery,
catalogPermissions,
}) {
const module = UnprocessedEntitiesModule.create({
database: await database.getClient(),
@@ -54,6 +58,8 @@ export const catalogModuleUnprocessedEntities = createBackendModule({
httpAuth,
});
catalogPermissions.addPermissions(unprocessedEntitiesDeletePermission);
module.registerRoutes();
logger.info(
+1
View File
@@ -148,6 +148,7 @@ export class CatalogBuilder {
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
>
): this;
addPermissions(...permissions: Array<Permission | Array<Permission>>): this;
addProcessor(
...processors: Array<CatalogProcessor_2 | Array<CatalogProcessor_2>>
): CatalogBuilder;
@@ -84,7 +84,10 @@ import { DefaultCatalogRulesEnforcer } from '../ingestion/CatalogRules';
import { Config, readDurationFromConfig } from '@backstage/config';
import { Logger } from 'winston';
import { connectEntityProviders } from '../processing/connectEntityProviders';
import { PermissionRuleParams } from '@backstage/plugin-permission-common';
import {
Permission,
PermissionRuleParams,
} from '@backstage/plugin-permission-common';
import { permissionRules as catalogPermissionRules } from '../permissions/rules';
import { PermissionRule } from '@backstage/plugin-permission-node';
import {
@@ -177,6 +180,7 @@ export class CatalogBuilder {
}) => Promise<void> | void;
private processingInterval: ProcessingIntervalFunction;
private locationAnalyzer: LocationAnalyzer | undefined = undefined;
private readonly permissions: Permission[];
private readonly permissionRules: CatalogPermissionRuleInput[];
private allowedLocationType: string[];
private legacySingleProcessorValidation = false;
@@ -200,6 +204,7 @@ export class CatalogBuilder {
this.locationAnalyzers = [];
this.processorsReplace = false;
this.parser = undefined;
this.permissions = [...catalogPermissions];
this.permissionRules = Object.values(catalogPermissionRules);
this.allowedLocationType = ['url'];
@@ -401,6 +406,17 @@ export class CatalogBuilder {
return this;
}
/**
* Adds additional permissions. See
* {@link @backstage/plugin-permission-node#Permission}.
*
* @param permissions - Additional permissions
*/
addPermissions(...permissions: Array<Permission | Array<Permission>>) {
this.permissions.push(...permissions.flat());
return this;
}
/**
* Adds additional permission rules. Permission rules are used to evaluate
* catalog resources against queries. See
@@ -551,7 +567,7 @@ export class CatalogBuilder {
entitiesByRef[stringifyEntityRef(parseEntityRef(resourceRef))],
);
},
permissions: catalogPermissions,
permissions: this.permissions,
rules: this.permissionRules,
});
@@ -38,6 +38,7 @@ import {
} from '@backstage/plugin-catalog-node';
import { loggerToWinstonLogger } from '@backstage/backend-common';
import { merge } from 'lodash';
import { Permission } from '@backstage/plugin-permission-common';
class CatalogProcessingExtensionPointImpl
implements CatalogProcessingExtensionPoint
@@ -113,8 +114,13 @@ class CatalogAnalysisExtensionPointImpl
class CatalogPermissionExtensionPointImpl
implements CatalogPermissionExtensionPoint
{
#permissions = new Array<Permission>();
#permissionRules = new Array<CatalogPermissionRuleInput>();
addPermissions(...permission: Array<Permission | Array<Permission>>): void {
this.#permissions.push(...permission.flat());
}
addPermissionRules(
...rules: Array<
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
@@ -123,6 +129,10 @@ class CatalogPermissionExtensionPointImpl
this.#permissionRules.push(...rules.flat());
}
get permissions() {
return this.#permissions;
}
get permissionRules() {
return this.#permissionRules;
}
@@ -239,6 +249,7 @@ export const catalogPlugin = createBackendPlugin({
([key, resolver]) => builder.setPlaceholderResolver(key, resolver),
);
builder.addLocationAnalyzers(...analysisExtensions.locationAnalyzers);
builder.addPermissions(...permissionExtensions.permissions);
builder.addPermissionRules(...permissionExtensions.permissionRules);
builder.setFieldFormatValidators(modelExtensions.fieldValidators);
+3
View File
@@ -10,6 +10,7 @@ import { EntitiesSearchFilter } from '@backstage/plugin-catalog-node';
import { Entity } from '@backstage/catalog-model';
import { EntityProvider } from '@backstage/plugin-catalog-node';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
import { Permission } from '@backstage/plugin-permission-common';
import { PermissionRule } from '@backstage/plugin-permission-node';
import { PermissionRuleParams } from '@backstage/plugin-permission-common';
import { PlaceholderResolver } from '@backstage/plugin-catalog-node';
@@ -43,6 +44,8 @@ export interface CatalogPermissionExtensionPoint {
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
>
): void;
// (undocumented)
addPermissions(...permissions: Array<Permission | Array<Permission>>): void;
}
// @alpha (undocumented)
+5 -1
View File
@@ -24,7 +24,10 @@ import {
PlaceholderResolver,
ScmLocationAnalyzer,
} from '@backstage/plugin-catalog-node';
import { PermissionRuleParams } from '@backstage/plugin-permission-common';
import {
Permission,
PermissionRuleParams,
} from '@backstage/plugin-permission-common';
import { PermissionRule } from '@backstage/plugin-permission-node';
/**
@@ -104,6 +107,7 @@ export type CatalogPermissionRuleInput<
* @alpha
*/
export interface CatalogPermissionExtensionPoint {
addPermissions(...permissions: Array<Permission | Array<Permission>>): void;
addPermissionRules(
...rules: Array<
CatalogPermissionRuleInput | Array<CatalogPermissionRuleInput>
@@ -29,6 +29,7 @@ import {
BackstageCredentials,
DiscoveryService,
} from '@backstage/backend-plugin-api';
import { ResponseError } from '@backstage/errors';
const responseSchema = z.object({
items: z.array(
@@ -90,9 +91,7 @@ export class PermissionIntegrationClient {
});
if (!response.ok) {
throw new Error(
`Unexpected response from plugin upstream when applying conditions. Expected 200 but got ${response.status} - ${response.statusText}`,
);
throw await ResponseError.fromResponse(response);
}
const result = responseSchema.parse(await response.json());
@@ -162,7 +162,6 @@ const applyConditions = <TResourceType extends string, TResource>(
};
/**
* Takes some permission conditions and returns a definitive authorization result
* on the resource to which they apply.
*
+1
View File
@@ -5631,6 +5631,7 @@ __metadata:
"@backstage/cli": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/plugin-auth-node": "workspace:^"
"@backstage/plugin-catalog-node": "workspace:^"
"@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^"
"@backstage/plugin-permission-common": "workspace:^"
"@backstage/plugin-permission-node": "workspace:^"