permission-node: make resources and rules optional in createPermissionIntegrationRouter

Co-authored-by: Harry Hogg <hhogg@spotify.com>
Signed-off-by: Vincenzo Scamporlino <vincenzos@spotify.com>
This commit is contained in:
Vincenzo Scamporlino
2023-02-14 17:12:28 +01:00
parent 3bf83a2aab
commit 85194da56c
2 changed files with 75 additions and 33 deletions
@@ -62,12 +62,16 @@ const createApp = (
| typeof defaultMockedGetResources
| null = defaultMockedGetResources,
) => {
const router = createPermissionIntegrationRouter({
resourceType: 'test-resource',
permissions: [testPermission],
getResources: mockedGetResources || undefined,
rules: [testRule1, testRule2],
});
const router = createPermissionIntegrationRouter(
mockedGetResources
? {
resourceType: 'test-resource',
permissions: [testPermission],
getResources: mockedGetResources,
rules: [testRule1, testRule2],
}
: { permissions: [testPermission] },
);
return express().use(router);
};
@@ -555,16 +559,16 @@ describe('createPermissionIntegrationRouter', () => {
expect(response.error && response.error.text).toMatch(/invalid/i);
});
it('returns 400 with no getResources implementation', async () => {
it('returns 501 with no getResources implementation', async () => {
const response = await request(createApp(null))
.post('/.well-known/backstage/permissions/apply-conditions')
.send({
items: [],
});
expect(response.status).toEqual(400);
expect(response.status).toEqual(501);
expect(response.body.error.message).toEqual(
'This plugin does not support the apply-conditions API.',
'This plugin does not support the apply-conditions API',
);
});
});
@@ -36,6 +36,7 @@ import {
isNotCriteria,
isOrCriteria,
} from './util';
import { NotImplementedError } from '@backstage/errors';
const permissionCriteriaSchema: z.ZodSchema<
PermissionCriteria<PermissionCondition>
@@ -204,10 +205,11 @@ const applyConditions = <TResourceType extends string, TResource>(
*
* @public
*/
export const createPermissionIntegrationRouter = <
type CreatePermissionIntegrationRouterResourceOptions<
TResourceType extends string,
TResource,
>(options: {
> = {
resourceType: TResourceType;
permissions?: Array<Permission>;
// Do not infer value of TResourceType from supplied rules.
@@ -215,12 +217,28 @@ export const createPermissionIntegrationRouter = <
// consider any rules whose resource type does not match
// to be an error.
rules: PermissionRule<TResource, any, NoInfer<TResourceType>>[];
getResources?: GetResourcesFn<TResource>;
}): express.Router => {
const { resourceType, permissions, rules, getResources } = options;
getResources: GetResourcesFn<TResource>;
};
type CreatePermissionIntegrationRouterOptions<
TResourceType extends string,
TResource,
> =
| {
permissions: Array<Permission>;
}
| CreatePermissionIntegrationRouterResourceOptions<TResourceType, TResource>;
export const createPermissionIntegrationRouter = <
TResourceType extends string,
TResource,
>(
options: CreatePermissionIntegrationRouterOptions<TResourceType, TResource>,
): express.Router => {
const router = Router();
router.use(express.json());
const { permissions = [], rules = [] } = { rules: [], ...options };
router.get('/.well-known/backstage/permissions/metadata', (_, res) => {
const serializedRules: MetadataResponseSerializedRule[] = rules.map(
rule => ({
@@ -239,30 +257,31 @@ export const createPermissionIntegrationRouter = <
return res.json(responseJson);
});
const getRule = createGetRule(rules);
const assertValidResourceTypes = (
requests: ApplyConditionsRequestEntry[],
) => {
const invalidResourceTypes = requests
.filter(request => request.resourceType !== resourceType)
.map(request => request.resourceType);
if (invalidResourceTypes.length) {
throw new InputError(
`Unexpected resource types: ${invalidResourceTypes.join(', ')}.`,
);
}
};
router.post(
'/.well-known/backstage/permissions/apply-conditions',
async (req, res: Response<ApplyConditionsResponse | string>) => {
if (!getResources) {
throw new InputError(
'This plugin does not support the apply-conditions API.',
if (!isCreatePermissionIntegrationRouterResourceOptions(options)) {
throw new NotImplementedError(
'This plugin does not support the apply-conditions API',
);
}
const { resourceType, getResources } = options;
const getRule = createGetRule(rules);
const assertValidResourceTypes = (
requests: ApplyConditionsRequestEntry[],
) => {
const invalidResourceTypes = requests
.filter(request => request.resourceType !== resourceType)
.map(request => request.resourceType);
if (invalidResourceTypes.length) {
throw new InputError(
`Unexpected resource types: ${invalidResourceTypes.join(', ')}.`,
);
}
};
const parseResult = applyConditionsRequestSchema.safeParse(req.body);
@@ -303,3 +322,22 @@ export const createPermissionIntegrationRouter = <
return router;
};
function isCreatePermissionIntegrationRouterResourceOptions<
TResourceType extends string,
TResource,
>(
options: CreatePermissionIntegrationRouterOptions<TResourceType, TResource>,
): options is CreatePermissionIntegrationRouterResourceOptions<
TResourceType,
TResource
> {
return (
(
options as CreatePermissionIntegrationRouterResourceOptions<
TResourceType,
TResource
>
).resourceType !== undefined
);
}