catalog-backend: update permission integration to support batching

Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
This commit is contained in:
MT Lewis
2022-01-13 13:04:47 +00:00
parent 3bb0afb54c
commit 74967cf50d
2 changed files with 107 additions and 10 deletions
@@ -25,6 +25,7 @@ import {
NoForeignRootFieldsEntityPolicy,
parseEntityRef,
SchemaValidEntityPolicy,
stringifyEntityRef,
Validators,
} from '@backstage/catalog-model';
import {
@@ -34,7 +35,7 @@ import {
} from '@backstage/integration';
import { createHash } from 'crypto';
import { Router } from 'express';
import lodash from 'lodash';
import lodash, { keyBy } from 'lodash';
import { EntitiesCatalog, EntitiesSearchFilter } from '../catalog';
import {
DatabaseLocationsCatalog,
@@ -415,18 +416,27 @@ export class NextCatalogBuilder {
);
const permissionIntegrationRouter = createPermissionIntegrationRouter({
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
getResource: async (resourceRef: string) => {
const parsed = parseEntityRef(resourceRef);
getResources: async (resourceRefs: string[]) => {
const { entities } = await entitiesCatalog.entities({
filter: {
anyOf: resourceRefs.map(resourceRef => {
const { kind, namespace, name } = parseEntityRef(resourceRef);
const { entities } = await unauthorizedEntitiesCatalog.entities({
filter: basicEntityFilter({
kind: parsed.kind,
'metadata.namespace': parsed.namespace,
'metadata.name': parsed.name,
}),
return basicEntityFilter({
kind,
'metadata.namespace': namespace,
'metadata.name': name,
});
}),
},
});
return entities[0];
const entitiesByRef = keyBy(entities, stringifyEntityRef);
return resourceRefs.map(
resourceRef =>
entitiesByRef[stringifyEntityRef(parseEntityRef(resourceRef))],
);
},
rules: this.permissionRules,
});
@@ -24,6 +24,9 @@ import { EntitiesCatalog } from '../catalog';
import { LocationService, RefreshService } from './types';
import { basicEntityFilter } from './request';
import { createNextRouter } from './NextRouter';
import { AuthorizeResult } from '@backstage/plugin-permission-common';
import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';
import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common';
describe('createNextRouter readonly disabled', () => {
let entitiesCatalog: jest.Mocked<EntitiesCatalog>;
@@ -433,3 +436,87 @@ describe('createNextRouter readonly enabled', () => {
});
});
});
describe('NextRouter permissioning', () => {
let entitiesCatalog: jest.Mocked<EntitiesCatalog>;
let locationService: jest.Mocked<LocationService>;
let app: express.Express;
let refreshService: RefreshService;
const fakeRule = {
name: 'FAKE_RULE',
description: 'fake rule',
apply: () => true,
toQuery: () => ({ key: '', values: [] }),
};
beforeAll(async () => {
entitiesCatalog = {
entities: jest.fn(),
removeEntityByUid: jest.fn(),
batchAddOrUpdateEntities: jest.fn(),
entityAncestry: jest.fn(),
};
locationService = {
getLocation: jest.fn(),
createLocation: jest.fn(),
listLocations: jest.fn(),
deleteLocation: jest.fn(),
};
refreshService = { refresh: jest.fn() };
const router = await createNextRouter({
entitiesCatalog,
locationService,
logger: getVoidLogger(),
refreshService,
config: new ConfigReader(undefined),
permissionIntegrationRouter: createPermissionIntegrationRouter({
resourceType: RESOURCE_TYPE_CATALOG_ENTITY,
rules: [fakeRule],
getResources: jest.fn((resourceRefs: string[]) =>
Promise.resolve(
resourceRefs.map(resourceRef => ({ id: resourceRef })),
),
),
}),
});
app = express().use(router);
});
afterEach(() => {
jest.resetAllMocks();
});
it('accepts and evaluates conditions at the apply-conditions endpoint', async () => {
const spideySense: Entity = {
apiVersion: 'a',
kind: 'component',
metadata: {
name: 'spidey-sense',
},
};
entitiesCatalog.entities.mockResolvedValueOnce({
entities: [spideySense],
pageInfo: { hasNextPage: false },
});
const requestBody = {
items: [
{
id: '123',
resourceType: 'catalog-entity',
resourceRef: 'component:default/spidey-sense',
conditions: { rule: 'FAKE_RULE', params: ['user:default/spiderman'] },
},
],
};
const response = await request(app)
.post('/.well-known/backstage/permissions/apply-conditions')
.send(requestBody);
expect(response.status).toBe(200);
expect(response.body).toEqual({
items: [{ id: '123', result: AuthorizeResult.ALLOW }],
});
});
});