Merge pull request #25924 from 04kash/add-catalog-permissions
Add Missing Catalog permissions for `/analyze-location` and `/validate-entity` endpoints
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
'@backstage/plugin-catalog-common': minor
|
||||
'@backstage/plugin-catalog-node': minor
|
||||
---
|
||||
|
||||
The `analyze-location` endpoint is now protected by the `catalog.location.analyze` permission.
|
||||
The `validate-entity` endpoint is now protected by the `catalog.entity.validate` permission.
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2021 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 { NotAllowedError } from '@backstage/errors';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { AuthorizedLocationAnalyzer } from './AuthorizedLocationAnalyzer';
|
||||
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
|
||||
import { mockCredentials } from '@backstage/backend-test-utils';
|
||||
|
||||
describe('AuthorizedLocationAnalyzer', () => {
|
||||
const locationAnalyzerService = {
|
||||
analyzeLocation: jest.fn(),
|
||||
};
|
||||
const permissionApi = {
|
||||
authorize: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('throws Authorization Error on deny', async () => {
|
||||
permissionApi.authorize.mockResolvedValueOnce([
|
||||
{
|
||||
result: AuthorizeResult.DENY,
|
||||
},
|
||||
]);
|
||||
const authorizedService = new AuthorizedLocationAnalyzer(
|
||||
locationAnalyzerService,
|
||||
permissionApi as unknown as ServerPermissionClient,
|
||||
);
|
||||
await expect(() =>
|
||||
authorizedService.analyzeLocation(
|
||||
{
|
||||
location: {
|
||||
type: 'url',
|
||||
target: 'https://example.com/path/to/your/catalog-info.yaml',
|
||||
presence: 'required',
|
||||
},
|
||||
catalogFilename: 'catalog-info.yaml',
|
||||
},
|
||||
mockCredentials.none(),
|
||||
),
|
||||
).rejects.toThrow(NotAllowedError);
|
||||
});
|
||||
|
||||
it('calls analyzeLocation on allow', async () => {
|
||||
permissionApi.authorize.mockResolvedValueOnce([
|
||||
{
|
||||
result: AuthorizeResult.ALLOW,
|
||||
},
|
||||
]);
|
||||
const authorizedService = new AuthorizedLocationAnalyzer(
|
||||
locationAnalyzerService,
|
||||
permissionApi as unknown as ServerPermissionClient,
|
||||
);
|
||||
const analyzeLocationRequest = {
|
||||
location: {
|
||||
type: 'url',
|
||||
target: 'https://example.com/path/to/your/catalog-info.yaml',
|
||||
},
|
||||
catalogFilename: 'catalog-info.yaml',
|
||||
};
|
||||
await authorizedService.analyzeLocation(
|
||||
analyzeLocationRequest,
|
||||
mockCredentials.none(),
|
||||
);
|
||||
expect(locationAnalyzerService.analyzeLocation).toHaveBeenCalledWith(
|
||||
analyzeLocationRequest,
|
||||
mockCredentials.none(),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2021 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 { NotAllowedError } from '@backstage/errors';
|
||||
import { catalogLocationAnalyzePermission } from '@backstage/plugin-catalog-common/alpha';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
BackstageCredentials,
|
||||
PermissionsService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { LocationAnalyzer } from '@backstage/plugin-catalog-node';
|
||||
import { AnalyzeLocationRequest } from '@backstage/plugin-catalog-common';
|
||||
import { AnalyzeLocationResponse } from '@backstage/plugin-catalog-common';
|
||||
|
||||
export class AuthorizedLocationAnalyzer implements LocationAnalyzer {
|
||||
constructor(
|
||||
private readonly service: LocationAnalyzer,
|
||||
private readonly permissionApi: PermissionsService,
|
||||
) {}
|
||||
|
||||
async analyzeLocation(
|
||||
request: AnalyzeLocationRequest,
|
||||
credentials: BackstageCredentials,
|
||||
): Promise<AnalyzeLocationResponse> {
|
||||
const authorizeDecision = (
|
||||
await this.permissionApi.authorize(
|
||||
[
|
||||
{
|
||||
permission: catalogLocationAnalyzePermission,
|
||||
},
|
||||
],
|
||||
{ credentials: credentials },
|
||||
)
|
||||
)[0];
|
||||
if (authorizeDecision.result !== AuthorizeResult.ALLOW) {
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
return this.service.analyzeLocation(request, credentials);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2021 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 { NotAllowedError } from '@backstage/errors';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
|
||||
import { mockCredentials } from '@backstage/backend-test-utils';
|
||||
import { AuthorizedValidationService } from './AuthorizedValidationService';
|
||||
|
||||
describe('AuthorizedValidationService', () => {
|
||||
const orchestratorService = {
|
||||
process: jest.fn(),
|
||||
};
|
||||
const permissionApi = {
|
||||
authorize: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('throws Authorization Error on deny', async () => {
|
||||
permissionApi.authorize.mockResolvedValueOnce([
|
||||
{
|
||||
result: AuthorizeResult.DENY,
|
||||
},
|
||||
]);
|
||||
const authorizedService = new AuthorizedValidationService(
|
||||
orchestratorService,
|
||||
permissionApi as unknown as ServerPermissionClient,
|
||||
);
|
||||
const entityProcessingRequest = {
|
||||
entity: {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'your-entity-name',
|
||||
namespace: 'default',
|
||||
description: 'your-entity-description',
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
owner: 'team-a',
|
||||
},
|
||||
},
|
||||
};
|
||||
await expect(() =>
|
||||
authorizedService.process(
|
||||
entityProcessingRequest,
|
||||
mockCredentials.none(),
|
||||
),
|
||||
).rejects.toThrow(NotAllowedError);
|
||||
});
|
||||
|
||||
it('calls process on allow', async () => {
|
||||
permissionApi.authorize.mockResolvedValueOnce([
|
||||
{
|
||||
result: AuthorizeResult.ALLOW,
|
||||
},
|
||||
]);
|
||||
const authorizedService = new AuthorizedValidationService(
|
||||
orchestratorService,
|
||||
permissionApi as unknown as ServerPermissionClient,
|
||||
);
|
||||
const entityProcessingRequest = {
|
||||
entity: {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'your-entity-name',
|
||||
namespace: 'default',
|
||||
description: 'your-entity-description',
|
||||
},
|
||||
spec: {
|
||||
type: 'service',
|
||||
owner: 'team-a',
|
||||
},
|
||||
},
|
||||
};
|
||||
await authorizedService.process(
|
||||
entityProcessingRequest,
|
||||
mockCredentials.none(),
|
||||
);
|
||||
expect(orchestratorService.process).toHaveBeenCalledWith(
|
||||
entityProcessingRequest,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2021 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 { NotAllowedError } from '@backstage/errors';
|
||||
import { catalogEntityValidatePermission } from '@backstage/plugin-catalog-common/alpha';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
CatalogProcessingOrchestrator,
|
||||
EntityProcessingRequest,
|
||||
EntityProcessingResult,
|
||||
} from '../processing/types';
|
||||
import {
|
||||
BackstageCredentials,
|
||||
PermissionsService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
|
||||
export class AuthorizedValidationService {
|
||||
constructor(
|
||||
private readonly service: CatalogProcessingOrchestrator,
|
||||
private readonly permissionApi: PermissionsService,
|
||||
) {}
|
||||
|
||||
async process(
|
||||
request: EntityProcessingRequest,
|
||||
credentials: BackstageCredentials,
|
||||
): Promise<EntityProcessingResult> {
|
||||
const authorizeDecision = (
|
||||
await this.permissionApi.authorize(
|
||||
[
|
||||
{
|
||||
permission: catalogEntityValidatePermission,
|
||||
},
|
||||
],
|
||||
{ credentials },
|
||||
)
|
||||
)[0];
|
||||
|
||||
if (authorizeDecision.result !== AuthorizeResult.ALLOW) {
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
return this.service.process(request);
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ import {
|
||||
import { ConfigLocationEntityProvider } from '../modules/core/ConfigLocationEntityProvider';
|
||||
import { DefaultLocationStore } from '../modules/core/DefaultLocationStore';
|
||||
import { RepoLocationAnalyzer } from '../ingestion/LocationAnalyzer';
|
||||
import { AuthorizedLocationAnalyzer } from './AuthorizedLocationAnalyzer';
|
||||
import {
|
||||
jsonPlaceholderResolver,
|
||||
textPlaceholderResolver,
|
||||
@@ -515,15 +516,7 @@ export class CatalogBuilder {
|
||||
});
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
const rulesEnforcer = DefaultCatalogRulesEnforcer.fromConfig(config);
|
||||
const orchestrator = new DefaultCatalogProcessingOrchestrator({
|
||||
processors,
|
||||
integrations,
|
||||
rulesEnforcer,
|
||||
logger,
|
||||
parser,
|
||||
policy,
|
||||
legacySingleProcessorValidation: this.legacySingleProcessorValidation,
|
||||
});
|
||||
|
||||
const unauthorizedEntitiesCatalog = new DefaultEntitiesCatalog({
|
||||
database: dbClient,
|
||||
logger,
|
||||
@@ -540,6 +533,16 @@ export class CatalogBuilder {
|
||||
permissionsService = toPermissionEvaluator(permissions);
|
||||
}
|
||||
|
||||
const orchestrator = new DefaultCatalogProcessingOrchestrator({
|
||||
processors,
|
||||
integrations,
|
||||
rulesEnforcer,
|
||||
logger,
|
||||
parser,
|
||||
policy,
|
||||
legacySingleProcessorValidation: this.legacySingleProcessorValidation,
|
||||
});
|
||||
|
||||
const entitiesCatalog = new AuthorizedEntitiesCatalog(
|
||||
unauthorizedEntitiesCatalog,
|
||||
permissionsService,
|
||||
@@ -599,7 +602,10 @@ export class CatalogBuilder {
|
||||
|
||||
const locationAnalyzer =
|
||||
this.locationAnalyzer ??
|
||||
new RepoLocationAnalyzer(logger, integrations, this.locationAnalyzers);
|
||||
new AuthorizedLocationAnalyzer(
|
||||
new RepoLocationAnalyzer(logger, integrations, this.locationAnalyzers),
|
||||
permissionsService,
|
||||
);
|
||||
const locationService = new AuthorizedLocationService(
|
||||
new DefaultLocationService(locationStore, orchestrator, {
|
||||
allowedLocationTypes: this.allowedLocationType,
|
||||
@@ -622,6 +628,7 @@ export class CatalogBuilder {
|
||||
permissionIntegrationRouter,
|
||||
auth,
|
||||
httpAuth,
|
||||
permissionsService,
|
||||
});
|
||||
|
||||
await connectEntityProviders(providerDatabase, entityProviders);
|
||||
|
||||
@@ -42,6 +42,7 @@ import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils';
|
||||
import { Server } from 'http';
|
||||
import { mockCredentials, mockServices } from '@backstage/backend-test-utils';
|
||||
import { LocationAnalyzer } from '@backstage/plugin-catalog-node';
|
||||
import { PermissionsService } from '@backstage/backend-plugin-api';
|
||||
|
||||
describe('createRouter readonly disabled', () => {
|
||||
let entitiesCatalog: jest.Mocked<EntitiesCatalog>;
|
||||
@@ -50,6 +51,7 @@ describe('createRouter readonly disabled', () => {
|
||||
let app: express.Express | Server;
|
||||
let refreshService: RefreshService;
|
||||
let locationAnalyzer: jest.Mocked<LocationAnalyzer>;
|
||||
let permissionsService: jest.Mocked<PermissionsService>;
|
||||
|
||||
beforeAll(async () => {
|
||||
entitiesCatalog = {
|
||||
@@ -71,6 +73,11 @@ describe('createRouter readonly disabled', () => {
|
||||
locationAnalyzer = {
|
||||
analyzeLocation: jest.fn(),
|
||||
};
|
||||
permissionsService = {
|
||||
authorize: jest.fn(),
|
||||
authorizeConditional: jest.fn(),
|
||||
};
|
||||
|
||||
refreshService = { refresh: jest.fn() };
|
||||
orchestrator = { process: jest.fn() };
|
||||
const router = await createRouter({
|
||||
@@ -84,6 +91,7 @@ describe('createRouter readonly disabled', () => {
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
locationAnalyzer,
|
||||
permissionsService: permissionsService,
|
||||
});
|
||||
app = wrapInOpenApiTestServer(express().use(router));
|
||||
});
|
||||
@@ -725,6 +733,12 @@ describe('createRouter readonly disabled', () => {
|
||||
metadata: { name: 'n' },
|
||||
};
|
||||
|
||||
permissionsService.authorize.mockResolvedValueOnce([
|
||||
{
|
||||
result: AuthorizeResult.ALLOW,
|
||||
},
|
||||
]);
|
||||
|
||||
orchestrator.process.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
state: {},
|
||||
@@ -765,6 +779,12 @@ describe('createRouter readonly disabled', () => {
|
||||
metadata: { name: 'invalid*name' },
|
||||
};
|
||||
|
||||
permissionsService.authorize.mockResolvedValueOnce([
|
||||
{
|
||||
result: AuthorizeResult.ALLOW,
|
||||
},
|
||||
]);
|
||||
|
||||
orchestrator.process.mockResolvedValueOnce({
|
||||
ok: false,
|
||||
errors: [new Error('Invalid entity name')],
|
||||
@@ -802,6 +822,12 @@ describe('createRouter readonly disabled', () => {
|
||||
metadata: { name: 'n' },
|
||||
};
|
||||
|
||||
permissionsService.authorize.mockResolvedValueOnce([
|
||||
{
|
||||
result: AuthorizeResult.ALLOW,
|
||||
},
|
||||
]);
|
||||
|
||||
const response = await request(app)
|
||||
.post('/validate-entity')
|
||||
.send({ entity, location: null });
|
||||
@@ -848,6 +874,7 @@ describe('createRouter readonly enabled', () => {
|
||||
let entitiesCatalog: jest.Mocked<EntitiesCatalog>;
|
||||
let app: express.Express;
|
||||
let locationService: jest.Mocked<LocationService>;
|
||||
let permissionsService: jest.Mocked<PermissionsService>;
|
||||
|
||||
beforeAll(async () => {
|
||||
entitiesCatalog = {
|
||||
@@ -877,6 +904,7 @@ describe('createRouter readonly enabled', () => {
|
||||
permissionIntegrationRouter: express.Router(),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
permissionsService: permissionsService,
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
@@ -1046,6 +1074,7 @@ describe('NextRouter permissioning', () => {
|
||||
let locationService: jest.Mocked<LocationService>;
|
||||
let app: express.Express;
|
||||
let refreshService: RefreshService;
|
||||
let permissionsService: jest.Mocked<PermissionsService>;
|
||||
|
||||
const fakeRule = createPermissionRule({
|
||||
name: 'FAKE_RULE',
|
||||
@@ -1092,6 +1121,7 @@ describe('NextRouter permissioning', () => {
|
||||
}),
|
||||
auth: mockServices.auth(),
|
||||
httpAuth: mockServices.httpAuth(),
|
||||
permissionsService: permissionsService,
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
|
||||
@@ -53,8 +53,10 @@ import {
|
||||
HttpAuthService,
|
||||
LoggerService,
|
||||
SchedulerService,
|
||||
PermissionsService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { LocationAnalyzer } from '@backstage/plugin-catalog-node';
|
||||
import { AuthorizedValidationService } from './AuthorizedValidationService';
|
||||
|
||||
/**
|
||||
* Options used by {@link createRouter}.
|
||||
@@ -74,6 +76,7 @@ export interface RouterOptions {
|
||||
permissionIntegrationRouter?: express.Router;
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
permissionsService: PermissionsService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,6 +101,7 @@ export async function createRouter(
|
||||
config,
|
||||
logger,
|
||||
permissionIntegrationRouter,
|
||||
permissionsService,
|
||||
auth,
|
||||
httpAuth,
|
||||
} = options;
|
||||
@@ -301,9 +305,13 @@ export async function createRouter(
|
||||
location: locationInput,
|
||||
catalogFilename: z.string().optional(),
|
||||
});
|
||||
const credentials = await httpAuth.credentials(req);
|
||||
const parsedBody = schema.parse(body);
|
||||
try {
|
||||
const output = await locationAnalyzer.analyzeLocation(parsedBody);
|
||||
const output = await locationAnalyzer.analyzeLocation(
|
||||
parsedBody,
|
||||
credentials,
|
||||
);
|
||||
res.status(200).json(output);
|
||||
} catch (err) {
|
||||
if (
|
||||
@@ -342,19 +350,27 @@ export async function createRouter(
|
||||
});
|
||||
}
|
||||
|
||||
const processingResult = await orchestrator.process({
|
||||
entity: {
|
||||
...entity,
|
||||
metadata: {
|
||||
...entity.metadata,
|
||||
annotations: {
|
||||
[ANNOTATION_LOCATION]: body.location,
|
||||
[ANNOTATION_ORIGIN_LOCATION]: body.location,
|
||||
...entity.metadata.annotations,
|
||||
const credentials = await httpAuth.credentials(req);
|
||||
const authorizedValidationService = new AuthorizedValidationService(
|
||||
orchestrator,
|
||||
permissionsService,
|
||||
);
|
||||
const processingResult = await authorizedValidationService.process(
|
||||
{
|
||||
entity: {
|
||||
...entity,
|
||||
metadata: {
|
||||
...entity.metadata,
|
||||
annotations: {
|
||||
[ANNOTATION_LOCATION]: body.location,
|
||||
[ANNOTATION_ORIGIN_LOCATION]: body.location,
|
||||
...entity.metadata.annotations,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
credentials,
|
||||
);
|
||||
|
||||
if (!processingResult.ok)
|
||||
res.status(400).json({
|
||||
|
||||
@@ -23,6 +23,12 @@ export const catalogEntityReadPermission: ResourcePermission<'catalog-entity'>;
|
||||
// @alpha
|
||||
export const catalogEntityRefreshPermission: ResourcePermission<'catalog-entity'>;
|
||||
|
||||
// @alpha
|
||||
export const catalogEntityValidatePermission: BasicPermission;
|
||||
|
||||
// @alpha
|
||||
export const catalogLocationAnalyzePermission: BasicPermission;
|
||||
|
||||
// @alpha
|
||||
export const catalogLocationCreatePermission: BasicPermission;
|
||||
|
||||
|
||||
@@ -20,9 +20,11 @@ export {
|
||||
catalogEntityCreatePermission,
|
||||
catalogEntityDeletePermission,
|
||||
catalogEntityRefreshPermission,
|
||||
catalogEntityValidatePermission,
|
||||
catalogLocationReadPermission,
|
||||
catalogLocationCreatePermission,
|
||||
catalogLocationDeletePermission,
|
||||
catalogLocationAnalyzePermission,
|
||||
catalogPermissions,
|
||||
} from './permissions';
|
||||
export type { CatalogEntityPermission } from './permissions';
|
||||
|
||||
@@ -91,6 +91,15 @@ export const catalogEntityRefreshPermission = createPermission({
|
||||
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
|
||||
});
|
||||
|
||||
/**
|
||||
* This permission is used to authorize validating catalog entities.
|
||||
* @alpha
|
||||
*/
|
||||
export const catalogEntityValidatePermission = createPermission({
|
||||
name: 'catalog.entity.validate',
|
||||
attributes: {},
|
||||
});
|
||||
|
||||
/**
|
||||
* This permission is used to designate actions that involve reading one or more
|
||||
* locations from the catalog.
|
||||
@@ -118,6 +127,15 @@ export const catalogLocationCreatePermission = createPermission({
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* This permission is used to authorize analyzing catalog locations.
|
||||
* @alpha
|
||||
*/
|
||||
export const catalogLocationAnalyzePermission = createPermission({
|
||||
name: 'catalog.location.analyze',
|
||||
attributes: {},
|
||||
});
|
||||
|
||||
/**
|
||||
* This permission is used to designate actions that involve deleting locations
|
||||
* from the catalog.
|
||||
@@ -139,7 +157,9 @@ export const catalogPermissions = [
|
||||
catalogEntityCreatePermission,
|
||||
catalogEntityDeletePermission,
|
||||
catalogEntityRefreshPermission,
|
||||
catalogEntityValidatePermission,
|
||||
catalogLocationReadPermission,
|
||||
catalogLocationCreatePermission,
|
||||
catalogLocationDeletePermission,
|
||||
catalogLocationAnalyzePermission,
|
||||
];
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import { AnalyzeLocationExistingEntity } from '@backstage/plugin-catalog-common';
|
||||
import { AnalyzeLocationRequest } from '@backstage/plugin-catalog-common';
|
||||
import { AnalyzeLocationResponse } from '@backstage/plugin-catalog-common';
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
@@ -172,6 +173,7 @@ export type EntityRelationSpec = {
|
||||
export type LocationAnalyzer = {
|
||||
analyzeLocation(
|
||||
location: AnalyzeLocationRequest,
|
||||
credentials: BackstageCredentials,
|
||||
): Promise<AnalyzeLocationResponse>;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from '@backstage/plugin-catalog-common';
|
||||
import { JsonValue } from '@backstage/types';
|
||||
import { CatalogProcessorEmit } from '../api';
|
||||
import { BackstageCredentials } from '@backstage/backend-plugin-api';
|
||||
|
||||
/**
|
||||
* Entities that are not yet processed.
|
||||
@@ -66,6 +67,7 @@ export type LocationAnalyzer = {
|
||||
*/
|
||||
analyzeLocation(
|
||||
location: AnalyzeLocationRequest,
|
||||
credentials: BackstageCredentials,
|
||||
): Promise<AnalyzeLocationResponse>;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user