mirror of
https://github.com/docker/login-action.git
synced 2026-05-23 18:35:56 +00:00
Add Chainguard registry (cgr.dev) login support
Implement native OIDC-based authentication for Chainguard's container registry, following the same pattern as the existing AWS ECR integration. When registry is set to cgr.dev, the action automatically exchanges a GitHub Actions OIDC token with Chainguard's STS endpoint for a short-lived registry credential, removing the need for chainctl or long-lived pull tokens. New inputs: chainguard (auto/true/false), chainguard-identity. Signed-off-by: Augustus Nguyen <theflash28012002@gmail.com>
This commit is contained in:
parent
4a8376e001
commit
4bcfaae325
9 changed files with 11038 additions and 5741 deletions
99
__tests__/chainguard.test.ts
Normal file
99
__tests__/chainguard.test.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import {beforeEach, describe, expect, test, vi} from 'vitest';
|
||||
|
||||
import * as chainguard from '../src/chainguard.js';
|
||||
|
||||
describe('isChainguard', () => {
|
||||
test.each([
|
||||
['cgr.dev', true],
|
||||
['registry.gitlab.com', false],
|
||||
['gcr.io', false],
|
||||
['docker.io', false],
|
||||
['ghcr.io', false],
|
||||
['public.ecr.aws', false],
|
||||
['012345678901.dkr.ecr.eu-west-3.amazonaws.com', false],
|
||||
['not-cgr.dev', false],
|
||||
['cgr.dev.example.com', false]
|
||||
])('given registry %p returns %p', (registry, expected) => {
|
||||
expect(chainguard.isChainguard(registry)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
const mockGetIDToken = vi.fn();
|
||||
vi.mock('@actions/core', () => ({
|
||||
info: vi.fn(),
|
||||
setSecret: vi.fn(),
|
||||
getIDToken: (...args: unknown[]) => mockGetIDToken(...args)
|
||||
}));
|
||||
|
||||
const mockGetJson = vi.fn();
|
||||
vi.mock('@actions/http-client', () => {
|
||||
return {
|
||||
HttpClient: class {
|
||||
getJson = mockGetJson;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
describe('getRegistryToken', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('exchanges OIDC token for Chainguard token', async () => {
|
||||
const fakeOIDCToken = 'oidc-token-123';
|
||||
const fakeChainguardToken = 'chainguard-token-456';
|
||||
const identity = 'abc123/def456';
|
||||
|
||||
mockGetIDToken.mockResolvedValue(fakeOIDCToken);
|
||||
mockGetJson.mockResolvedValue({
|
||||
statusCode: 200,
|
||||
result: {token: fakeChainguardToken}
|
||||
});
|
||||
|
||||
const result = await chainguard.getRegistryToken(identity);
|
||||
|
||||
expect(mockGetIDToken).toHaveBeenCalledWith('cgr.dev');
|
||||
expect(mockGetJson).toHaveBeenCalledWith(`https://issuer.enforce.dev/sts/exchange?aud=cgr.dev&identity=${encodeURIComponent(identity)}`, {Authorization: `Bearer ${fakeOIDCToken}`});
|
||||
expect(result).toEqual({
|
||||
username: 'user',
|
||||
password: fakeChainguardToken
|
||||
});
|
||||
});
|
||||
|
||||
test('uses custom issuer URL when provided', async () => {
|
||||
const fakeOIDCToken = 'oidc-token-123';
|
||||
const fakeChainguardToken = 'chainguard-token-456';
|
||||
const identity = 'abc123/def456';
|
||||
const customIssuer = 'https://custom-issuer.example.dev';
|
||||
|
||||
mockGetIDToken.mockResolvedValue(fakeOIDCToken);
|
||||
mockGetJson.mockResolvedValue({
|
||||
statusCode: 200,
|
||||
result: {token: fakeChainguardToken}
|
||||
});
|
||||
|
||||
await chainguard.getRegistryToken(identity, customIssuer);
|
||||
|
||||
expect(mockGetJson).toHaveBeenCalledWith(`${customIssuer}/sts/exchange?aud=cgr.dev&identity=${encodeURIComponent(identity)}`, {Authorization: `Bearer ${fakeOIDCToken}`});
|
||||
});
|
||||
|
||||
test('throws on non-200 response', async () => {
|
||||
mockGetIDToken.mockResolvedValue('oidc-token');
|
||||
mockGetJson.mockResolvedValue({
|
||||
statusCode: 401,
|
||||
result: null
|
||||
});
|
||||
|
||||
await expect(chainguard.getRegistryToken('identity-id')).rejects.toThrow('Failed to exchange OIDC token with Chainguard (HTTP 401)');
|
||||
});
|
||||
|
||||
test('throws when response has no token', async () => {
|
||||
mockGetIDToken.mockResolvedValue('oidc-token');
|
||||
mockGetJson.mockResolvedValue({
|
||||
statusCode: 200,
|
||||
result: {}
|
||||
});
|
||||
|
||||
await expect(chainguard.getRegistryToken('identity-id')).rejects.toThrow('Failed to exchange OIDC token with Chainguard (HTTP 200)');
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue