permission-node: wrap request and response arrays in object

Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
This commit is contained in:
MT Lewis
2022-01-06 12:18:01 +00:00
parent ef291ff985
commit 1fb2e0e0b4
5 changed files with 196 additions and 151 deletions
@@ -96,22 +96,26 @@ describe('createPermissionIntegrationRouter', () => {
])('returns 200/ALLOW when criteria match (case %#)', async conditions => {
const response = await request(app)
.post('/.well-known/backstage/permissions/apply-conditions')
.send([
{
id: '123',
resourceRef: 'default:test/resource',
resourceType: 'test-resource',
conditions,
},
]);
.send({
items: [
{
id: '123',
resourceRef: 'default:test/resource',
resourceType: 'test-resource',
conditions,
},
],
});
expect(response.status).toEqual(200);
expect(response.body).toEqual([
{
id: '123',
result: AuthorizeResult.ALLOW,
},
]);
expect(response.body).toEqual({
items: [
{
id: '123',
result: AuthorizeResult.ALLOW,
},
],
});
});
it.each([
@@ -145,19 +149,21 @@ describe('createPermissionIntegrationRouter', () => {
async conditions => {
const response = await request(app)
.post('/.well-known/backstage/permissions/apply-conditions')
.send([
{
id: '123',
resourceRef: 'default:test/resource',
resourceType: 'test-resource',
conditions,
},
]);
.send({
items: [
{
id: '123',
resourceRef: 'default:test/resource',
resourceType: 'test-resource',
conditions,
},
],
});
expect(response.status).toEqual(200);
expect(response.body).toEqual([
{ id: '123', result: AuthorizeResult.DENY },
]);
expect(response.body).toEqual({
items: [{ id: '123', result: AuthorizeResult.DENY }],
});
},
);
@@ -167,54 +173,58 @@ describe('createPermissionIntegrationRouter', () => {
beforeEach(async () => {
response = await request(app)
.post('/.well-known/backstage/permissions/apply-conditions')
.send([
{
id: '123',
resourceRef: 'default:test/resource-1',
resourceType: 'test-resource',
conditions: { rule: 'test-rule-1', params: [] },
},
{
id: '234',
resourceRef: 'default:test/resource-1',
resourceType: 'test-resource',
conditions: { rule: 'test-rule-2', params: [] },
},
{
id: '345',
resourceRef: 'default:test/resource-2',
resourceType: 'test-resource',
conditions: { not: { rule: 'test-rule-1', params: [] } },
},
{
id: '456',
resourceRef: 'default:test/resource-3',
resourceType: 'test-resource',
conditions: { not: { rule: 'test-rule-2', params: [] } },
},
{
id: '567',
resourceRef: 'default:test/resource-4',
resourceType: 'test-resource',
conditions: {
anyOf: [
{ rule: 'test-rule-1', params: [] },
{ rule: 'test-rule-2', params: [] },
],
.send({
items: [
{
id: '123',
resourceRef: 'default:test/resource-1',
resourceType: 'test-resource',
conditions: { rule: 'test-rule-1', params: [] },
},
},
]);
{
id: '234',
resourceRef: 'default:test/resource-1',
resourceType: 'test-resource',
conditions: { rule: 'test-rule-2', params: [] },
},
{
id: '345',
resourceRef: 'default:test/resource-2',
resourceType: 'test-resource',
conditions: { not: { rule: 'test-rule-1', params: [] } },
},
{
id: '456',
resourceRef: 'default:test/resource-3',
resourceType: 'test-resource',
conditions: { not: { rule: 'test-rule-2', params: [] } },
},
{
id: '567',
resourceRef: 'default:test/resource-4',
resourceType: 'test-resource',
conditions: {
anyOf: [
{ rule: 'test-rule-1', params: [] },
{ rule: 'test-rule-2', params: [] },
],
},
},
],
});
});
it('processes batched requests', () => {
expect(response.status).toEqual(200);
expect(response.body).toEqual([
{ id: '123', result: AuthorizeResult.ALLOW },
{ id: '234', result: AuthorizeResult.DENY },
{ id: '345', result: AuthorizeResult.DENY },
{ id: '456', result: AuthorizeResult.ALLOW },
{ id: '567', result: AuthorizeResult.ALLOW },
]);
expect(response.body).toEqual({
items: [
{ id: '123', result: AuthorizeResult.ALLOW },
{ id: '234', result: AuthorizeResult.DENY },
{ id: '345', result: AuthorizeResult.DENY },
{ id: '456', result: AuthorizeResult.ALLOW },
{ id: '567', result: AuthorizeResult.ALLOW },
],
});
});
it('calls getResources for all required resources at once', () => {
@@ -230,16 +240,18 @@ describe('createPermissionIntegrationRouter', () => {
it('returns 400 when called with incorrect resource type', async () => {
const response = await request(app)
.post('/.well-known/backstage/permissions/apply-conditions')
.send([
{
id: '123',
resourceRef: 'default:test/resource',
resourceType: 'test-incorrect-resource',
conditions: {
anyOf: [],
.send({
items: [
{
id: '123',
resourceRef: 'default:test/resource',
resourceType: 'test-incorrect-resource',
conditions: {
anyOf: [],
},
},
},
]);
],
});
expect(response.status).toEqual(400);
expect(response.error && response.error.text).toMatch(
@@ -254,22 +266,26 @@ describe('createPermissionIntegrationRouter', () => {
const response = await request(app)
.post('/.well-known/backstage/permissions/apply-conditions')
.send([
{
id: '123',
resourceRef: 'default:test/resource',
resourceType: 'test-resource',
conditions: { rule: 'testRule1', params: [] },
},
]);
.send({
items: [
{
id: '123',
resourceRef: 'default:test/resource',
resourceType: 'test-resource',
conditions: { rule: 'test-rule-1', params: [] },
},
],
});
expect(response.status).toEqual(200);
expect(response.body).toEqual([
{
id: '123',
result: AuthorizeResult.DENY,
},
]);
expect(response.body).toEqual({
items: [
{
id: '123',
result: AuthorizeResult.DENY,
},
],
});
});
it.each([
@@ -278,14 +294,17 @@ describe('createPermissionIntegrationRouter', () => {
{},
{ resourceType: 'test-resource-type' },
[{ resourceType: 'test-resource-type' }],
[{ resourceRef: 'test/resource-ref' }],
[
{
resourceType: 'test-resource-type',
resourceRef: 'test/resource-ref',
},
],
[{ conditions: { anyOf: [] } }],
{ items: [{ resourceType: 'test-resource-type' }] },
{ items: [{ resourceRef: 'test/resource-ref' }] },
{
items: [
{
resourceType: 'test-resource-type',
resourceRef: 'test/resource-ref',
},
],
},
{ items: [{ conditions: { anyOf: [] } }] },
])(`returns 400 for invalid input %#`, async input => {
const response = await request(app)
.post('/.well-known/backstage/permissions/apply-conditions')
@@ -48,14 +48,16 @@ const permissionCriteriaSchema: z.ZodSchema<
]),
);
const applyConditionsRequestSchema = z.array(
z.object({
id: z.string(),
resourceRef: z.string(),
resourceType: z.string(),
conditions: permissionCriteriaSchema,
}),
);
const applyConditionsRequestSchema = z.object({
items: z.array(
z.object({
id: z.string(),
resourceRef: z.string(),
resourceType: z.string(),
conditions: permissionCriteriaSchema,
}),
),
});
/**
* A request to load the referenced resource and apply conditions in order to
@@ -74,7 +76,9 @@ export type ApplyConditionsRequestEntry = Identified<{
*
* @public
*/
export type ApplyConditionsRequest = ApplyConditionsRequestEntry[];
export type ApplyConditionsRequest = {
items: ApplyConditionsRequestEntry[];
};
/**
* The result of applying the conditions, expressed as a definitive authorize
@@ -89,7 +93,9 @@ export type ApplyConditionsResponseEntry = Identified<DefinitivePolicyDecision>;
*
* @public
*/
export type ApplyConditionsResponse = ApplyConditionsResponseEntry[];
export type ApplyConditionsResponse = {
items: ApplyConditionsResponseEntry[];
};
const applyConditions = <TResource>(
criteria: PermissionCriteria<PermissionCondition>,
@@ -165,7 +171,9 @@ export const createPermissionIntegrationRouter = <TResource>(options: {
const getRule = createGetRule(rules);
const assertValidResourceTypes = (requests: ApplyConditionsRequest) => {
const assertValidResourceTypes = (
requests: ApplyConditionsRequestEntry[],
) => {
const invalidResourceType = requests.find(
request => request.resourceType !== resourceType,
)?.resourceType;
@@ -188,10 +196,10 @@ export const createPermissionIntegrationRouter = <TResource>(options: {
const body = parseResult.data;
assertValidResourceTypes(body);
assertValidResourceTypes(body.items);
const resourceRefs = Array.from(
new Set(body.map(({ resourceRef }) => resourceRef)),
new Set(body.items.map(({ resourceRef }) => resourceRef)),
);
const resourceArray = await getResources(resourceRefs);
const resources = resourceRefs.reduce((acc, resourceRef, index) => {
@@ -200,8 +208,8 @@ export const createPermissionIntegrationRouter = <TResource>(options: {
return acc;
}, {} as Record<string, TResource | undefined>);
return res.status(200).json(
body.map(request => ({
return res.status(200).json({
items: body.items.map(request => ({
id: request.id,
result: applyConditions(
request.conditions,
@@ -211,7 +219,7 @@ export const createPermissionIntegrationRouter = <TResource>(options: {
? AuthorizeResult.ALLOW
: AuthorizeResult.DENY,
})),
);
});
},
);