diff --git a/src/operators/harbor/lib/consts.ts b/src/operators/harbor/lib/consts.ts index cae3e062..71447881 100644 --- a/src/operators/harbor/lib/consts.ts +++ b/src/operators/harbor/lib/consts.ts @@ -10,6 +10,8 @@ export const HARBOR_GROUP_TYPE = { http: 2, } +export const DEFAULT_OIDC_SCOPE = 'openid' +export const DEFAULT_OIDC_NAME = 'otomi' export const ROBOT_PREFIX = 'otomi-' export const SYSTEM_SECRET_NAME = 'harbor-robot-admin' export const PROJECT_PULL_SECRET_NAME = 'harbor-pullsecret' diff --git a/src/operators/harbor/lib/managers/harbor-oidc.test.ts b/src/operators/harbor/lib/managers/harbor-oidc.test.ts new file mode 100644 index 00000000..2c7959ba --- /dev/null +++ b/src/operators/harbor/lib/managers/harbor-oidc.test.ts @@ -0,0 +1,96 @@ +import { Configurations, ConfigureApi } from '@linode/harbor-client-fetch' +import { DEFAULT_OIDC_NAME, DEFAULT_OIDC_SCOPE } from '../consts' +import { HarborConfig } from '../types/oidc' +import manageHarborOidcConfig from './harbor-oidc' + +describe('manageHarborOidcConfig', () => { + const mockConfigureApi = { + updateConfigurations: jest.fn(), + } + + const configureApi = mockConfigureApi as unknown as ConfigureApi + + const expectConfigurations = (configurations: Partial) => + expect.objectContaining({ configurations: expect.objectContaining(configurations) }) + + const harborConfig = (overrides: Partial = {}): HarborConfig => + ({ + harborBaseRepoUrl: 'harbor.example.com', + harborUser: 'admin', + harborPassword: 'password', + oidcClientId: 'otomi', + oidcClientSecret: 'client-secret', + oidcEndpoint: 'https://idp.example.com', + oidcVerifyCert: true, + oidcUserClaim: 'email', + oidcAutoOnboard: true, + oidcGroupsClaim: 'groups', + oidcName: 'keycloak', + oidcScope: 'openid email profile groups', + teamNamespaces: [], + ...overrides, + }) as HarborConfig + + beforeEach(() => { + jest.clearAllMocks() + }) + + it('configures Harbor with the scope supplied in the configuration', async () => { + await manageHarborOidcConfig(configureApi, harborConfig()) + + expect(mockConfigureApi.updateConfigurations).toHaveBeenCalledWith( + expectConfigurations({ oidcScope: 'openid email profile groups' }), + ) + }) + + it('falls back to the default scope when no scope is supplied', async () => { + await manageHarborOidcConfig(configureApi, harborConfig({ oidcScope: undefined })) + + expect(mockConfigureApi.updateConfigurations).toHaveBeenCalledWith( + expectConfigurations({ oidcScope: DEFAULT_OIDC_SCOPE }), + ) + }) + + it('falls back to the default scope when an empty scope is supplied', async () => { + await manageHarborOidcConfig(configureApi, harborConfig({ oidcScope: '' })) + + expect(mockConfigureApi.updateConfigurations).toHaveBeenCalledWith( + expectConfigurations({ oidcScope: DEFAULT_OIDC_SCOPE }), + ) + }) + + it('configures Harbor with the identity provider name supplied in the configuration', async () => { + await manageHarborOidcConfig(configureApi, harborConfig({ oidcName: 'dex' })) + + expect(mockConfigureApi.updateConfigurations).toHaveBeenCalledWith(expectConfigurations({ oidcName: 'dex' })) + }) + + it('falls back to the default identity provider name when none is supplied', async () => { + await manageHarborOidcConfig(configureApi, harborConfig({ oidcName: '' })) + + expect(mockConfigureApi.updateConfigurations).toHaveBeenCalledWith( + expectConfigurations({ oidcName: DEFAULT_OIDC_NAME }), + ) + }) + + it('keeps the remaining configuration unchanged', async () => { + await manageHarborOidcConfig(configureApi, harborConfig()) + + expect(mockConfigureApi.updateConfigurations).toHaveBeenCalledWith( + expectConfigurations({ + authMode: 'oidc_auth', + oidcAdminGroup: 'platform-admin', + oidcClientId: 'otomi', + oidcClientSecret: 'client-secret', + oidcEndpoint: 'https://idp.example.com', + oidcGroupsClaim: 'groups', + oidcUserClaim: 'email', + oidcAutoOnboard: true, + oidcVerifyCert: true, + projectCreationRestriction: 'adminonly', + selfRegistration: false, + primaryAuthMode: true, + }), + ) + }) +}) diff --git a/src/operators/harbor/lib/managers/harbor-oidc.ts b/src/operators/harbor/lib/managers/harbor-oidc.ts index 3968c315..46e21256 100644 --- a/src/operators/harbor/lib/managers/harbor-oidc.ts +++ b/src/operators/harbor/lib/managers/harbor-oidc.ts @@ -1,6 +1,6 @@ import { Configurations, ConfigureApi } from '@linode/harbor-client-fetch' import { log } from 'console' -import { ROBOT_PREFIX } from '../consts' +import { DEFAULT_OIDC_NAME, DEFAULT_OIDC_SCOPE, ROBOT_PREFIX } from '../consts' import { HarborConfig } from '../types/oidc' export default async function manageHarborOidcConfig( @@ -14,8 +14,8 @@ export default async function manageHarborOidcConfig( oidcClientSecret: harborConfig.oidcClientSecret, oidcEndpoint: harborConfig.oidcEndpoint, oidcGroupsClaim: 'groups', - oidcName: 'otomi', - oidcScope: 'openid', + oidcName: harborConfig.oidcName || DEFAULT_OIDC_NAME, + oidcScope: harborConfig.oidcScope || DEFAULT_OIDC_SCOPE, oidcVerifyCert: harborConfig.oidcVerifyCert, oidcUserClaim: harborConfig.oidcUserClaim, oidcAutoOnboard: harborConfig.oidcAutoOnboard, diff --git a/src/operators/harbor/lib/types/oidc.ts b/src/operators/harbor/lib/types/oidc.ts index 8df84fd1..0bd8835d 100644 --- a/src/operators/harbor/lib/types/oidc.ts +++ b/src/operators/harbor/lib/types/oidc.ts @@ -14,7 +14,7 @@ export interface HarborConfigMapData { oidcUserClaim: string oidcGroupsClaim: string oidcName: string - oidcScope: string + oidcScope?: string oidcVerifyCert: boolean teamNamespaces: string[] } @@ -39,7 +39,6 @@ export function validateConfigMapData(configMap: V1ConfigMap): HarborConfigMapDa 'oidcUserClaim', 'oidcGroupsClaim', 'oidcName', - 'oidcScope', 'oidcVerifyCert', 'teamNamespaces', ] @@ -71,7 +70,7 @@ export class HarborConfig { oidcAutoOnboard: boolean oidcGroupsClaim: string oidcName: string - oidcScope: string + oidcScope?: string teamNamespaces: string[] constructor(secretData: HarborSecretData, configMapData: HarborConfigMapData) {