permission-node: wrap request and response arrays in object
Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
This commit is contained in:
+118
-99
@@ -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,
|
||||
})),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user