feat: Implement Docker registry token endpoint and enhance registry request handling
This commit is contained in:
@@ -76,13 +76,13 @@ export class RegistryManager {
|
||||
},
|
||||
ociTokens: {
|
||||
enabled: true,
|
||||
realm: 'onebox-registry',
|
||||
realm: 'http://localhost:3000/v2/token',
|
||||
service: 'onebox-registry',
|
||||
},
|
||||
},
|
||||
oci: {
|
||||
enabled: true,
|
||||
basePath: '/v2',
|
||||
basePath: '', // Empty basePath - OCI paths are passed directly as /v2/...
|
||||
},
|
||||
});
|
||||
|
||||
@@ -105,7 +105,68 @@ export class RegistryManager {
|
||||
}
|
||||
|
||||
try {
|
||||
return await this.registry.handleRequest(req);
|
||||
// Convert native Request to IRequestContext format expected by smartregistry
|
||||
const url = new URL(req.url);
|
||||
const headers: Record<string, string> = {};
|
||||
req.headers.forEach((value, key) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
|
||||
const query: Record<string, string> = {};
|
||||
url.searchParams.forEach((value, key) => {
|
||||
query[key] = value;
|
||||
});
|
||||
|
||||
// Read body for non-GET requests
|
||||
// IMPORTANT: smartregistry expects Buffer (not Uint8Array) for proper digest calculation
|
||||
// Buffer.isBuffer(Uint8Array) returns false, causing JSON.stringify which corrupts the data
|
||||
let body: Buffer | undefined;
|
||||
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
||||
const bodyData = await req.arrayBuffer();
|
||||
if (bodyData.byteLength > 0) {
|
||||
body = Buffer.from(bodyData);
|
||||
}
|
||||
}
|
||||
|
||||
// smartregistry v2.0.0 handles JWT tokens natively - no decoding needed
|
||||
// Pass rawBody for content-addressable operations (manifest push needs exact bytes for digest)
|
||||
const context = {
|
||||
method: req.method,
|
||||
path: url.pathname,
|
||||
headers,
|
||||
query,
|
||||
body,
|
||||
rawBody: body, // smartregistry uses rawBody for digest calculation
|
||||
};
|
||||
|
||||
const result = await this.registry.handleRequest(context);
|
||||
|
||||
// Log the result for debugging
|
||||
logger.info(`Registry response: status=${result.status}, headers=${JSON.stringify(result.headers)}`);
|
||||
|
||||
// smartregistry v2.0.0 now properly includes WWW-Authenticate headers on 401 responses
|
||||
|
||||
// Convert IResponse back to native Response
|
||||
const responseHeaders = new Headers(result.headers || {});
|
||||
let responseBody: BodyInit | null = null;
|
||||
|
||||
if (result.body !== undefined) {
|
||||
if (result.body instanceof Uint8Array) {
|
||||
responseBody = result.body;
|
||||
} else if (typeof result.body === 'string') {
|
||||
responseBody = result.body;
|
||||
} else {
|
||||
responseBody = JSON.stringify(result.body);
|
||||
if (!responseHeaders.has('Content-Type')) {
|
||||
responseHeaders.set('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(responseBody, {
|
||||
status: result.status,
|
||||
headers: responseHeaders,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`Registry request error: ${getErrorMessage(error)}`);
|
||||
return new Response('Internal registry error', { status: 500 });
|
||||
@@ -195,6 +256,41 @@ export class RegistryManager {
|
||||
return randomSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the auth manager from the registry
|
||||
*/
|
||||
getAuthManager(): any {
|
||||
if (!this.isInitialized) {
|
||||
throw new Error('Registry not initialized');
|
||||
}
|
||||
return this.registry.getAuthManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an OCI token for Docker authentication
|
||||
* @param repository - Repository name (e.g., 'hello-world') or '*' for all
|
||||
* @param actions - Actions to allow: 'push', 'pull', or both
|
||||
* @param expiresIn - Token expiry in seconds (default: 3600)
|
||||
*/
|
||||
async createOciToken(
|
||||
repository: string = '*',
|
||||
actions: ('push' | 'pull')[] = ['push', 'pull'],
|
||||
expiresIn: number = 3600
|
||||
): Promise<string> {
|
||||
if (!this.isInitialized) {
|
||||
throw new Error('Registry not initialized');
|
||||
}
|
||||
|
||||
const authManager = this.registry.getAuthManager();
|
||||
|
||||
// Create scopes for the token
|
||||
const scopes = actions.map(action => `oci:repository:${repository}:${action}`);
|
||||
|
||||
// Create OCI token with scopes
|
||||
const token = await authManager.createOciToken('onebox-system', scopes, expiresIn);
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registry base URL
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user