permission-common: add utility types for creating and refining Permissions

Signed-off-by: Mike Lewis <mtlewis@users.noreply.github.com>
This commit is contained in:
Mike Lewis
2022-03-03 18:05:23 +00:00
committed by Joe Porpeglia
parent 95284162d6
commit 899d38ea68
8 changed files with 106 additions and 17 deletions
+3 -1
View File
@@ -2,4 +2,6 @@
'@backstage/plugin-permission-common': patch
---
Add more specific `Permission` types
- Add more specific `Permission` types.
- Add `createPermission` helper to infer the appropriate type for some permission input.
- Add `isResourcePermission` helper to refine Permissions to ResourcePermissions.
@@ -35,6 +35,7 @@ import {
Identified,
AuthorizeRequest,
AuthorizeResponse,
isResourcePermission,
} from '@backstage/plugin-permission-common';
import {
ApplyConditionsRequestEntry,
@@ -109,7 +110,7 @@ const handleRequest = async (
};
}
if (!('resourceType' in request.permission)) {
if (!isResourcePermission(request.permission)) {
throw new Error(
`Conditional decision returned from permission policy for non-resource permission ${request.permission.name}`,
);
+19
View File
@@ -59,6 +59,19 @@ export type BasicPermission = {
attributes: PermissionAttributes;
};
// @public
export function createPermission<TResourceType extends string>(input: {
name: string;
attributes: PermissionAttributes;
resourceType: TResourceType;
}): ResourcePermission<TResourceType>;
// @public
export function createPermission(input: {
name: string;
attributes: PermissionAttributes;
}): BasicPermission;
// @public
export type DiscoveryApi = {
getBaseUrl(pluginId: string): Promise<string>;
@@ -78,6 +91,12 @@ export function isDeletePermission(permission: Permission): boolean;
// @public
export function isReadPermission(permission: Permission): boolean;
// @public
export function isResourcePermission<T extends string = string>(
permission: Permission,
resourceType?: T,
): permission is ResourcePermission<T>;
// @public
export function isUpdatePermission(permission: Permission): boolean;
@@ -20,7 +20,7 @@ import { ConfigReader } from '@backstage/config';
import { PermissionClient } from './PermissionClient';
import { AuthorizeQuery, AuthorizeResult, Identified } from './types/api';
import { DiscoveryApi } from './types/discovery';
import { Permission } from './types/permission';
import { createPermission } from './permissions';
const server = setupServer();
const token = 'fake-token';
@@ -36,16 +36,11 @@ const client: PermissionClient = new PermissionClient({
config: new ConfigReader({ permission: { enabled: true } }),
});
const mockPermission: Permission = {
const mockPermission = createPermission({
name: 'test.permission',
attributes: {},
resourceType: 'test-resource',
};
const mockAuthorizeQuery = {
permission: mockPermission,
resourceRef: 'foo',
};
resourceType: 'foo',
});
describe('PermissionClient', () => {
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
@@ -53,6 +48,11 @@ describe('PermissionClient', () => {
afterEach(() => server.resetHandlers());
describe('authorize', () => {
const mockAuthorizeQuery = {
permission: mockPermission,
resourceRef: 'foo:bar',
};
const mockAuthorizeHandler = jest.fn((req, res, { json }: RestContext) => {
const responses = req.body.items.map((a: Identified<AuthorizeQuery>) => ({
id: a.id,
@@ -84,7 +84,7 @@ describe('PermissionClient', () => {
items: [
expect.objectContaining({
permission: mockPermission,
resourceRef: 'foo',
resourceRef: 'foo:bar',
}),
],
});
@@ -0,0 +1,50 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
BasicPermission,
Permission,
PermissionAttributes,
ResourcePermission,
} from '../types';
/**
* Utility function for creating a valid {@link ResourcePermission}, inferring
* the appropriate type and resource type parameter.
*
* @public
*/
export function createPermission<TResourceType extends string>(input: {
name: string;
attributes: PermissionAttributes;
resourceType: TResourceType;
}): ResourcePermission<TResourceType>;
/**
* Utility function for creating a valid {@link BasicPermission}.
*
* @public
*/
export function createPermission(input: {
name: string;
attributes: PermissionAttributes;
}): BasicPermission;
export function createPermission(input: {
name: string;
attributes: PermissionAttributes;
resourceType?: string;
}): Permission {
return input;
}
@@ -15,3 +15,4 @@
*/
export * from './util';
export { createPermission } from './createPermission';
@@ -14,7 +14,24 @@
* limitations under the License.
*/
import { Permission } from '../types';
import { Permission, ResourcePermission } from '../types';
/**
* Check if a given permission is a {@link ResourcePermission}. When
* `resourceType` is supplied as the second parameter, also checks if
* the permission has the specified resource type.
* @public
*/
export function isResourcePermission<T extends string = string>(
permission: Permission,
resourceType?: T,
): permission is ResourcePermission<T> {
if (!('resourceType' in permission)) {
return false;
}
return !resourceType || permission.resourceType === resourceType;
}
/**
* Check if a given permission is related to a create action.
@@ -16,10 +16,10 @@
import { ServerPermissionClient } from './ServerPermissionClient';
import {
Permission,
Identified,
AuthorizeQuery,
AuthorizeResult,
createPermission,
} from '@backstage/plugin-permission-common';
import { ConfigReader } from '@backstage/config';
import {
@@ -48,11 +48,10 @@ const discovery: PluginEndpointDiscovery = {
return mockBaseUrl;
},
};
const testPermission: Permission = {
const testPermission = createPermission({
name: 'test.permission',
attributes: {},
resourceType: 'test-resource',
};
});
const config = new ConfigReader({
permission: { enabled: true },
backend: { auth: { keys: [{ secret: 'a-secret-key' }] } },