Merge pull request #7943 from backstage/natasha/b2b-auth
Support backend to backend authentication
This commit is contained in:
@@ -31,6 +31,7 @@ import { ResourceEntityV1alpha1 } from '@backstage/catalog-model';
|
||||
import { Router } from 'express';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
import { Validators } from '@backstage/catalog-model';
|
||||
|
||||
@@ -756,8 +757,10 @@ export class DefaultCatalogCollator implements DocumentCollator {
|
||||
locationTemplate,
|
||||
filter,
|
||||
catalogClient,
|
||||
tokenManager,
|
||||
}: {
|
||||
discovery: PluginEndpointDiscovery;
|
||||
tokenManager: TokenManager;
|
||||
locationTemplate?: string;
|
||||
filter?: CatalogEntitiesRequest['filter'];
|
||||
catalogClient?: CatalogApi;
|
||||
@@ -780,12 +783,15 @@ export class DefaultCatalogCollator implements DocumentCollator {
|
||||
_config: Config,
|
||||
options: {
|
||||
discovery: PluginEndpointDiscovery;
|
||||
tokenManager: TokenManager;
|
||||
filter?: CatalogEntitiesRequest['filter'];
|
||||
},
|
||||
): DefaultCatalogCollator;
|
||||
// (undocumented)
|
||||
protected locationTemplate: string;
|
||||
// (undocumented)
|
||||
protected tokenManager: TokenManager;
|
||||
// (undocumented)
|
||||
readonly type: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { DefaultCatalogCollator } from './DefaultCatalogCollator';
|
||||
import { setupServer } from 'msw/node';
|
||||
@@ -55,6 +58,7 @@ const expectedEntities: Entity[] = [
|
||||
|
||||
describe('DefaultCatalogCollator', () => {
|
||||
let mockDiscoveryApi: jest.Mocked<PluginEndpointDiscovery>;
|
||||
let mockTokenManager: jest.Mocked<TokenManager>;
|
||||
let collator: DefaultCatalogCollator;
|
||||
|
||||
beforeAll(() => {
|
||||
@@ -62,7 +66,14 @@ describe('DefaultCatalogCollator', () => {
|
||||
getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007'),
|
||||
getExternalBaseUrl: jest.fn(),
|
||||
};
|
||||
collator = new DefaultCatalogCollator({ discovery: mockDiscoveryApi });
|
||||
mockTokenManager = {
|
||||
getToken: jest.fn().mockResolvedValue({ token: '' }),
|
||||
authenticate: jest.fn(),
|
||||
};
|
||||
collator = new DefaultCatalogCollator({
|
||||
discovery: mockDiscoveryApi,
|
||||
tokenManager: mockTokenManager,
|
||||
});
|
||||
server.listen();
|
||||
});
|
||||
beforeEach(() => {
|
||||
@@ -118,6 +129,7 @@ describe('DefaultCatalogCollator', () => {
|
||||
// Provide an alternate location template.
|
||||
collator = new DefaultCatalogCollator({
|
||||
discovery: mockDiscoveryApi,
|
||||
tokenManager: mockTokenManager,
|
||||
locationTemplate: '/software/:name',
|
||||
});
|
||||
|
||||
@@ -131,6 +143,7 @@ describe('DefaultCatalogCollator', () => {
|
||||
// Provide an alternate location template.
|
||||
collator = DefaultCatalogCollator.fromConfig(new ConfigReader({}), {
|
||||
discovery: mockDiscoveryApi,
|
||||
tokenManager: mockTokenManager,
|
||||
filter: {
|
||||
kind: ['Foo', 'Bar'],
|
||||
},
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { Entity, UserEntity } from '@backstage/catalog-model';
|
||||
import { IndexableDocument, DocumentCollator } from '@backstage/search-common';
|
||||
import { Config } from '@backstage/config';
|
||||
@@ -38,11 +41,13 @@ export class DefaultCatalogCollator implements DocumentCollator {
|
||||
protected filter?: CatalogEntitiesRequest['filter'];
|
||||
protected readonly catalogClient: CatalogApi;
|
||||
public readonly type: string = 'software-catalog';
|
||||
protected tokenManager: TokenManager;
|
||||
|
||||
static fromConfig(
|
||||
_config: Config,
|
||||
options: {
|
||||
discovery: PluginEndpointDiscovery;
|
||||
tokenManager: TokenManager;
|
||||
filter?: CatalogEntitiesRequest['filter'];
|
||||
},
|
||||
) {
|
||||
@@ -56,8 +61,10 @@ export class DefaultCatalogCollator implements DocumentCollator {
|
||||
locationTemplate,
|
||||
filter,
|
||||
catalogClient,
|
||||
tokenManager,
|
||||
}: {
|
||||
discovery: PluginEndpointDiscovery;
|
||||
tokenManager: TokenManager;
|
||||
locationTemplate?: string;
|
||||
filter?: CatalogEntitiesRequest['filter'];
|
||||
catalogClient?: CatalogApi;
|
||||
@@ -68,6 +75,7 @@ export class DefaultCatalogCollator implements DocumentCollator {
|
||||
this.filter = filter;
|
||||
this.catalogClient =
|
||||
catalogClient || new CatalogClient({ discoveryApi: discovery });
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
protected applyArgsToFormat(
|
||||
@@ -100,9 +108,13 @@ export class DefaultCatalogCollator implements DocumentCollator {
|
||||
}
|
||||
|
||||
async execute() {
|
||||
const response = await this.catalogClient.getEntities({
|
||||
filter: this.filter,
|
||||
});
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const response = await this.catalogClient.getEntities(
|
||||
{
|
||||
filter: this.filter,
|
||||
},
|
||||
{ token },
|
||||
);
|
||||
return response.items.map((entity: Entity): CatalogEntityDocument => {
|
||||
return {
|
||||
title: entity.metadata.title ?? entity.metadata.name,
|
||||
|
||||
@@ -14,6 +14,7 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import { PreparerBuilder } from '@backstage/techdocs-common';
|
||||
import { PublisherBase } from '@backstage/techdocs-common';
|
||||
import { TechDocsDocument } from '@backstage/techdocs-common';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "RouterOptions" needs to be exported by the entry point index.d.ts
|
||||
// Warning: (ae-missing-release-tag) "createRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
@@ -31,6 +32,7 @@ export class DefaultTechDocsCollator implements DocumentCollator {
|
||||
locationTemplate,
|
||||
logger,
|
||||
catalogClient,
|
||||
tokenManager,
|
||||
parallelismLimit,
|
||||
legacyPathCasing,
|
||||
}: TechDocsCollatorOptions);
|
||||
@@ -60,6 +62,7 @@ export class DefaultTechDocsCollator implements DocumentCollator {
|
||||
export type TechDocsCollatorOptions = {
|
||||
discovery: PluginEndpointDiscovery;
|
||||
logger: Logger_2;
|
||||
tokenManager: TokenManager;
|
||||
locationTemplate?: string;
|
||||
catalogClient?: CatalogApi;
|
||||
parallelismLimit?: number;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
getVoidLogger,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { DefaultTechDocsCollator } from './DefaultTechDocsCollator';
|
||||
@@ -87,6 +88,7 @@ const expectedEntities: Entity[] = [
|
||||
|
||||
describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => {
|
||||
let mockDiscoveryApi: jest.Mocked<PluginEndpointDiscovery>;
|
||||
let mockTokenManager: jest.Mocked<TokenManager>;
|
||||
let collator: DefaultTechDocsCollator;
|
||||
|
||||
const worker = setupServer();
|
||||
@@ -96,6 +98,10 @@ describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => {
|
||||
getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'),
|
||||
getExternalBaseUrl: jest.fn(),
|
||||
};
|
||||
mockTokenManager = {
|
||||
getToken: jest.fn().mockResolvedValue({ token: '' }),
|
||||
authenticate: jest.fn(),
|
||||
};
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
legacyUseCaseSensitiveTripletPaths: true,
|
||||
@@ -103,6 +109,7 @@ describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => {
|
||||
});
|
||||
collator = DefaultTechDocsCollator.fromConfig(mockConfig, {
|
||||
discovery: mockDiscoveryApi,
|
||||
tokenManager: mockTokenManager,
|
||||
logger,
|
||||
legacyPathCasing: true,
|
||||
});
|
||||
@@ -147,6 +154,7 @@ describe('DefaultTechDocsCollator with legacyPathCasing configuration', () => {
|
||||
|
||||
describe('DefaultTechDocsCollator', () => {
|
||||
let mockDiscoveryApi: jest.Mocked<PluginEndpointDiscovery>;
|
||||
let mockTokenManager: jest.Mocked<TokenManager>;
|
||||
let collator: DefaultTechDocsCollator;
|
||||
|
||||
const worker = setupServer();
|
||||
@@ -156,8 +164,13 @@ describe('DefaultTechDocsCollator', () => {
|
||||
getBaseUrl: jest.fn().mockResolvedValue('http://test-backend'),
|
||||
getExternalBaseUrl: jest.fn(),
|
||||
};
|
||||
mockTokenManager = {
|
||||
getToken: jest.fn().mockResolvedValue({ token: '' }),
|
||||
authenticate: jest.fn(),
|
||||
};
|
||||
collator = DefaultTechDocsCollator.fromConfig(new ConfigReader({}), {
|
||||
discovery: mockDiscoveryApi,
|
||||
tokenManager: mockTokenManager,
|
||||
logger,
|
||||
});
|
||||
|
||||
@@ -195,6 +208,7 @@ describe('DefaultTechDocsCollator', () => {
|
||||
// Provide an alternate location template.
|
||||
collator = new DefaultTechDocsCollator({
|
||||
discovery: mockDiscoveryApi,
|
||||
tokenManager: mockTokenManager,
|
||||
locationTemplate: '/software/:name',
|
||||
logger,
|
||||
});
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import { DocumentCollator } from '@backstage/search-common';
|
||||
import fetch from 'node-fetch';
|
||||
@@ -34,6 +37,7 @@ interface MkSearchIndexDoc {
|
||||
export type TechDocsCollatorOptions = {
|
||||
discovery: PluginEndpointDiscovery;
|
||||
logger: Logger;
|
||||
tokenManager: TokenManager;
|
||||
locationTemplate?: string;
|
||||
catalogClient?: CatalogApi;
|
||||
parallelismLimit?: number;
|
||||
@@ -51,6 +55,7 @@ export class DefaultTechDocsCollator implements DocumentCollator {
|
||||
protected locationTemplate: string;
|
||||
private readonly logger: Logger;
|
||||
private readonly catalogClient: CatalogApi;
|
||||
private readonly tokenManager: TokenManager;
|
||||
private readonly parallelismLimit: number;
|
||||
private readonly legacyPathCasing: boolean;
|
||||
public readonly type: string = 'techdocs';
|
||||
@@ -63,6 +68,7 @@ export class DefaultTechDocsCollator implements DocumentCollator {
|
||||
locationTemplate,
|
||||
logger,
|
||||
catalogClient,
|
||||
tokenManager,
|
||||
parallelismLimit = 10,
|
||||
legacyPathCasing = false,
|
||||
}: TechDocsCollatorOptions) {
|
||||
@@ -74,6 +80,7 @@ export class DefaultTechDocsCollator implements DocumentCollator {
|
||||
catalogClient || new CatalogClient({ discoveryApi: discovery });
|
||||
this.parallelismLimit = parallelismLimit;
|
||||
this.legacyPathCasing = legacyPathCasing;
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
static fromConfig(config: Config, options: TechDocsCollatorOptions) {
|
||||
@@ -87,19 +94,23 @@ export class DefaultTechDocsCollator implements DocumentCollator {
|
||||
async execute() {
|
||||
const limit = pLimit(this.parallelismLimit);
|
||||
const techDocsBaseUrl = await this.discovery.getBaseUrl('techdocs');
|
||||
const entities = await this.catalogClient.getEntities({
|
||||
fields: [
|
||||
'kind',
|
||||
'namespace',
|
||||
'metadata.annotations',
|
||||
'metadata.name',
|
||||
'metadata.title',
|
||||
'metadata.namespace',
|
||||
'spec.type',
|
||||
'spec.lifecycle',
|
||||
'relations',
|
||||
],
|
||||
});
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const entities = await this.catalogClient.getEntities(
|
||||
{
|
||||
fields: [
|
||||
'kind',
|
||||
'namespace',
|
||||
'metadata.annotations',
|
||||
'metadata.name',
|
||||
'metadata.title',
|
||||
'metadata.namespace',
|
||||
'spec.type',
|
||||
'spec.lifecycle',
|
||||
'relations',
|
||||
],
|
||||
},
|
||||
{ token },
|
||||
);
|
||||
const docPromises = entities.items
|
||||
.filter(it => it.metadata?.annotations?.['backstage.io/techdocs-ref'])
|
||||
.map((entity: Entity) =>
|
||||
|
||||
Reference in New Issue
Block a user