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:
committed by
Joe Porpeglia
parent
95284162d6
commit
899d38ea68
@@ -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}`,
|
||||
);
|
||||
|
||||
@@ -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' }] } },
|
||||
|
||||
Reference in New Issue
Block a user