diff --git a/plugins/catalog-backend/src/service/AuthorizedCatalogProcessingOrchestrator.test.ts b/plugins/catalog-backend/src/service/AuthorizedCatalogProcessingOrchestrator.test.ts new file mode 100644 index 0000000000..72572b68e7 --- /dev/null +++ b/plugins/catalog-backend/src/service/AuthorizedCatalogProcessingOrchestrator.test.ts @@ -0,0 +1,97 @@ +/* + * 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 { AuthorizedCatalogProcessingOrchestrator } from './AuthorizedCatalogProcessingOrchestrator'; + +describe('AuthorizedCatalogProcessingOrchestrator', () => { + 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 AuthorizedCatalogProcessingOrchestrator( + 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', + }, + }, + credentials: mockCredentials.none(), + }; + await expect(() => + authorizedService.process(entityProcessingRequest), + ).rejects.toThrow(NotAllowedError); + }); + + it('calls process on allow', async () => { + permissionApi.authorize.mockResolvedValueOnce([ + { + result: AuthorizeResult.ALLOW, + }, + ]); + const authorizedService = new AuthorizedCatalogProcessingOrchestrator( + 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', + }, + }, + credentials: mockCredentials.none(), + }; + await authorizedService.process(entityProcessingRequest); + expect(orchestratorService.process).toHaveBeenCalledWith( + entityProcessingRequest, + ); + }); +}); diff --git a/plugins/catalog-backend/src/service/AuthorizedLocationAnalyzer.test.ts b/plugins/catalog-backend/src/service/AuthorizedLocationAnalyzer.test.ts new file mode 100644 index 0000000000..1583049080 --- /dev/null +++ b/plugins/catalog-backend/src/service/AuthorizedLocationAnalyzer.test.ts @@ -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(), + ); + }); +});