@@ -16,14 +16,17 @@
|
||||
|
||||
import { ServerPermissionClient } from '.';
|
||||
import {
|
||||
DiscoveryApi,
|
||||
Permission,
|
||||
Identified,
|
||||
AuthorizeRequest,
|
||||
AuthorizeResult,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { getVoidLogger, ServerTokenManager } from '@backstage/backend-common';
|
||||
import {
|
||||
getVoidLogger,
|
||||
PluginEndpointDiscovery,
|
||||
ServerTokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { RestContext, rest } from 'msw';
|
||||
|
||||
@@ -37,10 +40,13 @@ const mockAuthorizeHandler = jest.fn((req, res, { json }: RestContext) => {
|
||||
return res(json(responses));
|
||||
});
|
||||
const mockBaseUrl = 'http://backstage:9191/i-am-a-mock-base';
|
||||
const discoveryApi: DiscoveryApi = {
|
||||
const discovery: PluginEndpointDiscovery = {
|
||||
async getBaseUrl() {
|
||||
return mockBaseUrl;
|
||||
},
|
||||
async getExternalBaseUrl() {
|
||||
return mockBaseUrl;
|
||||
},
|
||||
};
|
||||
const testPermission: Permission = {
|
||||
name: 'test.permission',
|
||||
@@ -62,10 +68,10 @@ describe('ServerPermissionClient', () => {
|
||||
afterEach(() => server.resetHandlers());
|
||||
|
||||
it('should bypass authorization if permissions are disabled', async () => {
|
||||
const client = new ServerPermissionClient({
|
||||
discoveryApi,
|
||||
configApi: new ConfigReader({}),
|
||||
serverTokenManager: ServerTokenManager.noop(),
|
||||
const client = ServerPermissionClient.create({
|
||||
discovery,
|
||||
config: new ConfigReader({}),
|
||||
tokenManager: ServerTokenManager.noop(),
|
||||
});
|
||||
|
||||
await client.authorize([{ permission: testPermission }]);
|
||||
@@ -75,10 +81,10 @@ describe('ServerPermissionClient', () => {
|
||||
|
||||
it('should bypass authorization if permissions are enabled and request has valid server token', async () => {
|
||||
const tokenManager = ServerTokenManager.fromConfig(config, { logger });
|
||||
const client = new ServerPermissionClient({
|
||||
discoveryApi,
|
||||
configApi: config,
|
||||
serverTokenManager: tokenManager,
|
||||
const client = ServerPermissionClient.create({
|
||||
discovery,
|
||||
config,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
await client.authorize([{ permission: testPermission }], {
|
||||
@@ -90,10 +96,10 @@ describe('ServerPermissionClient', () => {
|
||||
|
||||
it('should authorize normally if permissions are enabled and request does not have valid server token', async () => {
|
||||
const tokenManager = ServerTokenManager.fromConfig(config, { logger });
|
||||
const client = new ServerPermissionClient({
|
||||
discoveryApi,
|
||||
configApi: config,
|
||||
serverTokenManager: tokenManager,
|
||||
const client = ServerPermissionClient.create({
|
||||
discovery,
|
||||
config,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
await client.authorize([{ permission: testPermission }], {
|
||||
@@ -104,13 +110,12 @@ describe('ServerPermissionClient', () => {
|
||||
});
|
||||
|
||||
it('should error if permissions are enabled but a no-op token manager is configured', async () => {
|
||||
expect(
|
||||
() =>
|
||||
new ServerPermissionClient({
|
||||
discoveryApi,
|
||||
configApi: config,
|
||||
serverTokenManager: ServerTokenManager.noop(),
|
||||
}),
|
||||
expect(() =>
|
||||
ServerPermissionClient.create({
|
||||
discovery,
|
||||
config,
|
||||
tokenManager: ServerTokenManager.noop(),
|
||||
}),
|
||||
).toThrowError(
|
||||
'You must configure at least one key in backend.auth.keys if permissions are enabled.',
|
||||
);
|
||||
|
||||
@@ -14,16 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TokenManager, ServerTokenManager } from '@backstage/backend-common';
|
||||
import {
|
||||
TokenManager,
|
||||
ServerTokenManager,
|
||||
PluginEndpointDiscovery,
|
||||
} from '@backstage/backend-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
AuthorizeRequest,
|
||||
AuthorizeRequestOptions,
|
||||
AuthorizeResponse,
|
||||
AuthorizeResult,
|
||||
DiscoveryApi,
|
||||
PermissionClient,
|
||||
PermissionClientInterface,
|
||||
PermissionAuthorizer,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
|
||||
/**
|
||||
@@ -32,32 +35,47 @@ import {
|
||||
* backend-to-backend requests.
|
||||
* @public
|
||||
*/
|
||||
export class ServerPermissionClient implements PermissionClientInterface {
|
||||
private readonly serverTokenManager: TokenManager;
|
||||
export class ServerPermissionClient implements PermissionAuthorizer {
|
||||
private readonly permissionClient: PermissionClient;
|
||||
private readonly tokenManager: TokenManager;
|
||||
private readonly permissionEnabled: boolean;
|
||||
|
||||
constructor(options: {
|
||||
discoveryApi: DiscoveryApi;
|
||||
configApi: Config;
|
||||
serverTokenManager: TokenManager;
|
||||
static create(options: {
|
||||
discovery: PluginEndpointDiscovery;
|
||||
config: Config;
|
||||
tokenManager: TokenManager;
|
||||
}) {
|
||||
const { discoveryApi, configApi, serverTokenManager } = options;
|
||||
this.permissionClient = new PermissionClient({ discoveryApi, configApi });
|
||||
this.permissionEnabled =
|
||||
options.configApi.getOptionalBoolean('permission.enabled') ?? false;
|
||||
const { discovery, config, tokenManager } = options;
|
||||
const permissionClient = new PermissionClient({ discovery, config });
|
||||
const permissionEnabled =
|
||||
config.getOptionalBoolean('permission.enabled') ?? false;
|
||||
|
||||
if (
|
||||
this.permissionEnabled &&
|
||||
permissionEnabled &&
|
||||
// TODO: Find a cleaner way of ensuring usage of SERVER token manager when
|
||||
// permissions are enabled.
|
||||
serverTokenManager instanceof ServerTokenManager.noop().constructor
|
||||
tokenManager instanceof ServerTokenManager.noop().constructor
|
||||
) {
|
||||
throw new Error(
|
||||
'You must configure at least one key in backend.auth.keys if permissions are enabled.',
|
||||
);
|
||||
}
|
||||
this.serverTokenManager = serverTokenManager;
|
||||
|
||||
return new ServerPermissionClient({
|
||||
permissionClient,
|
||||
tokenManager,
|
||||
permissionEnabled,
|
||||
});
|
||||
}
|
||||
|
||||
private constructor(options: {
|
||||
permissionClient: PermissionClient;
|
||||
tokenManager: TokenManager;
|
||||
permissionEnabled: boolean;
|
||||
}) {
|
||||
this.permissionClient = options.permissionClient;
|
||||
this.tokenManager = options.tokenManager;
|
||||
this.permissionEnabled = options.permissionEnabled;
|
||||
}
|
||||
|
||||
async authorize(
|
||||
@@ -83,7 +101,7 @@ export class ServerPermissionClient implements PermissionClientInterface {
|
||||
if (!token) {
|
||||
return false;
|
||||
}
|
||||
return this.serverTokenManager
|
||||
return this.tokenManager
|
||||
.authenticate(token)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
Reference in New Issue
Block a user