Use server token for catalog identity client
Signed-off-by: Joe Porpeglia <josephp@spotify.com>
This commit is contained in:
@@ -23,6 +23,13 @@ export default async function createPlugin({
|
||||
database,
|
||||
config,
|
||||
discovery,
|
||||
tokenManager,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
return await createRouter({ logger, config, database, discovery });
|
||||
return await createRouter({
|
||||
logger,
|
||||
config,
|
||||
database,
|
||||
discovery,
|
||||
tokenManager,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export default async function createPlugin({
|
||||
database,
|
||||
config,
|
||||
discovery,
|
||||
tokenManager,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
return await createRouter({ logger, config, database, discovery });
|
||||
return await createRouter({ logger, config, database, discovery, tokenManager });
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import {
|
||||
RELATION_MEMBER_OF,
|
||||
UserEntity,
|
||||
UserEntityV1alpha1,
|
||||
} from '@backstage/catalog-model';
|
||||
import { TokenIssuer } from '../../identity';
|
||||
import { CatalogIdentityClient } from './CatalogIdentityClient';
|
||||
|
||||
describe('CatalogIdentityClient', () => {
|
||||
@@ -36,19 +36,19 @@ describe('CatalogIdentityClient', () => {
|
||||
refreshEntity: jest.fn(),
|
||||
getEntityAncestors: jest.fn(),
|
||||
};
|
||||
const tokenIssuer: jest.Mocked<TokenIssuer> = {
|
||||
issueToken: jest.fn(),
|
||||
listPublicKeys: jest.fn(),
|
||||
const tokenManager: jest.Mocked<TokenManager> = {
|
||||
getToken: jest.fn(),
|
||||
authenticate: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => jest.resetAllMocks());
|
||||
|
||||
it('findUser passes through the correct search params', async () => {
|
||||
catalogApi.getEntities.mockResolvedValueOnce({ items: [{} as UserEntity] });
|
||||
tokenIssuer.issueToken.mockResolvedValue('my-token');
|
||||
tokenManager.getToken.mockResolvedValue({ token: 'my-token' });
|
||||
const client = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
await client.findUser({ annotations: { key: 'value' } });
|
||||
@@ -62,11 +62,7 @@ describe('CatalogIdentityClient', () => {
|
||||
},
|
||||
{ token: 'my-token' },
|
||||
);
|
||||
expect(tokenIssuer.issueToken).toHaveBeenCalledWith({
|
||||
claims: {
|
||||
sub: 'backstage.io/auth-backend',
|
||||
},
|
||||
});
|
||||
expect(tokenManager.getToken).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('resolveCatalogMembership resolves membership', async () => {
|
||||
@@ -114,35 +110,39 @@ describe('CatalogIdentityClient', () => {
|
||||
},
|
||||
];
|
||||
catalogApi.getEntities.mockResolvedValueOnce({ items: mockUsers });
|
||||
tokenManager.getToken.mockResolvedValue({ token: 'my-token' });
|
||||
|
||||
const client = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const claims = await client.resolveCatalogMembership({
|
||||
entityRefs: ['inigom', 'User:default/imontoya', 'User:reality/mpatinkin'],
|
||||
});
|
||||
|
||||
expect(catalogApi.getEntities).toHaveBeenCalledWith({
|
||||
filter: [
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'default',
|
||||
'metadata.name': 'inigom',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'default',
|
||||
'metadata.name': 'imontoya',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'reality',
|
||||
'metadata.name': 'mpatinkin',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(catalogApi.getEntities).toHaveBeenCalledWith(
|
||||
{
|
||||
filter: [
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'default',
|
||||
'metadata.name': 'inigom',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'default',
|
||||
'metadata.name': 'imontoya',
|
||||
},
|
||||
{
|
||||
kind: 'user',
|
||||
'metadata.namespace': 'reality',
|
||||
'metadata.name': 'mpatinkin',
|
||||
},
|
||||
],
|
||||
},
|
||||
{ token: 'my-token' },
|
||||
);
|
||||
|
||||
expect(claims).toMatchObject([
|
||||
'user:default/inigom',
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
stringifyEntityRef,
|
||||
UserEntity,
|
||||
} from '@backstage/catalog-model';
|
||||
import { TokenIssuer } from '../../identity';
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
|
||||
type UserQuery = {
|
||||
annotations: Record<string, string>;
|
||||
@@ -40,11 +40,11 @@ type MemberClaimQuery = {
|
||||
*/
|
||||
export class CatalogIdentityClient {
|
||||
private readonly catalogApi: CatalogApi;
|
||||
private readonly tokenIssuer: TokenIssuer;
|
||||
private readonly tokenManager: TokenManager;
|
||||
|
||||
constructor(options: { catalogApi: CatalogApi; tokenIssuer: TokenIssuer }) {
|
||||
constructor(options: { catalogApi: CatalogApi; tokenManager: TokenManager }) {
|
||||
this.catalogApi = options.catalogApi;
|
||||
this.tokenIssuer = options.tokenIssuer;
|
||||
this.tokenManager = options.tokenManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,9 +61,7 @@ export class CatalogIdentityClient {
|
||||
}
|
||||
|
||||
// TODO(Rugvip): cache the token
|
||||
const token = await this.tokenIssuer.issueToken({
|
||||
claims: { sub: 'backstage.io/auth-backend' },
|
||||
});
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const { items } = await this.catalogApi.getEntities({ filter }, { token });
|
||||
|
||||
if (items.length !== 1) {
|
||||
@@ -106,8 +104,9 @@ export class CatalogIdentityClient {
|
||||
'metadata.namespace': ref.namespace,
|
||||
'metadata.name': ref.name,
|
||||
}));
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
const entities = await this.catalogApi
|
||||
.getEntities({ filter })
|
||||
.getEntities({ filter }, { token })
|
||||
.then(r => r.items);
|
||||
|
||||
if (entityRefs.length !== entities.length) {
|
||||
|
||||
@@ -197,6 +197,7 @@ export const createAtlassianProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -208,7 +209,7 @@ export const createAtlassianProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> =
|
||||
|
||||
@@ -220,6 +220,7 @@ export const createAuth0Provider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -231,7 +232,7 @@ export const createAuth0Provider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -241,7 +241,7 @@ export type AwsAlbProviderOptions = {
|
||||
export const createAwsAlbProvider = (
|
||||
options?: AwsAlbProviderOptions,
|
||||
): AuthProviderFactory => {
|
||||
return ({ config, tokenIssuer, catalogApi, logger }) => {
|
||||
return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
|
||||
const region = config.getString('region');
|
||||
const issuer = config.getOptionalString('iss');
|
||||
|
||||
@@ -253,7 +253,7 @@ export const createAwsAlbProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<AwsAlbResult> = options?.authHandler
|
||||
|
||||
@@ -273,6 +273,7 @@ export const createBitbucketProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -283,7 +284,7 @@ export const createBitbucketProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<BitbucketOAuthResult> =
|
||||
|
||||
@@ -102,7 +102,7 @@ export class GcpIapProvider implements AuthProviderRouteHandlers {
|
||||
export function createGcpIapProvider(
|
||||
options: GcpIapProviderOptions,
|
||||
): AuthProviderFactory {
|
||||
return ({ config, tokenIssuer, catalogApi, logger }) => {
|
||||
return ({ config, tokenIssuer, catalogApi, logger, tokenManager }) => {
|
||||
const audience = config.getString('audience');
|
||||
|
||||
const authHandler = options.authHandler ?? defaultAuthHandler;
|
||||
@@ -111,7 +111,7 @@ export function createGcpIapProvider(
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
return new GcpIapProvider({
|
||||
|
||||
@@ -245,6 +245,7 @@ export const createGithubProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -270,7 +271,7 @@ export const createGithubProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<GithubOAuthResult> = options?.authHandler
|
||||
|
||||
@@ -227,6 +227,7 @@ export const createGitlabProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -239,7 +240,7 @@ export const createGitlabProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> =
|
||||
|
||||
@@ -259,6 +259,7 @@ export const createGoogleProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -269,7 +270,7 @@ export const createGoogleProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -267,6 +267,7 @@ export const createMicrosoftProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -281,7 +282,7 @@ export const createMicrosoftProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -182,12 +182,12 @@ export const createOauth2ProxyProvider =
|
||||
<JWTPayload>(
|
||||
options: Oauth2ProxyProviderOptions<JWTPayload>,
|
||||
): AuthProviderFactory =>
|
||||
({ catalogApi, logger, tokenIssuer }) => {
|
||||
({ catalogApi, logger, tokenIssuer, tokenManager }) => {
|
||||
const signInResolver = options.signIn.resolver;
|
||||
const authHandler = options.authHandler;
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
return new Oauth2ProxyAuthProvider<JWTPayload>({
|
||||
logger,
|
||||
|
||||
@@ -233,6 +233,7 @@ export const createOAuth2Provider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -249,7 +250,7 @@ export const createOAuth2Provider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -257,6 +257,7 @@ export const createOidcProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -272,7 +273,7 @@ export const createOidcProvider = (
|
||||
const prompt = envConfig.getOptionalString('prompt');
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OidcAuthResult> = options?.authHandler
|
||||
|
||||
@@ -268,6 +268,7 @@ export const createOktaProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -286,7 +287,7 @@ export const createOktaProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = _options?.authHandler
|
||||
|
||||
@@ -219,6 +219,7 @@ export const createOneLoginProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) =>
|
||||
@@ -230,7 +231,7 @@ export const createOneLoginProvider = (
|
||||
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<OAuthResult> = options?.authHandler
|
||||
|
||||
@@ -187,12 +187,13 @@ export const createSamlProvider = (
|
||||
globalConfig,
|
||||
config,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
catalogApi,
|
||||
logger,
|
||||
}) => {
|
||||
const catalogIdentityClient = new CatalogIdentityClient({
|
||||
catalogApi,
|
||||
tokenIssuer,
|
||||
tokenManager,
|
||||
});
|
||||
|
||||
const authHandler: AuthHandler<SamlAuthResult> = options?.authHandler
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
import {
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Config } from '@backstage/config';
|
||||
@@ -124,6 +127,7 @@ export type AuthProviderFactoryOptions = {
|
||||
globalConfig: AuthProviderConfig;
|
||||
config: Config;
|
||||
logger: Logger;
|
||||
tokenManager: TokenManager;
|
||||
tokenIssuer: TokenIssuer;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
catalogApi: CatalogApi;
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import {
|
||||
PluginDatabaseManager,
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { assertError, NotFoundError } from '@backstage/errors';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
@@ -41,13 +42,21 @@ export interface RouterOptions {
|
||||
database: PluginDatabaseManager;
|
||||
config: Config;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
tokenManager: TokenManager;
|
||||
providerFactories?: ProviderFactories;
|
||||
}
|
||||
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { logger, config, discovery, database, providerFactories } = options;
|
||||
const {
|
||||
logger,
|
||||
config,
|
||||
discovery,
|
||||
database,
|
||||
tokenManager,
|
||||
providerFactories,
|
||||
} = options;
|
||||
const router = Router();
|
||||
|
||||
const appUrl = config.getString('app.baseUrl');
|
||||
@@ -105,6 +114,7 @@ export async function createRouter(
|
||||
globalConfig: { baseUrl: authUrl, appUrl, isOriginAllowed },
|
||||
config: providersConfig.getConfig(providerId),
|
||||
logger,
|
||||
tokenManager,
|
||||
tokenIssuer,
|
||||
discovery,
|
||||
catalogApi,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import {
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
ServerTokenManager,
|
||||
SingleHostDiscovery,
|
||||
useHotMemoize,
|
||||
} from '@backstage/backend-common';
|
||||
@@ -58,6 +59,7 @@ export async function startStandaloneServer(
|
||||
},
|
||||
},
|
||||
discovery,
|
||||
tokenManager: ServerTokenManager.noop(),
|
||||
});
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
|
||||
Reference in New Issue
Block a user