Complete code in router and fix tests
Signed-off-by: Ainhoa Larumbe <ainhoaL@users.noreply.github.com> Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com>
This commit is contained in:
@@ -34,6 +34,11 @@ const testPermission: Permission = createPermission({
|
||||
attributes: {},
|
||||
});
|
||||
|
||||
const testPermission2: Permission = createPermission({
|
||||
name: 'test.permission2',
|
||||
attributes: {},
|
||||
});
|
||||
|
||||
const mockTestRule1Apply = jest
|
||||
.fn()
|
||||
.mockImplementation((_resource: any, _params) => true);
|
||||
@@ -60,6 +65,17 @@ const testRule2 = createPermissionRule({
|
||||
toQuery: () => ({}),
|
||||
});
|
||||
|
||||
const mockTestRule3Apply = jest
|
||||
.fn()
|
||||
.mockImplementation((_resource: any) => false);
|
||||
const testRule3 = createPermissionRule({
|
||||
name: 'test-rule-3',
|
||||
description: 'Test rule 3',
|
||||
resourceType: 'test-resource-2',
|
||||
apply: mockTestRule2Apply,
|
||||
toQuery: () => ({}),
|
||||
});
|
||||
|
||||
const defaultMockedGetResources: CreatePermissionIntegrationRouterResourceOptions<
|
||||
string,
|
||||
{ id: string }
|
||||
@@ -69,8 +85,7 @@ const defaultMockedGetResources: CreatePermissionIntegrationRouterResourceOption
|
||||
|
||||
const createApp = (
|
||||
mockedGetResources:
|
||||
| typeof defaultMockedGetResources
|
||||
| null = defaultMockedGetResources,
|
||||
| typeof defaultMockedGetResources = defaultMockedGetResources,
|
||||
) => {
|
||||
const router = mockedGetResources
|
||||
? createPermissionIntegrationRouter({
|
||||
@@ -84,6 +99,16 @@ const createApp = (
|
||||
return express().use(router);
|
||||
};
|
||||
|
||||
const createAppWithResources = (
|
||||
resourceOptions: CreatePermissionIntegrationRouterResourceOptions<
|
||||
string,
|
||||
any
|
||||
>,
|
||||
) => {
|
||||
const router = createPermissionIntegrationRouter(resourceOptions);
|
||||
return express().use(router);
|
||||
};
|
||||
|
||||
describe('createPermissionIntegrationRouter', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -573,15 +598,35 @@ describe('createPermissionIntegrationRouter', () => {
|
||||
});
|
||||
|
||||
it('returns 501 with no getResources implementation', async () => {
|
||||
const response = await request(createApp(null))
|
||||
const response = await request(
|
||||
createAppWithResources({
|
||||
resourceType: 'test-resource',
|
||||
permissions: [testPermission],
|
||||
rules: [testRule1, testRule2],
|
||||
}),
|
||||
)
|
||||
.post('/.well-known/backstage/permissions/apply-conditions')
|
||||
.send({
|
||||
items: [],
|
||||
items: [
|
||||
{
|
||||
id: '345',
|
||||
resourceRef: 'default:test/resource-2',
|
||||
resourceType: 'test-resource',
|
||||
conditions: {
|
||||
rule: 'test-rule-1',
|
||||
resourceType: 'test-resource',
|
||||
params: {
|
||||
foo: 'a',
|
||||
bar: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(501);
|
||||
expect(response.body.error.message).toEqual(
|
||||
`This plugin does not expose any permission rule or can't evaluate conditional decisions`,
|
||||
`This plugin does not expose any permission rule or can't evaluate the conditions request for test-resource`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -630,6 +675,73 @@ describe('createPermissionIntegrationRouter', () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
it.skip('returns a list of permissions and rules used by a given backend that was created with an array of resource options', async () => {
|
||||
const mockedResourceOptions = [
|
||||
{
|
||||
resourceType: 'test-resource',
|
||||
permissions: [testPermission],
|
||||
rules: [testRule1, testRule2],
|
||||
},
|
||||
{
|
||||
resourceType: 'test-resource-2',
|
||||
permissions: [testPermission2],
|
||||
rules: [testRule3],
|
||||
},
|
||||
];
|
||||
|
||||
const response = await request(
|
||||
createApp({ resourceOptions: mockedResourceOptions }),
|
||||
).get('/.well-known/backstage/permissions/metadata');
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
permissions: [testPermission, testPermission2],
|
||||
rules: [
|
||||
{
|
||||
name: testRule1.name,
|
||||
description: testRule1.description,
|
||||
resourceType: testRule1.resourceType,
|
||||
paramsSchema: {
|
||||
$schema: 'http://json-schema.org/draft-07/schema#',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
foo: {
|
||||
type: 'string',
|
||||
},
|
||||
bar: {
|
||||
description: 'bar',
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
required: ['foo', 'bar'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: testRule2.name,
|
||||
description: testRule2.description,
|
||||
resourceType: testRule2.resourceType,
|
||||
paramsSchema: {
|
||||
$schema: 'http://json-schema.org/draft-07/schema#',
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: testRule3.name,
|
||||
description: testRule3.description,
|
||||
resourceType: testRule3.resourceType,
|
||||
paramsSchema: {
|
||||
$schema: 'http://json-schema.org/draft-07/schema#',
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -346,12 +346,7 @@ export function createPermissionIntegrationRouter<
|
||||
options:
|
||||
| { permissions: Array<Permission> }
|
||||
| CreatePermissionIntegrationRouterResourceOptions<TResourceType, TResource>
|
||||
| Array<
|
||||
CreatePermissionIntegrationRouterResourceOptions<
|
||||
TResourceType,
|
||||
TResource
|
||||
>
|
||||
>,
|
||||
| Array<CreatePermissionIntegrationRouterResourceOptions<string, any>>,
|
||||
): express.Router {
|
||||
const allOptions = [options].flat();
|
||||
const allRules = allOptions.flatMap(
|
||||
@@ -366,6 +361,12 @@ export function createPermissionIntegrationRouter<
|
||||
const allPermissions = allOptions
|
||||
.flatMap(option => option.permissions)
|
||||
.filter((p): p is Permission => !!p);
|
||||
const allResourceTypes = allOptions.reduce((acc, option) => {
|
||||
if (isCreatePermissionIntegrationRouterResourceOptions(option)) {
|
||||
acc.push(option.resourceType);
|
||||
}
|
||||
return acc;
|
||||
}, [] as string[]);
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
@@ -391,15 +392,6 @@ export function createPermissionIntegrationRouter<
|
||||
router.post(
|
||||
'/.well-known/backstage/permissions/apply-conditions',
|
||||
async (req, res: Response<ApplyConditionsResponse | string>) => {
|
||||
// if (
|
||||
// !isCreatePermissionIntegrationRouterResourceOptions(options) ||
|
||||
// options.getResources === undefined
|
||||
// ) {
|
||||
// throw new NotImplementedError(
|
||||
// `This plugin does not expose any permission rule or can't evaluate conditional decisions`,
|
||||
// );
|
||||
// }
|
||||
|
||||
const ruleMapByResourceType: Record<
|
||||
string,
|
||||
ReturnType<typeof createGetRule>
|
||||
@@ -422,15 +414,11 @@ export function createPermissionIntegrationRouter<
|
||||
}
|
||||
}
|
||||
|
||||
// const { resourceType, getResources } = options;
|
||||
|
||||
// const getRule = createGetRule(rules);
|
||||
|
||||
const assertValidResourceTypes = (
|
||||
requests: ApplyConditionsRequestEntry[],
|
||||
) => {
|
||||
const invalidResourceTypes = requests
|
||||
.filter(request => request.resourceType !== resourceType)
|
||||
.filter(request => !allResourceTypes.includes(request.resourceType))
|
||||
.map(request => request.resourceType);
|
||||
|
||||
if (invalidResourceTypes.length) {
|
||||
@@ -461,11 +449,9 @@ export function createPermissionIntegrationRouter<
|
||||
}, {});
|
||||
|
||||
const resourcesByResourceType: Record<string, Record<string, any>> = {};
|
||||
Object.keys(resourceRefsByResourceType).forEach(async resourceType => {
|
||||
if (
|
||||
!getResourcesByResourceType ||
|
||||
!getResourcesByResourceType[resourceType]
|
||||
) {
|
||||
for (const resourceType of Object.keys(resourceRefsByResourceType)) {
|
||||
const getResources = getResourcesByResourceType[resourceType];
|
||||
if (!getResources) {
|
||||
throw new NotImplementedError(
|
||||
`This plugin does not expose any permission rule or can't evaluate the conditions request for ${resourceType}`,
|
||||
);
|
||||
@@ -473,30 +459,22 @@ export function createPermissionIntegrationRouter<
|
||||
const resourceRefs = Array.from(
|
||||
resourceRefsByResourceType[resourceType],
|
||||
);
|
||||
const resources = await getResourcesByResourceType[resourceType](
|
||||
resourceRefs,
|
||||
);
|
||||
const resources = await getResources(resourceRefs);
|
||||
resourceRefs.forEach((resourceRef, index) => {
|
||||
if (!resourcesByResourceType[resourceType]) {
|
||||
resourcesByResourceType[resourceType] = {};
|
||||
}
|
||||
resourcesByResourceType[resourceType][resourceRef] = resources[index];
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
const resourceArray = await getResources(resourceRefs);
|
||||
const resources = resourceRefs.reduce((acc, resourceRef, index) => {
|
||||
acc[resourceRef] = resourceArray[index];
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, TResource | undefined>);
|
||||
*/
|
||||
}
|
||||
|
||||
return res.json({
|
||||
items: body.items.map(request => ({
|
||||
id: request.id,
|
||||
result: applyConditions(
|
||||
request.conditions,
|
||||
resources[request.resourceRef],
|
||||
getRule,
|
||||
resourcesByResourceType[request.resourceType][request.resourceRef],
|
||||
ruleMapByResourceType[request.resourceType],
|
||||
)
|
||||
? AuthorizeResult.ALLOW
|
||||
: AuthorizeResult.DENY,
|
||||
@@ -516,12 +494,9 @@ function isCreatePermissionIntegrationRouterResourceOptions<
|
||||
>(
|
||||
options:
|
||||
| { permissions: Array<Permission> }
|
||||
| CreatePermissionIntegrationRouterResourceOptions<TResourceType, TResource>
|
||||
| Array<
|
||||
CreatePermissionIntegrationRouterResourceOptions<
|
||||
TResourceType,
|
||||
TResource
|
||||
>
|
||||
| CreatePermissionIntegrationRouterResourceOptions<
|
||||
TResourceType,
|
||||
TResource
|
||||
>,
|
||||
): options is CreatePermissionIntegrationRouterResourceOptions<
|
||||
TResourceType,
|
||||
|
||||
Reference in New Issue
Block a user