diff --git a/.changeset/brave-jobs-invite.md b/.changeset/brave-jobs-invite.md new file mode 100644 index 0000000000..402c5cebe7 --- /dev/null +++ b/.changeset/brave-jobs-invite.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Added a new `LegacyRootDatabaseService` interface that can be used to avoid direct dependencies on the `DatabaseManager`. diff --git a/.changeset/khaki-seahorses-joke.md b/.changeset/khaki-seahorses-joke.md new file mode 100644 index 0000000000..9382d6f108 --- /dev/null +++ b/.changeset/khaki-seahorses-joke.md @@ -0,0 +1,6 @@ +--- +'@backstage/integration-aws-node': patch +'@backstage/integration': patch +--- + +All single-line secrets read from config will now have both leading and trailing whitespace trimmed. This is done to ensure that the secrets are always valid HTTP header values, since many fetch implementations will include the header value itself when an error is thrown due to invalid header values. diff --git a/.changeset/lazy-dolls-roll.md b/.changeset/lazy-dolls-roll.md new file mode 100644 index 0000000000..da375727cc --- /dev/null +++ b/.changeset/lazy-dolls-roll.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-tasks': patch +--- + +The `TaskScheduler.fromConfig` method now accepts the `LegacyRootDatabaseService` interface rather than the full `DatabaseManager` implementation. diff --git a/.changeset/long-wasps-wait.md b/.changeset/long-wasps-wait.md new file mode 100644 index 0000000000..f027fb97ce --- /dev/null +++ b/.changeset/long-wasps-wait.md @@ -0,0 +1,5 @@ +--- +'@backstage/config-loader': patch +--- + +Make schema processing gracefully handle an empty config. diff --git a/.changeset/modern-peaches-clean.md b/.changeset/modern-peaches-clean.md new file mode 100644 index 0000000000..13dbb75183 --- /dev/null +++ b/.changeset/modern-peaches-clean.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Updated the default app index template at `packages/app/public/index.html` to have a fallback value for the `app.title` config. diff --git a/.changeset/poor-jeans-rescue.md b/.changeset/poor-jeans-rescue.md new file mode 100644 index 0000000000..e44b9c6bc6 --- /dev/null +++ b/.changeset/poor-jeans-rescue.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Make `http://localhost:3000` the default base URL for serving locally, and `/` the default public path for built apps. The app build no longer requires any configuration values to be present. diff --git a/.changeset/renovate-f253121.md b/.changeset/renovate-f253121.md new file mode 100644 index 0000000000..42899973fd --- /dev/null +++ b/.changeset/renovate-f253121.md @@ -0,0 +1,16 @@ +--- +'@backstage/backend-common': patch +'@backstage/cli': patch +'@backstage/integration': patch +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-github-actions': patch +'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-techdocs-module-addons-contrib': patch +'@backstage/plugin-techdocs-node': patch +'@backstage/plugin-techdocs': patch +--- + +Updated dependency `git-url-parse` to `^14.0.0`. diff --git a/.changeset/serious-dryers-grin.md b/.changeset/serious-dryers-grin.md new file mode 100644 index 0000000000..24d17f4959 --- /dev/null +++ b/.changeset/serious-dryers-grin.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Fix entity content extension filtering. diff --git a/.changeset/silver-crabs-exercise.md b/.changeset/silver-crabs-exercise.md new file mode 100644 index 0000000000..ff34b5a3c3 --- /dev/null +++ b/.changeset/silver-crabs-exercise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': minor +--- + +Migrate the `api-docs` to the new frontend system. It is experimental and available via alpha subpath. diff --git a/OWNERS.md b/OWNERS.md index 7ad21c6fee..1e92e68c40 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -148,6 +148,7 @@ Scope: The Scaffolder frontend and backend plugins, and related tooling. | Brian Fletcher | Roadie.io | [punkle](https://github.com/punkle) | `Brian Fletcher#7051` | | Carlos Esteban Lopez Jaramillo | VMWare | [luchillo17](https://github.com/luchillo17) | `luchillo17#8777` | | David Tuite | Roadie.io | [dtuite](https://github.com/dtuite) | `David Tuite (roadie.io)#1010` | +| Deepankumar Loganathan | | [deepan10](https://github.com/deepan10) | `deepan10` | | Himanshu Mishra | Harness.io | [OrkoHunter](https://github.com/OrkoHunter) | `OrkoHunter#1520` | | Irma Solakovic | Roadie.io | [Irma12](https://github.com/Irma12) | `Irma#7629` | | Jamie Klassen | VMware | [jamieklassen](https://github.com/jamieklassen) | `jamieklassen#3047` | diff --git a/beps/0003-auth-architecture-evolution/README.md b/beps/0003-auth-architecture-evolution/README.md index e5937aab0d..ec2660a9cc 100644 --- a/beps/0003-auth-architecture-evolution/README.md +++ b/beps/0003-auth-architecture-evolution/README.md @@ -113,7 +113,9 @@ export interface AuthService { // TODO: should the caller provide the target plugin ID? // TODO: how can we make it very difficult to forget to forward credentials - issueToken(credentials: BackstageCredentials): Promise<{ token: string }>; + issueToken(options: { + forward?: BackstageCredentials; + }): Promise<{ token: string }>; } ``` @@ -138,15 +140,18 @@ The `UserInfoService` is exported by `@backstage/auth-node`, and the initial imp The `HttpRouterService` interface will be extended with the ability to opt-out of the default protection of endpoints, enabling either cookie auth or unauthenticated access. ```ts +export interface HttpRouterServiceAuthPolicy { + // The path matches in the same way as if it was passed to `express.Router.use(path, ...)` + path: string; + allow: 'unauthenticated' | 'user-cookie'; +} + export interface HttpRouterService { // All routes only allow authenticated users and services by default. use(handler: Handler): void; - // Exact option structure is TBD, just highlighting the general idea for now - configure(options: { - allowCookieAuthOnPaths?: string[]; - allowUnauthenticatedAccessPaths?: string[]; - }): void; + // These are additive and the most relaxed access level takes precedence + addAuthPolicy(policy: HttpRouterServiceAuthPolicy): void; } ``` @@ -187,8 +192,9 @@ export default createBackendPlugin({ async init({ http }) { // The order of these two calls does not matter http.use(await createRouter(/* ... */)); - http.configure({ - allowCookieAuthOnPaths: ['/static'], + http.addAuthPolicy({ + path: '/static', + allow: 'user-cookie', }); }, }); @@ -208,10 +214,16 @@ export default createBackendPlugin({ }, async init({ http }) { http.use(await createRouter(/* ... */)); - http.configure({ - allowCookieAuthOnPaths: ['/'], - // Unauthenticated access takes precedence, the /public endpoint does not require cookie auth - allowUnauthenticatedAccessPaths: ['/public'], + + http.addAuthPolicy({ + path: '/', + allow: 'user-cookie', + }); + + // Unauthenticated access takes precedence, the /public endpoint does not require cookie auth + http.addAuthPolicy({ + path: '/public', + allow: 'unauthenticated', }); }, }); @@ -232,22 +244,21 @@ export type BackstageUnauthorizedCredentials = { type BackstageCredentialTypes = { user: BackstageUserCredentials; + 'user-cookie': BackstageUserCredentials; service: BackstageServiceCredentials; unauthorized: BackstageUnauthorizedCredentials; }; export interface HttpAuthService { - createHttpPluginRouterMiddleware(options: OptionsTBD): Handler; - + // Implementations should cache resolved credentials on the request object credentials( req: Request, options?: HttpAuthServiceMiddlewareOptions, ): Promise; - // TODO: Keep an eye on this, might not be needed - requestHeaders( - credentials: BackstageCredentials, - ): Promise>; + requestHeaders(options?: { + forward?: BackstageCredentials; + }): Promise>; issueUserCookie(res: Response): Promise; } @@ -336,11 +347,11 @@ router.get('/cookie', async (req, res) => { res.json({ ok: true }); }); -// Allowing cookie auth is a separate step where you call the configure method +// Allowing cookie auth is a separate step where you call the addAuthPolicy method // of the httpRouter API in your plugin setup code. -httpRouter.configure({ - // In practice we can make this configuration a lot more capable, this is just a minimal example - allowCookieAuthOnPaths: ['/static'], // router.use('/static', cookieAuthMiddleware()) under the hood +httpRouter.addAuthPolicy({ + path: '/static', + allow: 'user-cookie', }); // Separate endpoint that serves static content, allowing user cookie auth as @@ -377,10 +388,11 @@ The release plan for the `HttpAuthService` is TBD, but is likely to be shipped a - [ ] Implement `AuthService` - [ ] Implement `HttpAuthService` - leave cookie auth as unimplemented for now -- [ ] Add `configure()` for `HttpRouterService`, using `HttpAuthService` +- [ ] Add `addAuthPolicy()` for `HttpRouterService`, using `HttpAuthService` - [ ] Implement a compatibility wrapper in `backend-common` that accepts `AuthService`, `HttpAuthService`, `IdentityService`, and `TokenManagerService` (all optional), and returns implementations for `AuthService` and `HttpAuthService`, such hat existing plugins can use a single `createRouter` implementation for both the old and new backend systems. - [ ] Implement `UserInfoService` in `@backstage/auth-node` - for now it will just extract the ownership entity refs from the token stored in the credentials - [ ] Implement cookie auth in `HttpAuthService` - just put the user token in the cookie for now +- [ ] Deprecate `IdentityService` and `TokenManagerService`, switch to using default factories that depend on the `AuthService` and `HttpAuthService`. Stop supplying implementations for these in `backend-defaults` and `backend-test-utils` - [ ] Migrate plugins: - [ ] Permission backend - [ ] TechDocs backend @@ -432,13 +444,13 @@ http.use(cookieRouter, { allow: ['user-cookie'] }); Similar to the previous approach, but also require that a path is provided. This removes much of the confusion around what middleware are applied. -The downside of this approach is that it still has the drawback of forcing a separation of the router, but at the same it provides very little benefit over a top-level path configuration approach like `http.configure()`. The `'/static'` path in the below example essentially has the exact same logic as `.configure({ cookieAuthPaths: ['/static'] })` since it'd be implemented in the same way. The `.configure()` approach has the benefit of allowing plugin authors to decide whether they want to keep the routes separate or not. +The downside of this approach is that it still has the drawback of forcing a separation of the router, but at the same it provides very little benefit over a top-level path configuration approach like `http.addAuthPolicy()`. The `'/static'` path in the below example essentially has the exact same logic as `.addAuthPolicy({ path: '/static', allow: 'user-cookie' })` since it'd be implemented in the same way. The `.addAuthPolicy()` approach has the benefit of allowing plugin authors to decide whether they want to keep the routes separate or not. -This does have the benefit of letting the framework know which exact routes are protected, which can be useful for introspection, although that benefit also applies to the `.configure()` approach. +This does have the benefit of letting the framework know which exact routes are protected, which can be useful for introspection, although that benefit also applies to the `.addAuthPolicy()` approach. ```ts -// This isn't too bad, but it's extremely similar to the configure() method since -// we're just matching on the path. The benefit of configure is that it allows you +// This isn't too bad, but it's extremely similar to the addAuthPolicy() method since +// we're just matching on the path. The benefit of addAuthPolicy is that it allows you // to keep everything in a singe router if desired. http.use('/static', cookieRouter, { allow: ['user-cookie'] }); ``` diff --git a/docs/features/software-templates/writing-custom-step-layouts.md b/docs/features/software-templates/writing-custom-step-layouts.md index b965f60f6b..d3c7c8fe82 100644 --- a/docs/features/software-templates/writing-custom-step-layouts.md +++ b/docs/features/software-templates/writing-custom-step-layouts.md @@ -20,11 +20,11 @@ The [createScaffolderLayout](https://backstage.io/docs/reference/plugin-scaffold ```ts import React from 'react'; +import { scaffolderPlugin } from '@backstage/plugin-scaffolder'; import { createScaffolderLayout, LayoutTemplate, - scaffolderPlugin, -} from '@backstage/plugin-scaffolder'; +} from '@backstage/plugin-scaffolder-react'; import { Grid } from '@material-ui/core'; const TwoColumn: LayoutTemplate = ({ properties, description, title }) => { diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index b07f556742..28d05c902b 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -1,6 +1,7 @@ app: experimental: packages: 'all' # ✨ + routes: bindings: catalog.viewTechDoc: techdocs.docRoot @@ -11,6 +12,7 @@ app: # - apis.plugin.graphiql.browse.gitlab: true - graphiql-endpoint:graphiql/gitlab: true + # Entity page cards - entity-card:catalog/about - entity-card:catalog/labels - entity-card:catalog/links: @@ -21,7 +23,15 @@ app: config: height: 300 - entity-card:azure-devops/readme + - entity-card:api-docs/has-apis + - entity-card:api-docs/consumed-apis + - entity-card:api-docs/provided-apis + - entity-card:api-docs/providing-components + - entity-card:api-docs/consuming-components + # Entity page content + - entity-content:api-docs/definition + - entity-content:api-docs/apis - entity-content:techdocs - entity-content:azure-devops/pipelines - entity-content:azure-devops/pull-requests diff --git a/packages/app/public/index.html b/packages/app/public/index.html index a3c3ef19b8..231e96b309 100644 --- a/packages/app/public/index.html +++ b/packages/app/public/index.html @@ -41,7 +41,7 @@ href="<%= publicPath %>/safari-pinned-tab.svg" color="#5bbad5" /> - <%= config.getString('app.title') %> + <%= config.getOptionalString('app.title') ?? 'Backstage' %> <% if (config.has('app.datadogRum')) { %>