From 6b8713df35c525edb4c1e327230ded591ee6878b Mon Sep 17 00:00:00 2001 From: Joon Park Date: Thu, 2 Dec 2021 10:26:44 +0000 Subject: [PATCH] Create ServerPermissionClient and add it to example backend Signed-off-by: Joon Park --- packages/backend/package.json | 3 + packages/backend/src/index.ts | 22 ++++++- packages/backend/src/plugins/permission.ts | 48 ++++++++++++++ packages/backend/src/types.ts | 2 + plugins/permission-node/package.json | 2 + .../src/ServerPermissionClient.ts | 62 +++++++++++++++++++ plugins/permission-node/src/index.ts | 1 + 7 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 packages/backend/src/plugins/permission.ts create mode 100644 plugins/permission-node/src/ServerPermissionClient.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 80ae3b0998..d5249c28bc 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -39,6 +39,9 @@ "@backstage/plugin-jenkins-backend": "^0.1.9", "@backstage/plugin-kubernetes-backend": "^0.4.0", "@backstage/plugin-kafka-backend": "^0.2.12", + "@backstage/plugin-permission-backend": "^0.1.0", + "@backstage/plugin-permission-common": "^0.2.0", + "@backstage/plugin-permission-node": "^0.2.0", "@backstage/plugin-proxy-backend": "^0.2.14", "@backstage/plugin-rollbar-backend": "^0.1.16", "@backstage/plugin-scaffolder-backend": "^0.15.17", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 4426ed7767..ba38f8c427 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -55,13 +55,20 @@ import graphql from './plugins/graphql'; import app from './plugins/app'; import badges from './plugins/badges'; import jenkins from './plugins/jenkins'; +import permission from './plugins/permission'; import { PluginEnvironment } from './types'; +import { ServerPermissionClient } from '@backstage/plugin-permission-node'; function makeCreateEnv(config: Config) { const root = getRootLogger(); const reader = UrlReaders.default({ logger: root, config }); const discovery = SingleHostDiscovery.fromConfig(config); - const tokenManager = ServerTokenManager.noop(); + const tokenManager = ServerTokenManager.fromConfig(config); + const permissions = new ServerPermissionClient({ + discoveryApi: discovery, + configApi: config, + serverTokenManager: tokenManager, + }); root.info(`Created UrlReader ${reader}`); @@ -72,7 +79,16 @@ function makeCreateEnv(config: Config) { const logger = root.child({ type: 'plugin', plugin }); const database = databaseManager.forPlugin(plugin); const cache = cacheManager.forPlugin(plugin); - return { logger, cache, database, config, reader, discovery, tokenManager }; + return { + logger, + cache, + database, + config, + reader, + discovery, + tokenManager, + permissions, + }; }; } @@ -113,6 +129,7 @@ async function main() { const techInsightsEnv = useHotMemoize(module, () => createEnv('tech-insights'), ); + const permissionEnv = useHotMemoize(module, () => createEnv('permission')); const apiRouter = Router(); apiRouter.use('/catalog', await catalog(catalogEnv)); @@ -131,6 +148,7 @@ async function main() { apiRouter.use('/graphql', await graphql(graphqlEnv)); apiRouter.use('/badges', await badges(badgesEnv)); apiRouter.use('/jenkins', await jenkins(jenkinsEnv)); + apiRouter.use('/permission', await permission(permissionEnv)); apiRouter.use(notFoundHandler()); const service = createServiceBuilder(module) diff --git a/packages/backend/src/plugins/permission.ts b/packages/backend/src/plugins/permission.ts new file mode 100644 index 0000000000..71a9b90311 --- /dev/null +++ b/packages/backend/src/plugins/permission.ts @@ -0,0 +1,48 @@ +/* + * 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. + */ + +import { IdentityClient } from '@backstage/plugin-auth-backend'; +import { createRouter } from '@backstage/plugin-permission-backend'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { + PermissionPolicy, + PolicyDecision, +} from '@backstage/plugin-permission-node'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +class AllowAllPermissionPolicy implements PermissionPolicy { + async handle(): Promise { + return { + result: AuthorizeResult.ALLOW, + }; + } +} + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const { logger, discovery } = env; + return await createRouter({ + logger, + discovery, + policy: new AllowAllPermissionPolicy(), + identity: new IdentityClient({ + discovery, + issuer: await discovery.getExternalBaseUrl('auth'), + }), + }); +} diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 4be9c036b3..e3826d69ae 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -23,6 +23,7 @@ import { TokenManager, UrlReader, } from '@backstage/backend-common'; +import { ServerPermissionClient } from '@backstage/plugin-permission-node'; export type PluginEnvironment = { logger: Logger; @@ -32,4 +33,5 @@ export type PluginEnvironment = { reader: UrlReader; discovery: PluginEndpointDiscovery; tokenManager: TokenManager; + permissions: ServerPermissionClient; }; diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 5e0a300873..7166ae5c31 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -29,6 +29,8 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/backend-common": "^0.9.11", + "@backstage/config": "^0.1.11", "@backstage/plugin-auth-backend": "^0.5.0", "@backstage/plugin-permission-common": "^0.2.0", "@types/express": "^4.17.6", diff --git a/plugins/permission-node/src/ServerPermissionClient.ts b/plugins/permission-node/src/ServerPermissionClient.ts new file mode 100644 index 0000000000..1e54d73808 --- /dev/null +++ b/plugins/permission-node/src/ServerPermissionClient.ts @@ -0,0 +1,62 @@ +/* + * 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. + */ + +import { ServerTokenManager } from '@backstage/backend-common'; +import { Config } from '@backstage/config'; +import { + AuthorizeRequest, + AuthorizeRequestOptions, + AuthorizeResponse, + AuthorizeResult, + DiscoveryApi, + PermissionClient, +} from '@backstage/plugin-permission-common'; + +export class ServerPermissionClient extends PermissionClient { + private readonly serverTokenManager: ServerTokenManager; + + constructor(options: { + discoveryApi: DiscoveryApi; + configApi: Config; + serverTokenManager: ServerTokenManager; + }) { + const { discoveryApi, configApi, serverTokenManager } = options; + super({ discoveryApi, configApi }); + this.serverTokenManager = serverTokenManager; + } + + async authorize( + requests: AuthorizeRequest[], + options?: AuthorizeRequestOptions, + ): Promise { + if (await this.isValidServerToken(options?.token)) { + return requests.map(_ => ({ result: AuthorizeResult.ALLOW })); + } + return super.authorize(requests, options); + } + + private async isValidServerToken( + token: string | undefined, + ): Promise { + if (!token) { + return false; + } + return this.serverTokenManager + .authenticate(token) + .then(() => true) + .catch(() => false); + } +} diff --git a/plugins/permission-node/src/index.ts b/plugins/permission-node/src/index.ts index 39527bac71..32d71ddeb3 100644 --- a/plugins/permission-node/src/index.ts +++ b/plugins/permission-node/src/index.ts @@ -22,3 +22,4 @@ export * from './integration'; export * from './policy'; export * from './types'; +export { ServerPermissionClient } from './ServerPermissionClient';