enable custom auth metadata in config
Signed-off-by: Jamie Klassen <jamie.klassen@broadcom.com>
This commit is contained in:
+7
-4
@@ -46,13 +46,16 @@ export interface Config {
|
||||
/** @visibility secret */
|
||||
serviceAccountToken?: string;
|
||||
/** @visibility frontend */
|
||||
authProvider:
|
||||
authProvider?:
|
||||
| 'aks'
|
||||
| 'aws'
|
||||
| 'google'
|
||||
| 'serviceAccount'
|
||||
| 'azure'
|
||||
| 'google'
|
||||
| 'googleServiceAccount'
|
||||
| 'oidc'
|
||||
| 'googleServiceAccount';
|
||||
| 'serviceAccount';
|
||||
/** @visibility secret */
|
||||
authMetadata?: object;
|
||||
/** @visibility frontend */
|
||||
oidcTokenProvider?: string;
|
||||
/** @visibility frontend */
|
||||
|
||||
@@ -177,6 +177,67 @@ describe('ConfigClusterLocator', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('reads custom authMetadata', async () => {
|
||||
const config: Config = new ConfigReader({
|
||||
clusters: [
|
||||
{
|
||||
name: 'cluster',
|
||||
url: 'http://url',
|
||||
authProvider: 'authProvider',
|
||||
authMetadata: { 'custom-key': 'custom-value' },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await ConfigClusterLocator.fromConfig(
|
||||
config,
|
||||
authStrategy,
|
||||
).getClusters();
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
authMetadata: expect.objectContaining({ 'custom-key': 'custom-value' }),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('reads authProvider from metadata block', async () => {
|
||||
const config: Config = new ConfigReader({
|
||||
clusters: [
|
||||
{
|
||||
name: 'cluster',
|
||||
url: 'http://url',
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'serviceAccount',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await ConfigClusterLocator.fromConfig(
|
||||
config,
|
||||
authStrategy,
|
||||
).getClusters();
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: 'serviceAccount',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('forbids cluster without auth provider', () => {
|
||||
const config: Config = new ConfigReader({
|
||||
clusters: [{ name: 'cluster', url: 'http://url' }],
|
||||
});
|
||||
|
||||
expect(() => ConfigClusterLocator.fromConfig(config, authStrategy)).toThrow(
|
||||
'Missing required config value',
|
||||
);
|
||||
});
|
||||
|
||||
it('one cluster with dashboardParameters', async () => {
|
||||
const config: Config = new ConfigReader({
|
||||
clusters: [
|
||||
|
||||
@@ -37,7 +37,12 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier {
|
||||
): ConfigClusterLocator {
|
||||
return new ConfigClusterLocator(
|
||||
config.getConfigArray('clusters').map(c => {
|
||||
const authProvider = c.getString('authProvider');
|
||||
const authMetadataBlock = c.getOptional<{
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]?: string;
|
||||
}>('authMetadata');
|
||||
const authProvider =
|
||||
authMetadataBlock?.[ANNOTATION_KUBERNETES_AUTH_PROVIDER] ??
|
||||
c.getString('authProvider');
|
||||
const clusterDetails: ClusterDetails = {
|
||||
name: c.getString('name'),
|
||||
url: c.getString('url'),
|
||||
@@ -48,6 +53,7 @@ export class ConfigClusterLocator implements KubernetesClustersSupplier {
|
||||
authMetadata: {
|
||||
[ANNOTATION_KUBERNETES_AUTH_PROVIDER]: authProvider,
|
||||
...ConfigClusterLocator.parseAuthMetadata(c),
|
||||
...authMetadataBlock,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import {
|
||||
ANNOTATION_KUBERNETES_AUTH_PROVIDER,
|
||||
ANNOTATION_KUBERNETES_OIDC_TOKEN_PROVIDER,
|
||||
ObjectsByEntityResponse,
|
||||
KubernetesRequestAuth,
|
||||
} from '@backstage/plugin-kubernetes-common';
|
||||
import request from 'supertest';
|
||||
@@ -39,10 +38,7 @@ import {
|
||||
startTestBackend,
|
||||
} from '@backstage/backend-test-utils';
|
||||
import { rest } from 'msw';
|
||||
import {
|
||||
AuthorizeResult,
|
||||
PermissionEvaluator,
|
||||
} from '@backstage/plugin-permission-common';
|
||||
import { AuthorizeResult } from '@backstage/plugin-permission-common';
|
||||
import {
|
||||
PermissionsService,
|
||||
createBackendModule,
|
||||
@@ -57,9 +53,9 @@ import {
|
||||
} from '@backstage/plugin-kubernetes-node';
|
||||
import { ExtendedHttpServer } from '@backstage/backend-app-api';
|
||||
|
||||
describe('KubernetesBuilder', () => {
|
||||
describe('API integration tests', () => {
|
||||
let app: ExtendedHttpServer;
|
||||
let objectsProviderMock: KubernetesObjectsProvider;
|
||||
let objectsProviderMock: jest.Mocked<KubernetesObjectsProvider>;
|
||||
const happyK8SResult = {
|
||||
items: [{ clusterOne: { pods: [{ metadata: { name: 'pod1' } }] } }],
|
||||
};
|
||||
@@ -530,52 +526,116 @@ metadata:
|
||||
expect(response.body).toStrictEqual({ items: [] });
|
||||
});
|
||||
|
||||
it('should not permit custom auth strategies with dashes', () => {
|
||||
const throwError = () =>
|
||||
startTestBackend({
|
||||
features: [
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testAuthStrategy',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesAuthStrategyExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addAuthStrategy('custom-strategy', {
|
||||
getCredential: jest
|
||||
.fn<
|
||||
Promise<KubernetesCredential>,
|
||||
[ClusterDetails, KubernetesRequestAuth]
|
||||
>()
|
||||
.mockResolvedValue({ type: 'anonymous' }),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
});
|
||||
it('reads custom auth metadata from config', async () => {
|
||||
const authStrategy = {
|
||||
getCredential: jest.fn().mockResolvedValue({ type: 'anonymous' }),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
};
|
||||
worker.use(
|
||||
rest.get('http://my.cluster/api', (_req, res, ctx) =>
|
||||
res(ctx.json({})),
|
||||
),
|
||||
);
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
kubernetes: {
|
||||
serviceLocatorMethod: { type: 'multiTenant' },
|
||||
clusterLocatorMethods: [
|
||||
{
|
||||
type: 'config',
|
||||
clusters: [
|
||||
{
|
||||
name: 'cluster',
|
||||
url: 'http://my.cluster',
|
||||
authProvider: 'custom',
|
||||
authMetadata: { 'custom-key': 'custom-value' },
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
],
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
return expect(throwError).rejects.toThrow(
|
||||
'Strategy name can not include dashes',
|
||||
},
|
||||
}),
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testAuthStrategy',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesAuthStrategyExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addAuthStrategy('custom', authStrategy);
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
app = server;
|
||||
|
||||
const proxyEndpointRequest = request(app).get(
|
||||
'/api/kubernetes/proxy/api',
|
||||
);
|
||||
worker.use(rest.all(proxyEndpointRequest.url, req => req.passthrough()));
|
||||
const response = await proxyEndpointRequest;
|
||||
|
||||
expect(response.body).toStrictEqual({});
|
||||
expect(authStrategy.getCredential).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
authMetadata: expect.objectContaining({
|
||||
'custom-key': 'custom-value',
|
||||
}),
|
||||
}),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get /.well-known/backstage/permissions/metadata', () => {
|
||||
it('lists permissions supported by the kubernetes plugin', async () => {
|
||||
const response = await request(app).get(
|
||||
'/api/kubernetes/.well-known/backstage/permissions/metadata',
|
||||
);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toMatchObject({
|
||||
permissions: [
|
||||
{ type: 'basic', name: 'kubernetes.proxy', attributes: {} },
|
||||
it('forbids custom auth strategies with dashes', () => {
|
||||
const throwError = () =>
|
||||
startTestBackend({
|
||||
features: [
|
||||
import('@backstage/plugin-kubernetes-backend/alpha'),
|
||||
createBackendModule({
|
||||
pluginId: 'kubernetes',
|
||||
moduleId: 'testAuthStrategy',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: { extension: kubernetesAuthStrategyExtensionPoint },
|
||||
async init({ extension }) {
|
||||
extension.addAuthStrategy('custom-strategy', {
|
||||
getCredential: jest
|
||||
.fn<
|
||||
Promise<KubernetesCredential>,
|
||||
[ClusterDetails, KubernetesRequestAuth]
|
||||
>()
|
||||
.mockResolvedValue({ type: 'anonymous' }),
|
||||
validateCluster: jest.fn().mockReturnValue([]),
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
rules: [],
|
||||
});
|
||||
return expect(throwError).rejects.toThrow(
|
||||
'Strategy name can not include dashes',
|
||||
);
|
||||
});
|
||||
|
||||
it('serves permission integration endpoint', async () => {
|
||||
const response = await request(app).get(
|
||||
'/api/kubernetes/.well-known/backstage/permissions/metadata',
|
||||
);
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toMatchObject({
|
||||
permissions: [
|
||||
{ type: 'basic', name: 'kubernetes.proxy', attributes: {} },
|
||||
],
|
||||
rules: [],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user