diff --git a/.changeset/four-readers-vanish.md b/.changeset/four-readers-vanish.md new file mode 100644 index 0000000000..6a1d4ffb5a --- /dev/null +++ b/.changeset/four-readers-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-node': patch +--- + +Added `AuthResolverContext.resolveOwnershipEntityRefs` as a way of accessing the default ownership resolution logic in sign-in resolvers, replacing `getDefaultOwnershipEntityRefs` from `@backstage/plugin-auth-backend`. diff --git a/.changeset/gold-donkeys-flow.md b/.changeset/gold-donkeys-flow.md new file mode 100644 index 0000000000..3fa7b4568e --- /dev/null +++ b/.changeset/gold-donkeys-flow.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +To avoid conflicts with Backstage, we removed global styles and set font-family and font-weight for each components. diff --git a/.changeset/olive-carrots-move.md b/.changeset/olive-carrots-move.md new file mode 100644 index 0000000000..b2cf4337b2 --- /dev/null +++ b/.changeset/olive-carrots-move.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-proxy-backend': minor +--- + +**BREAKING**: Removed support for the old backend system. + +As part of this change the plugin export from `/alpha` as been removed. If you are currently importing `@backstage/plugin-proxy-backend/alpha`, please update your import to `@backstage/plugin-proxy-backend`. diff --git a/.changeset/short-moose-attend.md b/.changeset/short-moose-attend.md new file mode 100644 index 0000000000..a6ed564917 --- /dev/null +++ b/.changeset/short-moose-attend.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Marked the remaining exports related to `createRouter` and the old backend system as deprecated. + +For more information about migrating to the new backend system, see the [migration guide](https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin). + +Support for the old backend system will be removed in the next release of this plugin. diff --git a/.changeset/silver-fishes-shop.md b/.changeset/silver-fishes-shop.md new file mode 100644 index 0000000000..a757cc9b42 --- /dev/null +++ b/.changeset/silver-fishes-shop.md @@ -0,0 +1,23 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Deprecated `getDefaultOwnershipEntityRefs` in favor of the new `.resolveOwnershipEntityRefs(...)` method in the `AuthResolverContext`. + +The following code in a custom sign-in resolver: + +```ts +import { getDefaultOwnershipEntityRefs } from '@backstage/plugin-auth-backend'; + +// ... + +const ent = getDefaultOwnershipEntityRefs(entity); +``` + +Can be replaced with the following: + +```ts +const { ownershipEntityRefs: ent } = await ctx.resolveOwnershipEntityRefs( + entity, +); +``` diff --git a/docs/auth/identity-resolver.md b/docs/auth/identity-resolver.md index 84e8726042..cce8f08b7a 100644 --- a/docs/auth/identity-resolver.md +++ b/docs/auth/identity-resolver.md @@ -298,10 +298,9 @@ of lower-level calls: ```ts // File: packages/backend/src/plugins/auth.ts -import { getDefaultOwnershipEntityRefs } from '@backstage/plugin-auth-backend'; // ... -async signInResolver({ profile: { email} }, ctx) { +async signInResolver({ profile: { email } }, ctx) { if (!email) { throw new Error('User profile contained no email'); } @@ -323,19 +322,19 @@ async signInResolver({ profile: { email} }, ctx) { // // You might also replace it if you for example want to filter out certain groups. // - // Note that `getDefaultOwnershipEntityRefs` only includes groups to which the - // user has a direct MEMBER_OF relationship. It's perfectly fine to include - // groups that the user is transitively part of in the claims array, but the - // catalog doesn't currently provide a direct way of accessing this list of - // groups. - const ownershipRefs = getDefaultOwnershipEntityRefs(entity); + // Note that `ctx.resolveOwnershipEntityRefs(...)` by default only includes groups + // to which the user has a direct MEMBER_OF relationship. + // It's perfectly fine to include groups that the user is transitively part of + // in the claims array, but the catalog doesn't currently provide a direct + // way of accessing this list of groups. + const { ownershipEntityRefs } = await ctx.resolveOwnershipEntityRefs(entity); // The last step is to issue the token, where we might provide more options in the // future. return ctx.issueToken({ claims: { sub: stringifyEntityRef(entity), - ent: ownershipRefs, + ent: ownershipEntityRefs, }, }); } diff --git a/docs/features/search/collators.md b/docs/features/search/collators.md index 0282bc845e..834a14437a 100644 --- a/docs/features/search/collators.md +++ b/docs/features/search/collators.md @@ -35,6 +35,10 @@ backend.start(); ### Configuring the Catalog Collator +The following sections outlines the available configurations for this collator. + +#### Scheduling + The default schedule for the Catalog Collator is to run every 10 minutes, you can provide your own schedule by adding it to your config: ```yaml title="app-config.yaml @@ -50,6 +54,42 @@ search: timeout: { minutes: 3 } ``` +#### Filtering + +You may wish to collate specific subsets of entities in your Catalog, this can be accomplished using the `filter` configuration option. Here's a basic example: + +```yaml title"app-config.yaml" +search: + collators: + catalog: + filter: + kind: ['component', 'api'] + spec.lifecycle: production +``` + +The above example will only collate entities that are `kind` equal to `component` or `api` AND have a `spec.lifecycle` set to `production` + +You can also apply a more advanced filter like this: + +```yaml title"app-config.yaml" +search: + collators: + catalog: + filter: + - kind: ['API'] + spec.type: openapi + - kind: ['Component'] + spec.lifecycle: experimental +``` + +Now with this example it will collate all entities that are `kind` equal to `api` with a `spec.type` equal to `openapi` OR all entities that are `kind` equal to `component` AND have a `spec.lifecycle` set to `experimental` + +:::tip + +The filter configuration is implemented using the `EntityFilterQuery` syntax. The [reference documentation on `EntityFilterQuery`](https://backstage.io/docs/reference/catalog-client.entityfilterquery/) provides more details. + +::: + ## TechDocs The TechDocs collator will index all the TechDocs in your Catalog. It is installed by default but if you need to add it manually here's how. diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md index 007b108df5..0476ef6c24 100644 --- a/docs/features/search/how-to-guides.md +++ b/docs/features/search/how-to-guides.md @@ -111,40 +111,6 @@ indexBuilder.addCollator({ }); ``` -## How to limit what can be searched in the Software Catalog - -The Software Catalog includes a wealth of information about the components, -systems, groups, users, and other aspects of your software ecosystem. However, -you may not always want _every_ aspect to appear when a user searches the -catalog. Examples include: - -- Entities of kind `Location`, which are often not useful to Backstage users. -- Entities of kind `User` or `Group`, if you'd prefer that users and groups be - exposed to search in a different way (or not at all). - -It's possible to write your own [Collator](./concepts.md#collators) to control -exactly what's available to search, (or a [Decorator](./concepts.md#decorators) -to filter things out here and there), but the `DefaultCatalogCollator` that's -provided by `@backstage/plugin-catalog-backend` offers some configuration too! - -```ts title="packages/backend/src/plugins/search.ts" -indexBuilder.addCollator({ - defaultRefreshIntervalSeconds: 600, - collator: DefaultCatalogCollator.fromConfig(env.config, { - discovery: env.discovery, - tokenManager: env.tokenManager, - /* highlight-add-start */ - filter: { - kind: ['API', 'Component', 'Domain', 'Group', 'System', 'User'], - }, - /* highlight-add-end */ - }), -}); -``` - -As shown above, you can add a catalog entity filter to narrow down what catalog -entities are indexed by the search engine. - ## How to customize search results highlighting styling The default highlighting styling for matched terms in search results is your diff --git a/packages/backend-legacy/package.json b/packages/backend-legacy/package.json index c1a08c6aca..a740f80004 100644 --- a/packages/backend-legacy/package.json +++ b/packages/backend-legacy/package.json @@ -48,7 +48,6 @@ "@backstage/plugin-permission-backend": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-permission-node": "workspace:^", - "@backstage/plugin-proxy-backend": "workspace:^", "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "workspace:^", "@backstage/plugin-scaffolder-backend-module-gitlab": "workspace:^", diff --git a/packages/backend-legacy/src/index.ts b/packages/backend-legacy/src/index.ts index c883603efb..4632cddc56 100644 --- a/packages/backend-legacy/src/index.ts +++ b/packages/backend-legacy/src/index.ts @@ -42,7 +42,6 @@ import catalog from './plugins/catalog'; import events from './plugins/events'; import kubernetes from './plugins/kubernetes'; import scaffolder from './plugins/scaffolder'; -import proxy from './plugins/proxy'; import search from './plugins/search'; import techdocs from './plugins/techdocs'; import app from './plugins/app'; @@ -129,7 +128,6 @@ async function main() { const catalogEnv = useHotMemoize(module, () => createEnv('catalog')); const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder')); const authEnv = useHotMemoize(module, () => createEnv('auth')); - const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); const searchEnv = useHotMemoize(module, () => createEnv('search')); const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs')); const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes')); @@ -145,7 +143,6 @@ async function main() { apiRouter.use('/search', await search(searchEnv)); apiRouter.use('/techdocs', await techdocs(techdocsEnv)); apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv)); - apiRouter.use('/proxy', await proxy(proxyEnv)); apiRouter.use('/permission', await permission(permissionEnv)); apiRouter.use(notFoundHandler()); diff --git a/packages/backend-legacy/src/plugins/proxy.ts b/packages/backend-legacy/src/plugins/proxy.ts deleted file mode 100644 index 273e791f1c..0000000000 --- a/packages/backend-legacy/src/plugins/proxy.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020 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 { createRouter } from '@backstage/plugin-proxy-backend'; -import { Router } from 'express'; -import { PluginEnvironment } from '../types'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - return await createRouter({ - logger: env.logger, - config: env.config, - discovery: env.discovery, - }); -} diff --git a/packages/canon/src/components/Box/styles.css b/packages/canon/src/components/Box/styles.css index f3b24793cf..7a01d5aa3a 100644 --- a/packages/canon/src/components/Box/styles.css +++ b/packages/canon/src/components/Box/styles.css @@ -1,4 +1,5 @@ .canon-Box { font-family: var(--canon-font-regular); + font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); } diff --git a/packages/canon/src/components/Checkbox/styles.css b/packages/canon/src/components/Checkbox/styles.css index 386c6f4671..f026740698 100644 --- a/packages/canon/src/components/Checkbox/styles.css +++ b/packages/canon/src/components/Checkbox/styles.css @@ -24,6 +24,7 @@ gap: var(--canon-space-2); font-size: var(--canon-font-size-xs); font-family: var(--canon-font-regular); + font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); user-select: none; diff --git a/packages/canon/src/components/Input/Input.styles.css b/packages/canon/src/components/Input/Input.styles.css index fa196cfa20..4e3e6771ab 100644 --- a/packages/canon/src/components/Input/Input.styles.css +++ b/packages/canon/src/components/Input/Input.styles.css @@ -20,6 +20,7 @@ padding: 0 var(--canon-space-4); background-color: var(--canon-bg-elevated); font-size: var(--canon-font-size-3); + font-family: var(--canon-font-regular); font-weight: var(--canon-font-weight-regular); color: var(--canon-fg-primary); transition: border-color 0.2s ease-in-out, outline-color 0.2s ease-in-out; diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index 6bb470a423..b834485ff5 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -7,6 +7,8 @@ padding-bottom: var(--canon-space-0_5); padding-top: var(--canon-space-0_5); font-size: var(--canon-font-size-3); + font-family: var(--canon-font-regular); + font-weight: var(--canon-font-weight-regular); table { width: 100%; diff --git a/packages/canon/src/css/base.css b/packages/canon/src/css/base.css deleted file mode 100644 index b8bd0c4f81..0000000000 --- a/packages/canon/src/css/base.css +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2024 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 './normalize.css'; - -@layer base { - *, - html, - body { - font-family: var(--canon-font-regular); - font-weight: var(--canon-font-weight-regular); - } -} diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index cc0e5ab85a..ded8fcaa6e 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -15,7 +15,7 @@ */ /* Normalize */ -@import './base.css'; +@import './normalize.css'; @import './utilities.css'; /* Light theme tokens */ diff --git a/plugins/auth-backend-module-vmware-cloud-provider/src/authenticator.test.ts b/plugins/auth-backend-module-vmware-cloud-provider/src/authenticator.test.ts index 0e363d4700..552c3068aa 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-vmware-cloud-provider/src/authenticator.test.ts @@ -235,6 +235,7 @@ describe('vmwareCloudAuthenticator', () => { signInWithCatalogUser: jest.fn().mockResolvedValue({ token: 'backstageToken', }), + resolveOwnershipEntityRefs: jest.fn(), }; oAuthState = { @@ -432,6 +433,7 @@ describe('vmwareCloudAuthenticator', () => { signInWithCatalogUser: jest.fn().mockResolvedValue({ token: 'backstageToken', }), + resolveOwnershipEntityRefs: jest.fn(), }; refreshRequest = { diff --git a/plugins/auth-backend/report.api.md b/plugins/auth-backend/report.api.md index 20690528d1..00b1c43bc0 100644 --- a/plugins/auth-backend/report.api.md +++ b/plugins/auth-backend/report.api.md @@ -121,7 +121,7 @@ export type BitbucketServerOAuthResult = { refreshToken?: string; }; -// @public +// @public @deprecated export class CatalogIdentityClient { constructor(options: { catalogApi: CatalogApi; @@ -176,7 +176,7 @@ export type CloudflareAccessResult = { // @public @deprecated (undocumented) export type CookieConfigurer = CookieConfigurer_2; -// @public +// @public @deprecated export function createAuthProviderIntegration< TCreateOptions extends unknown[], TResolvers extends { @@ -190,13 +190,13 @@ export function createAuthProviderIntegration< resolvers: Readonly; }>; -// @public (undocumented) +// @public @deprecated (undocumented) export function createOriginFilter(config: Config): (origin: string) => boolean; // @public @deprecated (undocumented) export function createRouter(options: RouterOptions): Promise; -// @public +// @public @deprecated export const defaultAuthProviderFactories: { [providerId: string]: AuthProviderFactory_2; }; @@ -216,10 +216,10 @@ export type GcpIapResult = GcpIapResult_2; // @public @deprecated export type GcpIapTokenInfo = GcpIapTokenInfo_2; -// @public +// @public @deprecated export function getDefaultOwnershipEntityRefs(entity: Entity): string[]; -// @public (undocumented) +// @public @deprecated (undocumented) export type GithubOAuthResult = { fullProfile: Profile; params: { @@ -361,12 +361,12 @@ export const prepareBackstageIdentityResponse: typeof prepareBackstageIdentityRe // @public @deprecated (undocumented) export type ProfileInfo = ProfileInfo_2; -// @public (undocumented) +// @public @deprecated (undocumented) export type ProviderFactories = { [s: string]: AuthProviderFactory_2; }; -// @public +// @public @deprecated export const providers: Readonly<{ atlassian: Readonly<{ create: ( @@ -676,7 +676,7 @@ export interface RouterOptions { tokenManager?: TokenManager; } -// @public (undocumented) +// @public @deprecated (undocumented) export type SamlAuthResult = { fullProfile: any; }; diff --git a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts index e91108dbf2..252cae88af 100644 --- a/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts +++ b/plugins/auth-backend/src/lib/catalog/CatalogIdentityClient.ts @@ -38,6 +38,7 @@ import { * A catalog client tailored for reading out identity data from the catalog. * * @public + * @deprecated Use the provided `AuthResolverContext` instead, see https://backstage.io/docs/auth/identity-resolver#building-custom-resolvers */ export class CatalogIdentityClient { private readonly catalogApi: CatalogApi; diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts index 51b374b468..7f66179d81 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts @@ -46,6 +46,7 @@ import { CatalogIdentityClient } from '../catalog'; * A reference to the entity itself will also be included in the returned array. * * @public + * @deprecated use `ctx.resolveOwnershipEntityRefs(entity)` from the provided `AuthResolverContext` instead. */ export function getDefaultOwnershipEntityRefs(entity: Entity) { const membershipRefs = @@ -164,21 +165,26 @@ export class CatalogAuthResolverContext implements AuthResolverContext { async signInWithCatalogUser(query: AuthResolverCatalogUserQuery) { const { entity } = await this.findCatalogUser(query); - let ent: string[]; - if (this.ownershipResolver) { - const { ownershipEntityRefs } = - await this.ownershipResolver.resolveOwnershipEntityRefs(entity); - ent = ownershipEntityRefs; - } else { - ent = getDefaultOwnershipEntityRefs(entity); - } + + const { ownershipEntityRefs } = await this.resolveOwnershipEntityRefs( + entity, + ); const token = await this.tokenIssuer.issueToken({ claims: { sub: stringifyEntityRef(entity), - ent, + ent: ownershipEntityRefs, }, }); return { token }; } + + async resolveOwnershipEntityRefs( + entity: Entity, + ): Promise<{ ownershipEntityRefs: string[] }> { + if (this.ownershipResolver) { + return this.ownershipResolver.resolveOwnershipEntityRefs(entity); + } + return { ownershipEntityRefs: getDefaultOwnershipEntityRefs(entity) }; + } } diff --git a/plugins/auth-backend/src/providers/atlassian/provider.ts b/plugins/auth-backend/src/providers/atlassian/provider.ts index 22db26fd85..539f055f75 100644 --- a/plugins/auth-backend/src/providers/atlassian/provider.ts +++ b/plugins/auth-backend/src/providers/atlassian/provider.ts @@ -31,6 +31,7 @@ import { AuthHandler } from '../types'; * Auth provider integration for Atlassian auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const atlassian = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/auth0/provider.ts b/plugins/auth-backend/src/providers/auth0/provider.ts index 44e7c5bb6e..9ae31b305d 100644 --- a/plugins/auth-backend/src/providers/auth0/provider.ts +++ b/plugins/auth-backend/src/providers/auth0/provider.ts @@ -47,6 +47,7 @@ export type Auth0AuthProviderOptions = OAuthProviderOptions & { * Auth provider integration for auth0 auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const auth0 = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.ts b/plugins/auth-backend/src/providers/aws-alb/provider.ts index c09f307e96..18d4f42f32 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.ts @@ -29,6 +29,7 @@ import { createAuthProviderIntegration } from '../createAuthProviderIntegration' * Auth provider integration for AWS ALB auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const awsAlb = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/azure-easyauth/provider.ts b/plugins/auth-backend/src/providers/azure-easyauth/provider.ts index 92c5c183f9..5e61b8bf8a 100644 --- a/plugins/auth-backend/src/providers/azure-easyauth/provider.ts +++ b/plugins/auth-backend/src/providers/azure-easyauth/provider.ts @@ -31,6 +31,7 @@ export type EasyAuthResult = AzureEasyAuthResult; * Auth provider integration for Azure EasyAuth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const easyAuth = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/bitbucket/provider.ts b/plugins/auth-backend/src/providers/bitbucket/provider.ts index 2cebb15861..69ac2cf23b 100644 --- a/plugins/auth-backend/src/providers/bitbucket/provider.ts +++ b/plugins/auth-backend/src/providers/bitbucket/provider.ts @@ -69,6 +69,7 @@ export type BitbucketPassportProfile = PassportProfile & { * Auth provider integration for Bitbucket auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const bitbucket = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/cloudflare-access/provider.ts b/plugins/auth-backend/src/providers/cloudflare-access/provider.ts index cbc9a99517..4710d60d47 100644 --- a/plugins/auth-backend/src/providers/cloudflare-access/provider.ts +++ b/plugins/auth-backend/src/providers/cloudflare-access/provider.ts @@ -125,6 +125,7 @@ export type CloudflareAccessResult = { * Auth provider integration for Cloudflare Access auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const cfAccess = createAuthProviderIntegration({ create(options: { diff --git a/plugins/auth-backend/src/providers/createAuthProviderIntegration.ts b/plugins/auth-backend/src/providers/createAuthProviderIntegration.ts index 9846bb8bf9..2f7bb4a4ec 100644 --- a/plugins/auth-backend/src/providers/createAuthProviderIntegration.ts +++ b/plugins/auth-backend/src/providers/createAuthProviderIntegration.ts @@ -27,6 +27,7 @@ import { * supplies built-in sign-in resolvers for the specific provider. * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export function createAuthProviderIntegration< TCreateOptions extends unknown[], diff --git a/plugins/auth-backend/src/providers/gcp-iap/provider.ts b/plugins/auth-backend/src/providers/gcp-iap/provider.ts index e0c5b62d47..f40cb4ed25 100644 --- a/plugins/auth-backend/src/providers/gcp-iap/provider.ts +++ b/plugins/auth-backend/src/providers/gcp-iap/provider.ts @@ -27,6 +27,7 @@ import { GcpIapResult } from './types'; * Auth provider integration for Google Identity-Aware Proxy auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const gcpIap = createAuthProviderIntegration({ create(options: { diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts index 51595b0428..6c6738ad57 100644 --- a/plugins/auth-backend/src/providers/github/provider.ts +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -25,7 +25,10 @@ import { } from '@backstage/plugin-auth-node'; import { githubAuthenticator } from '@backstage/plugin-auth-backend-module-github-provider'; -/** @public */ +/** + * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin + */ export type GithubOAuthResult = { fullProfile: PassportProfile; params: { @@ -41,6 +44,7 @@ export type GithubOAuthResult = { * Auth provider integration for GitHub auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const github = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/gitlab/provider.ts b/plugins/auth-backend/src/providers/gitlab/provider.ts index 899338adce..503145a805 100644 --- a/plugins/auth-backend/src/providers/gitlab/provider.ts +++ b/plugins/auth-backend/src/providers/gitlab/provider.ts @@ -31,6 +31,7 @@ import { gitlabAuthenticator } from '@backstage/plugin-auth-backend-module-gitla * Auth provider integration for GitLab auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const gitlab = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index b5e6e7127c..99a9c40ecb 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -36,6 +36,7 @@ import { AuthHandler } from '../types'; * Auth provider integration for Google auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const google = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index f0b019d1c7..f461fe8341 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -36,6 +36,7 @@ import { * Auth provider integration for Microsoft auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const microsoft = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts b/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts index 4200355c1a..cbd02d18ea 100644 --- a/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2-proxy/provider.ts @@ -29,6 +29,7 @@ import { * Auth provider integration for oauth2-proxy auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const oauth2Proxy = createAuthProviderIntegration({ create(options: { diff --git a/plugins/auth-backend/src/providers/oauth2/provider.ts b/plugins/auth-backend/src/providers/oauth2/provider.ts index 3a00de1f95..b9ab928730 100644 --- a/plugins/auth-backend/src/providers/oauth2/provider.ts +++ b/plugins/auth-backend/src/providers/oauth2/provider.ts @@ -31,6 +31,7 @@ import { oauth2Authenticator } from '@backstage/plugin-auth-backend-module-oauth * Auth provider integration for generic OAuth2 auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const oauth2 = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/oidc/provider.test.ts b/plugins/auth-backend/src/providers/oidc/provider.test.ts index 89cf9c3b2c..773c5c8bb9 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.test.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.test.ts @@ -119,6 +119,7 @@ describe('oidc.create', () => { issueToken: jest.fn(), findCatalogUser: jest.fn(), signInWithCatalogUser: jest.fn(), + resolveOwnershipEntityRefs: jest.fn(), }, }; }); diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 40e837b0b0..6caf97ef7c 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -37,6 +37,7 @@ import { * Auth provider integration for generic OpenID Connect auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const oidc = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/okta/provider.ts b/plugins/auth-backend/src/providers/okta/provider.ts index 463afc2bf4..5746e12710 100644 --- a/plugins/auth-backend/src/providers/okta/provider.ts +++ b/plugins/auth-backend/src/providers/okta/provider.ts @@ -36,6 +36,7 @@ import { * Auth provider integration for Okta auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const okta = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/onelogin/provider.ts b/plugins/auth-backend/src/providers/onelogin/provider.ts index ab5995da4c..808e6c15d3 100644 --- a/plugins/auth-backend/src/providers/onelogin/provider.ts +++ b/plugins/auth-backend/src/providers/onelogin/provider.ts @@ -31,6 +31,7 @@ import { AuthHandler } from '../types'; * Auth provider integration for OneLogin auth * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const onelogin = createAuthProviderIntegration({ create(options?: { diff --git a/plugins/auth-backend/src/providers/providers.ts b/plugins/auth-backend/src/providers/providers.ts index 76ac51f662..ce513e6ac7 100644 --- a/plugins/auth-backend/src/providers/providers.ts +++ b/plugins/auth-backend/src/providers/providers.ts @@ -38,6 +38,7 @@ import { AuthProviderFactory } from '@backstage/plugin-auth-node'; * All built-in auth provider integrations. * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const providers = Object.freeze({ atlassian, @@ -64,6 +65,7 @@ export const providers = Object.freeze({ * All auth provider factories that are installed by default. * * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin */ export const defaultAuthProviderFactories: { [providerId: string]: AuthProviderFactory; diff --git a/plugins/auth-backend/src/providers/router.ts b/plugins/auth-backend/src/providers/router.ts index 2867611075..ceab20553d 100644 --- a/plugins/auth-backend/src/providers/router.ts +++ b/plugins/auth-backend/src/providers/router.ts @@ -34,7 +34,10 @@ import { Minimatch } from 'minimatch'; import { CatalogAuthResolverContext } from '../lib/resolvers/CatalogAuthResolverContext'; import { TokenIssuer } from '../identity/types'; -/** @public */ +/** + * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin + */ export type ProviderFactories = { [s: string]: AuthProviderFactory }; export function bindProviderRouters( @@ -145,7 +148,10 @@ export function bindProviderRouters( } } -/** @public */ +/** + * @public + * @deprecated this export will be removed + */ export function createOriginFilter( config: Config, ): (origin: string) => boolean { diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts index 83166456c7..7797de897b 100644 --- a/plugins/auth-backend/src/providers/saml/provider.ts +++ b/plugins/auth-backend/src/providers/saml/provider.ts @@ -37,7 +37,10 @@ import { SignInResolver, } from '@backstage/plugin-auth-node'; -/** @public */ +/** + * @public + * @deprecated Migrate the auth plugin to the new backend system https://backstage.io/docs/backend-system/building-backends/migrating#the-auth-plugin + */ export type SamlAuthResult = { fullProfile: any; }; diff --git a/plugins/auth-node/report.api.md b/plugins/auth-node/report.api.md index 3b467c077f..f693683920 100644 --- a/plugins/auth-node/report.api.md +++ b/plugins/auth-node/report.api.md @@ -113,6 +113,9 @@ export type AuthResolverContext = { signInWithCatalogUser( query: AuthResolverCatalogUserQuery, ): Promise; + resolveOwnershipEntityRefs(entity: Entity): Promise<{ + ownershipEntityRefs: string[]; + }>; }; // @public diff --git a/plugins/auth-node/src/types.ts b/plugins/auth-node/src/types.ts index 845fd7b0cb..abcc6a2185 100644 --- a/plugins/auth-node/src/types.ts +++ b/plugins/auth-node/src/types.ts @@ -161,6 +161,14 @@ export type AuthResolverContext = { signInWithCatalogUser( query: AuthResolverCatalogUserQuery, ): Promise; + + /** + * Resolves the ownership entity references for the provided entity. + * This will use the `AuthOwnershipResolver` if one is installed, and otherwise fall back to the default resolution logic. + */ + resolveOwnershipEntityRefs( + entity: Entity, + ): Promise<{ ownershipEntityRefs: string[] }>; }; /** diff --git a/plugins/proxy-backend/dev/index.ts b/plugins/proxy-backend/dev/index.ts index 4e4f84056a..e0e3f8fd43 100644 --- a/plugins/proxy-backend/dev/index.ts +++ b/plugins/proxy-backend/dev/index.ts @@ -19,7 +19,7 @@ import { createBackendModule } from '@backstage/backend-plugin-api'; import { proxyEndpointsExtensionPoint } from '@backstage/plugin-proxy-node/alpha'; const backend = createBackend(); -backend.add(import('../src/alpha')); +backend.add(import('../src')); backend.add( createBackendModule({ diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index eda94842b8..7dcd738939 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -24,16 +24,12 @@ "license": "Apache-2.0", "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, "main": "src/index.ts", "types": "src/index.ts", "typesVersions": { "*": { - "alpha": [ - "src/alpha.ts" - ], "package.json": [ "package.json" ] @@ -53,21 +49,11 @@ "test": "backstage-cli package test" }, "dependencies": { - "@backstage/backend-common": "^0.25.0", "@backstage/backend-plugin-api": "workspace:^", - "@backstage/config": "workspace:^", "@backstage/plugin-proxy-node": "workspace:^", "@backstage/types": "workspace:^", - "@types/express": "^4.17.6", - "express": "^4.17.1", "express-promise-router": "^4.1.0", - "http-proxy-middleware": "^2.0.0", - "morgan": "^1.10.0", - "uuid": "^11.0.0", - "winston": "^3.2.1", - "yaml": "^2.0.0", - "yn": "^4.0.0", - "yup": "^1.0.0" + "http-proxy-middleware": "^2.0.0" }, "devDependencies": { "@backstage/backend-app-api": "workspace:^", @@ -76,8 +62,9 @@ "@backstage/cli": "workspace:^", "@backstage/config-loader": "workspace:^", "@backstage/errors": "workspace:^", + "@types/express": "^4.17.6", "@types/http-proxy-middleware": "^1.0.0", - "@types/yup": "^0.32.0", + "express": "^4.17.1", "msw": "^2.0.0" }, "configSchema": "config.d.ts" diff --git a/plugins/proxy-backend/report-alpha.api.md b/plugins/proxy-backend/report-alpha.api.md deleted file mode 100644 index 4b3b1dfdc6..0000000000 --- a/plugins/proxy-backend/report-alpha.api.md +++ /dev/null @@ -1,13 +0,0 @@ -## API Report File for "@backstage/plugin-proxy-backend" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; - -// @alpha (undocumented) -const _feature: BackendFeature; -export default _feature; - -// (No @packageDocumentation comment for this package) -``` diff --git a/plugins/proxy-backend/report.api.md b/plugins/proxy-backend/report.api.md index 90e7f54378..b0b38a8451 100644 --- a/plugins/proxy-backend/report.api.md +++ b/plugins/proxy-backend/report.api.md @@ -4,32 +4,8 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import { DiscoveryService } from '@backstage/backend-plugin-api'; -import express from 'express'; -import { Logger } from 'winston'; -import { ProxyConfig } from '@backstage/plugin-proxy-node/alpha'; -import { RootConfigService } from '@backstage/backend-plugin-api'; - -// @public @deprecated -export function createRouter(options: RouterOptions): Promise; // @public const proxyPlugin: BackendFeature; export default proxyPlugin; - -// @public @deprecated (undocumented) -export interface RouterOptions { - // (undocumented) - additionalEndpoints?: ProxyConfig; - // (undocumented) - config: RootConfigService; - // (undocumented) - discovery: DiscoveryService; - // (undocumented) - logger: Logger; - // (undocumented) - reviveConsumedRequestBodies?: boolean; - // (undocumented) - skipInvalidProxies?: boolean; -} ``` diff --git a/plugins/proxy-backend/src/alpha.ts b/plugins/proxy-backend/src/alpha.ts deleted file mode 100644 index 6efe450ac3..0000000000 --- a/plugins/proxy-backend/src/alpha.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2023 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 { proxyPlugin } from './plugin'; - -/** @alpha */ -const _feature = proxyPlugin; -export default _feature; diff --git a/plugins/proxy-backend/src/index.ts b/plugins/proxy-backend/src/index.ts index ae0b6235f9..6383d3fa55 100644 --- a/plugins/proxy-backend/src/index.ts +++ b/plugins/proxy-backend/src/index.ts @@ -21,4 +21,3 @@ */ export { proxyPlugin as default } from './plugin'; -export * from './service'; diff --git a/plugins/proxy-backend/src/plugin.ts b/plugins/proxy-backend/src/plugin.ts index 52c1c26acd..6124b0bc66 100644 --- a/plugins/proxy-backend/src/plugin.ts +++ b/plugins/proxy-backend/src/plugin.ts @@ -14,12 +14,11 @@ * limitations under the License. */ -import { loggerToWinstonLogger } from '@backstage/backend-common'; import { createBackendPlugin, coreServices, } from '@backstage/backend-plugin-api'; -import { createRouterInternal } from './service/router'; +import { createRouter } from './service/router'; import { proxyEndpointsExtensionPoint } from '@backstage/plugin-proxy-node/alpha'; /** @@ -45,10 +44,10 @@ export const proxyPlugin = createBackendPlugin({ httpRouter: coreServices.httpRouter, }, async init({ config, discovery, logger, httpRouter }) { - await createRouterInternal({ + await createRouter({ config, discovery, - logger: loggerToWinstonLogger(logger), + logger, httpRouterService: httpRouter, additionalEndpoints, }); diff --git a/plugins/proxy-backend/src/service/router.config.test.ts b/plugins/proxy-backend/src/service/router.config.test.ts index d862107e00..6f81659ceb 100644 --- a/plugins/proxy-backend/src/service/router.config.test.ts +++ b/plugins/proxy-backend/src/service/router.config.test.ts @@ -60,7 +60,7 @@ describe('createRouter reloadable configuration', () => { const backend = await startTestBackend({ features: [ - import('../alpha'), + import('..'), createServiceFactory({ service: coreServices.rootConfig, deps: {}, diff --git a/plugins/proxy-backend/src/service/router.credentials.test.ts b/plugins/proxy-backend/src/service/router.credentials.test.ts index 6d1f529b30..6ed7e636ec 100644 --- a/plugins/proxy-backend/src/service/router.credentials.test.ts +++ b/plugins/proxy-backend/src/service/router.credentials.test.ts @@ -77,7 +77,7 @@ describe('credentials', () => { const backend = await startTestBackend({ features: [ - import('../alpha'), + import('..'), mockServices.rootConfig.factory({ data: config }), authServiceFactory, httpAuthServiceFactory, diff --git a/plugins/proxy-backend/src/service/router.test.ts b/plugins/proxy-backend/src/service/router.test.ts index 8dcabed16b..66d6de2f28 100644 --- a/plugins/proxy-backend/src/service/router.test.ts +++ b/plugins/proxy-backend/src/service/router.test.ts @@ -14,12 +14,7 @@ * limitations under the License. */ -import { - HostDiscovery, - loggerToWinstonLogger, -} from '@backstage/backend-common'; import { mockServices } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; import { Request, Response } from 'express'; import * as http from 'http'; import { @@ -39,27 +34,37 @@ const mockCreateProxyMiddleware = createProxyMiddleware as jest.MockedFunction< >; describe('createRouter', () => { + const deps = { + logger: mockServices.logger.mock(), + discovery: mockServices.discovery(), + httpRouterService: mockServices.httpRouter.mock(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('where all proxy config are valid', () => { - const logger = loggerToWinstonLogger(mockServices.logger.mock()); - const config = new ConfigReader({ - backend: { - baseUrl: 'https://example.com:7007', - listen: { - port: 7007, + const config = mockServices.rootConfig({ + data: { + backend: { + baseUrl: 'https://example.com:7007', + listen: { + port: 7007, + }, }, - }, - proxy: { - endpoints: { - '/test': { - target: 'https://example.com', - headers: { - Authorization: 'Bearer supersecret', + proxy: { + endpoints: { + '/test': { + target: 'https://example.com', + headers: { + Authorization: 'Bearer supersecret', + }, }, }, }, }, }); - const discovery = HostDiscovery.fromConfig(config); beforeEach(() => { mockCreateProxyMiddleware.mockClear(); @@ -67,29 +72,29 @@ describe('createRouter', () => { it('works', async () => { const router = await createRouter({ + ...deps, config, - logger, - discovery, }); expect(router).toBeDefined(); }); it('supports deprecated proxy configuration', async () => { const router = await createRouter({ + ...deps, config: mockServices.rootConfig({ data: { proxy: { - '/test': { - target: 'https://example.com', - headers: { - Authorization: 'Bearer supersecret', + endpoints: { + '/test': { + target: 'https://example.com', + headers: { + Authorization: 'Bearer supersecret', + }, }, }, }, }, }), - logger, - discovery, }); expect(router).toBeDefined(); expect(mockCreateProxyMiddleware).toHaveBeenCalledWith( @@ -102,10 +107,22 @@ describe('createRouter', () => { it('revives request bodies when set', async () => { const router = await createRouter({ - config, - logger, - discovery, - reviveConsumedRequestBodies: true, + ...deps, + config: mockServices.rootConfig({ + data: { + proxy: { + endpoints: { + '/test': { + target: 'https://example.com', + headers: { + Authorization: 'Bearer supersecret', + }, + }, + }, + reviveConsumedRequestBodies: true, + }, + }, + }), }); expect(router).toBeDefined(); @@ -120,8 +137,7 @@ describe('createRouter', () => { it('does not revive request bodies when not set', async () => { const router = await createRouter({ config, - logger, - discovery, + ...deps, }); expect(router).toBeDefined(); @@ -133,32 +149,30 @@ describe('createRouter', () => { describe('where buildMiddleware would fail', () => { it('throws an error if skip failures is not set', async () => { - const logger = loggerToWinstonLogger(mockServices.logger.mock()); - logger.warn = jest.fn(); - const config = new ConfigReader({ - backend: { - baseUrl: 'https://example.com:7007', - listen: { - port: 7007, + const config = mockServices.rootConfig({ + data: { + backend: { + baseUrl: 'https://example.com:7007', + listen: { + port: 7007, + }, }, - }, - // no target would cause the buildMiddleware to fail - proxy: { - endpoints: { - '/test': { - headers: { - Authorization: 'Bearer supersecret', + // no target would cause the buildMiddleware to fail + proxy: { + endpoints: { + '/test': { + headers: { + Authorization: 'Bearer supersecret', + }, }, }, }, }, }); - const discovery = HostDiscovery.fromConfig(config); await expect( createRouter({ + ...deps, config, - logger, - discovery, }), ).rejects.toThrow( new Error( @@ -168,34 +182,32 @@ describe('createRouter', () => { }); it('works if skip failures is set', async () => { - const logger = loggerToWinstonLogger(mockServices.logger.mock()); - logger.warn = jest.fn(); - const config = new ConfigReader({ - backend: { - baseUrl: 'https://example.com:7007', - listen: { - port: 7007, - }, - }, - // no target would cause the buildMiddleware to fail - proxy: { - endpoints: { - '/test': { - headers: { - Authorization: 'Bearer supersecret', - }, + const config = mockServices.rootConfig({ + data: { + backend: { + baseUrl: 'https://example.com:7007', + listen: { + port: 7007, }, }, + // no target would cause the buildMiddleware to fail + proxy: { + endpoints: { + '/test': { + headers: { + Authorization: 'Bearer supersecret', + }, + }, + }, + skipInvalidProxies: true, + }, }, }); - const discovery = HostDiscovery.fromConfig(config); const router = await createRouter({ + ...deps, config, - logger, - discovery, - skipInvalidProxies: true, }); - expect((logger.warn as jest.Mock).mock.calls[0][0]).toEqual( + expect(deps.logger.warn.mock.calls[0][0]).toEqual( 'skipped configuring /test due to Proxy target for route "/test" must be a string, but is of type undefined', ); expect(router).toBeDefined(); @@ -204,14 +216,21 @@ describe('createRouter', () => { }); describe('buildMiddleware', () => { - const logger = loggerToWinstonLogger(mockServices.logger.mock()); + const logger = mockServices.logger.mock(); + const httpRouterService = mockServices.httpRouter.mock(); beforeEach(() => { mockCreateProxyMiddleware.mockClear(); }); it('accepts strings prefixed by /', async () => { - buildMiddleware('/proxy', logger, '/test', 'http://mocked'); + buildMiddleware( + '/proxy', + logger, + '/test', + 'http://mocked', + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -227,11 +246,20 @@ describe('buildMiddleware', () => { expect(fullConfig.pathRewrite).toEqual({ '^/proxy/test/?': '/' }); expect(fullConfig.changeOrigin).toBe(true); - expect(fullConfig.logProvider!(logger)).toBe(logger); + + expect(logger.info).not.toHaveBeenCalled(); + fullConfig.logProvider!({} as any).log('test'); + expect(logger.info).toHaveBeenCalledWith('test'); }); it('accepts routes not prefixed with / when path is not suffixed with /', async () => { - buildMiddleware('/proxy', logger, 'test', 'http://mocked'); + buildMiddleware( + '/proxy', + logger, + 'test', + 'http://mocked', + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -247,11 +275,16 @@ describe('buildMiddleware', () => { expect(fullConfig.pathRewrite).toEqual({ '^/proxy/test/?': '/' }); expect(fullConfig.changeOrigin).toBe(true); - expect(fullConfig.logProvider!(logger)).toBe(logger); }); it('accepts routes prefixed with / when path is suffixed with /', async () => { - buildMiddleware('/proxy/', logger, '/test', 'http://mocked'); + buildMiddleware( + '/proxy/', + logger, + '/test', + 'http://mocked', + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -267,14 +300,19 @@ describe('buildMiddleware', () => { expect(fullConfig.pathRewrite).toEqual({ '^/proxy/test/?': '/' }); expect(fullConfig.changeOrigin).toBe(true); - expect(fullConfig.logProvider!(logger)).toBe(logger); }); it('limits allowedMethods', async () => { - buildMiddleware('/proxy', logger, '/test', { - target: 'http://mocked', - allowedMethods: ['GET', 'DELETE'], - }); + buildMiddleware( + '/proxy', + logger, + '/test', + { + target: 'http://mocked', + allowedMethods: ['GET', 'DELETE'], + }, + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -290,13 +328,18 @@ describe('buildMiddleware', () => { expect(fullConfig.pathRewrite).toEqual({ '^/proxy/test/?': '/' }); expect(fullConfig.changeOrigin).toBe(true); - expect(fullConfig.logProvider!(logger)).toBe(logger); }); it('permits default headers', async () => { - buildMiddleware('/proxy', logger, '/test', { - target: 'http://mocked', - }); + buildMiddleware( + '/proxy', + logger, + '/test', + { + target: 'http://mocked', + }, + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -334,12 +377,18 @@ describe('buildMiddleware', () => { }); it('permits default and configured headers', async () => { - buildMiddleware('/proxy', logger, '/test', { - target: 'http://mocked', - headers: { - Authorization: 'my-token', + buildMiddleware( + '/proxy', + logger, + '/test', + { + target: 'http://mocked', + headers: { + Authorization: 'my-token', + }, }, - }); + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -367,10 +416,16 @@ describe('buildMiddleware', () => { }); it('permits configured headers', async () => { - buildMiddleware('/proxy', logger, '/test', { - target: 'http://mocked', - allowedHeaders: ['authorization', 'cookie'], - }); + buildMiddleware( + '/proxy', + logger, + '/test', + { + target: 'http://mocked', + allowedHeaders: ['authorization', 'cookie'], + }, + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -399,9 +454,15 @@ describe('buildMiddleware', () => { }); it('responds default headers', async () => { - buildMiddleware('/proxy', logger, '/test', { - target: 'http://mocked', - }); + buildMiddleware( + '/proxy', + logger, + '/test', + { + target: 'http://mocked', + }, + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -441,10 +502,16 @@ describe('buildMiddleware', () => { }); it('responds configured headers', async () => { - buildMiddleware('/proxy', logger, '/test', { - target: 'http://mocked', - allowedHeaders: ['set-cookie'], - }); + buildMiddleware( + '/proxy', + logger, + '/test', + { + target: 'http://mocked', + allowedHeaders: ['set-cookie'], + }, + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -477,6 +544,7 @@ describe('buildMiddleware', () => { { target: 'http://mocked', }, + httpRouterService, true, ); @@ -497,9 +565,15 @@ describe('buildMiddleware', () => { }); it('does not revive request body when not configured', async () => { - buildMiddleware('/proxy', logger, '/test', { - target: 'http://mocked', - }); + buildMiddleware( + '/proxy', + logger, + '/test', + { + target: 'http://mocked', + }, + httpRouterService, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); @@ -511,10 +585,22 @@ describe('buildMiddleware', () => { it('rejects malformed target URLs', async () => { expect(() => - buildMiddleware('/proxy', logger, '/test', 'backstage.io'), + buildMiddleware( + '/proxy', + logger, + '/test', + 'backstage.io', + httpRouterService, + ), ).toThrow(/Proxy target is not a valid URL/); expect(() => - buildMiddleware('/proxy', logger, '/test', { target: 'backstage.io' }), + buildMiddleware( + '/proxy', + logger, + '/test', + { target: 'backstage.io' }, + httpRouterService, + ), ).toThrow(/Proxy target is not a valid URL/); }); }); diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts index 934b1fcecb..a6a52db63a 100644 --- a/plugins/proxy-backend/src/service/router.ts +++ b/plugins/proxy-backend/src/service/router.ts @@ -14,20 +14,19 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; -import express from 'express'; +import type express from 'express'; import Router from 'express-promise-router'; import { createProxyMiddleware, fixRequestBody, RequestHandler, } from 'http-proxy-middleware'; -import { Logger } from 'winston'; import http from 'http'; import { JsonObject } from '@backstage/types'; import { DiscoveryService, HttpRouterService, + LoggerService, RootConfigService, } from '@backstage/backend-plugin-api'; import { ProxyConfig } from '@backstage/plugin-proxy-node/alpha'; @@ -54,15 +53,13 @@ const safeForwardHeaders = [ ]; /** - * @public - * @deprecated Please migrate to the new backend system as this will be removed in the future. + * @internal */ export interface RouterOptions { - logger: Logger; + logger: LoggerService; config: RootConfigService; discovery: DiscoveryService; - skipInvalidProxies?: boolean; - reviveConsumedRequestBodies?: boolean; + httpRouterService: HttpRouterService; additionalEndpoints?: ProxyConfig; } @@ -70,11 +67,11 @@ export interface RouterOptions { // given config. export function buildMiddleware( pathPrefix: string, - logger: Logger, + logger: LoggerService, route: string, config: string | ProxyConfig, + httpRouterService: HttpRouterService, reviveConsumedRequestBodies?: boolean, - httpRouterService?: HttpRouterService, ): RequestHandler { let fullConfig: ProxyConfig; let credentialsPolicy: string; @@ -100,7 +97,7 @@ export function buildMiddleware( } if (credentialsPolicy === 'dangerously-allow-unauthenticated') { - httpRouterService?.addAuthPolicy({ + httpRouterService.addAuthPolicy({ path: route, allow: 'unauthenticated', }); @@ -154,7 +151,13 @@ export function buildMiddleware( } // Attach the logger to the proxy config - fullConfig.logProvider = () => logger; + fullConfig.logProvider = () => ({ + log: logger.info.bind(logger), + debug: logger.debug.bind(logger), + info: logger.info.bind(logger), + warn: logger.warn.bind(logger), + error: logger.error.bind(logger), + }); // http-proxy-middleware uses this log level to check if it should log the // requests that it proxies. Setting this to the most verbose log level // ensures that it always logs these requests. Our logger ends up deciding @@ -229,7 +232,10 @@ export function buildMiddleware( return createProxyMiddleware(filter, fullConfig); } -function readProxyConfig(config: Config, logger: Logger): JsonObject { +function readProxyConfig( + config: RootConfigService, + logger: LoggerService, +): JsonObject { const endpoints = config .getOptionalConfig('proxy.endpoints') ?.get(); @@ -256,49 +262,16 @@ function readProxyConfig(config: Config, logger: Logger): JsonObject { return rootEndpoints; } -/** - * Creates a new - * {@link https://expressjs.com/en/api.html#router | "express router"} that - * proxies each target configured under the `proxy.endpoints` key of the config. - * - * @remarks - * - * Example configuration: - * - * ```yaml - * proxy: - * endpoints: - * # Option 1: Simple URL String - * simple-example: http://simple.example.com:8080 - * # Option 2: `http-proxy-middleware` compatible object - * '/larger-example/v1': - * target: http://larger.example.com:8080/svc.v1 - * headers: - * Authorization: Bearer ${EXAMPLE_AUTH_TOKEN} - * ``` - * - * @see https://backstage.io/docs/plugins/proxying - * @public - * @deprecated Please migrate to the new backend system as this will be removed in the future. - */ +/** @internal */ export async function createRouter( options: RouterOptions, -): Promise { - return createRouterInternal(options); -} - -export async function createRouterInternal( - options: RouterOptions & { httpRouterService?: HttpRouterService }, ): Promise { const router = Router(); let currentRouter = Router(); const skipInvalidProxies = - options.skipInvalidProxies ?? - options.config.getOptionalBoolean('proxy.skipInvalidProxies') ?? - false; + options.config.getOptionalBoolean('proxy.skipInvalidProxies') ?? false; const reviveConsumedRequestBodies = - options.reviveConsumedRequestBodies ?? options.config.getOptionalBoolean('proxy.reviveConsumedRequestBodies') ?? false; const proxyOptions = { @@ -345,7 +318,7 @@ export async function createRouterInternal( }); } - options.httpRouterService?.use(router); + options.httpRouterService.use(router); return router; } @@ -353,12 +326,12 @@ function configureMiddlewares( options: { reviveConsumedRequestBodies: boolean; skipInvalidProxies: boolean; - logger: Logger; + logger: LoggerService; }, router: express.Router, pathPrefix: string, proxyConfig: ProxyConfig, - httpRouterService?: HttpRouterService, + httpRouterService: HttpRouterService, ) { Object.entries(proxyConfig).forEach(([route, proxyRouteConfig]) => { try { @@ -369,8 +342,8 @@ function configureMiddlewares( options.logger, route, proxyRouteConfig, - options.reviveConsumedRequestBodies, httpRouterService, + options.reviveConsumedRequestBodies, ), ); } catch (e) { diff --git a/yarn.lock b/yarn.lock index ab92c447a1..378e56eb4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7299,29 +7299,20 @@ __metadata: resolution: "@backstage/plugin-proxy-backend@workspace:plugins/proxy-backend" dependencies: "@backstage/backend-app-api": "workspace:^" - "@backstage/backend-common": ^0.25.0 "@backstage/backend-defaults": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" - "@backstage/config": "workspace:^" "@backstage/config-loader": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/plugin-proxy-node": "workspace:^" "@backstage/types": "workspace:^" "@types/express": ^4.17.6 "@types/http-proxy-middleware": ^1.0.0 - "@types/yup": ^0.32.0 express: ^4.17.1 express-promise-router: ^4.1.0 http-proxy-middleware: ^2.0.0 - morgan: ^1.10.0 msw: ^2.0.0 - uuid: ^11.0.0 - winston: ^3.2.1 - yaml: ^2.0.0 - yn: ^4.0.0 - yup: ^1.0.0 languageName: unknown linkType: soft @@ -20997,15 +20988,6 @@ __metadata: languageName: node linkType: hard -"@types/yup@npm:^0.32.0": - version: 0.32.0 - resolution: "@types/yup@npm:0.32.0" - dependencies: - yup: "*" - checksum: 5b30f1118bca288d949bcd4c7e17d1425ffa320eddd751a78c895b158fae0b6cbf242ced45ad47e9e70643c9475da09a7ad004192fb6f2111791d37e6a627e73 - languageName: node - linkType: hard - "@types/zen-observable@npm:^0.8.0": version: 0.8.7 resolution: "@types/zen-observable@npm:0.8.7" @@ -28929,7 +28911,6 @@ __metadata: "@backstage/plugin-permission-backend": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-permission-node": "workspace:^" - "@backstage/plugin-proxy-backend": "workspace:^" "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "workspace:^" "@backstage/plugin-scaffolder-backend-module-gitlab": "workspace:^" @@ -40469,13 +40450,6 @@ __metadata: languageName: node linkType: hard -"property-expr@npm:^2.0.5": - version: 2.0.6 - resolution: "property-expr@npm:2.0.6" - checksum: 89977f4bb230736c1876f460dd7ca9328034502fd92e738deb40516d16564b850c0bbc4e052c3df88b5b8cd58e51c93b46a94bea049a3f23f4a022c038864cab - languageName: node - linkType: hard - "property-information@npm:^5.0.0": version: 5.6.0 resolution: "property-information@npm:5.6.0" @@ -45242,13 +45216,6 @@ __metadata: languageName: node linkType: hard -"tiny-case@npm:^1.0.3": - version: 1.0.3 - resolution: "tiny-case@npm:1.0.3" - checksum: 3f7a30c39d5b0e1bc097b0b271bec14eb5b836093db034f35a0de26c14422380b50dc12bfd37498cf35b192f5df06f28a710712c87ead68872a9e37ad6f6049d - languageName: node - linkType: hard - "tiny-glob@npm:0.2.9": version: 0.2.9 resolution: "tiny-glob@npm:0.2.9" @@ -45394,13 +45361,6 @@ __metadata: languageName: node linkType: hard -"toposort@npm:^2.0.2": - version: 2.0.2 - resolution: "toposort@npm:2.0.2" - checksum: d64c74b570391c9432873f48e231b439ee56bc49f7cb9780b505cfdf5cb832f808d0bae072515d93834dd6bceca5bb34448b5b4b408335e4d4716eaf68195dcb - languageName: node - linkType: hard - "tosource@npm:^2.0.0-alpha.3": version: 2.0.0-alpha.3 resolution: "tosource@npm:2.0.0-alpha.3" @@ -48089,18 +48049,6 @@ __metadata: languageName: node linkType: hard -"yup@npm:*, yup@npm:^1.0.0": - version: 1.4.0 - resolution: "yup@npm:1.4.0" - dependencies: - property-expr: ^2.0.5 - tiny-case: ^1.0.3 - toposort: ^2.0.2 - type-fest: ^2.19.0 - checksum: 20a2ee0c1e891979ca16b34805b3a3be9ab4bea6ea3d2f9005b998b4dc992d0e4d7b53e5f4d8d9423420046630fb44fdf0ecf7e83bc34dd83392bca046c5229d - languageName: node - linkType: hard - "zen-observable@npm:^0.10.0": version: 0.10.0 resolution: "zen-observable@npm:0.10.0"