Add config, type fixes

Signed-off-by: Tim Hansen <timbonicush@spotify.com>
This commit is contained in:
Tim Hansen
2021-11-16 13:21:17 -07:00
parent e84abaccaf
commit db299e05ef
7 changed files with 74 additions and 6 deletions
+7 -2
View File
@@ -21,7 +21,7 @@ export type AuthorizeResponse =
}
| {
result: AuthorizeResult.CONDITIONAL;
conditions: PermissionCriteria<unknown>;
conditions: PermissionCriteria<PermissionCondition>;
};
// @public
@@ -36,6 +36,11 @@ export type DiscoveryApi = {
getBaseUrl(pluginId: string): Promise<string>;
};
// @public
export type Identified<T> = T & {
id: string;
};
// @public
export function isCreatePermission(permission: Permission): boolean;
@@ -62,7 +67,7 @@ export type PermissionAttributes = {
// @public
export class PermissionClient {
constructor(options: { discoveryApi: DiscoveryApi });
constructor(options: { discoveryApi: DiscoveryApi; enabled?: boolean });
authorize(
requests: AuthorizeRequest[],
options?: AuthorizeRequestOptions,
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2021 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.
*/
export interface Config {
/** Configuration options for Backstage permissions and authorization */
permission?: {
/**
* Whether authorization is enabled in Backstage. Defaults to false, which means authorization
* requests will be automatically allowed without invoking the authorization policy.
* @visibility frontend
*/
enabled?: boolean;
};
}
+3 -1
View File
@@ -21,8 +21,10 @@
],
"license": "Apache-2.0",
"files": [
"dist"
"dist",
"config.d.ts"
],
"configSchema": "config.d.ts",
"scripts": {
"build": "backstage-cli build",
"lint": "backstage-cli lint",
@@ -30,7 +30,10 @@ const discoveryApi: DiscoveryApi = {
return mockBaseUrl;
},
};
const client: PermissionClient = new PermissionClient({ discoveryApi });
const client: PermissionClient = new PermissionClient({
discoveryApi,
enabled: true,
});
const mockPermission: Permission = {
name: 'test.permission',
@@ -141,5 +144,24 @@ describe('PermissionClient', () => {
client.authorize([mockAuthorizeRequest], { token }),
).rejects.toThrowError(/invalid input/i);
});
it('should allow all when authorization is not enabled', async () => {
mockAuthorizeHandler.mockImplementationOnce(
(req, res, { json }: RestContext) => {
const responses = req.body.map((a: Identified<AuthorizeRequest>) => ({
id: a.id,
outcome: AuthorizeResult.DENY,
}));
return res(json(responses));
},
);
const disabled = new PermissionClient({ discoveryApi, enabled: false });
const response = await disabled.authorize([mockAuthorizeRequest]);
expect(response[0]).toEqual(
expect.objectContaining({ result: AuthorizeResult.ALLOW }),
);
expect(mockAuthorizeHandler).not.toBeCalled();
});
});
});
@@ -71,10 +71,12 @@ export type AuthorizeRequestOptions = {
* @public
*/
export class PermissionClient {
private readonly enabled: boolean;
private readonly discoveryApi: DiscoveryApi;
constructor(options: { discoveryApi: DiscoveryApi }) {
constructor(options: { discoveryApi: DiscoveryApi; enabled?: boolean }) {
this.discoveryApi = options.discoveryApi;
this.enabled = options.enabled ?? false;
}
/**
@@ -102,6 +104,10 @@ export class PermissionClient {
// but no resourceRef. That way clients who aren't prepared to handle filtering according
// to conditions can be guaranteed that they won't unexpectedly get a CONDITIONAL response.
if (!this.enabled) {
return requests.map(_ => ({ result: AuthorizeResult.ALLOW }));
}
const identifiedRequests: Identified<AuthorizeRequest>[] = requests.map(
request => ({
id: uuid.v4(),
+6 -1
View File
@@ -16,6 +16,11 @@
import { Permission } from './permission';
/**
* A request with a UUID identifier, so that batched responses can be matched up with the original
* requests.
* @public
*/
export type Identified<T> = T & { id: string };
/**
@@ -77,5 +82,5 @@ export type AuthorizeResponse =
| { result: AuthorizeResult.ALLOW | AuthorizeResult.DENY }
| {
result: AuthorizeResult.CONDITIONAL;
conditions: PermissionCriteria<unknown>;
conditions: PermissionCriteria<PermissionCondition>;
};
@@ -18,6 +18,7 @@ export { AuthorizeResult } from './api';
export type {
AuthorizeRequest,
AuthorizeResponse,
Identified,
PermissionCondition,
PermissionCriteria,
} from './api';