feat(maven): Add Maven registry protocol support (storage, auth, routing, interfaces, and exports)
This commit is contained in:
@@ -18,6 +18,39 @@ export class AuthManager {
|
||||
// In production, this could be Redis or a database
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// UUID TOKEN CREATION (Base method for NPM, Maven, etc.)
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Create a UUID-based token with custom scopes (base method)
|
||||
* @param userId - User ID
|
||||
* @param protocol - Protocol type
|
||||
* @param scopes - Permission scopes
|
||||
* @param readonly - Whether the token is readonly
|
||||
* @returns UUID token string
|
||||
*/
|
||||
private async createUuidToken(
|
||||
userId: string,
|
||||
protocol: TRegistryProtocol,
|
||||
scopes: string[],
|
||||
readonly: boolean = false
|
||||
): Promise<string> {
|
||||
const token = this.generateUuid();
|
||||
const authToken: IAuthToken = {
|
||||
type: protocol,
|
||||
userId,
|
||||
scopes,
|
||||
readonly,
|
||||
metadata: {
|
||||
created: new Date().toISOString(),
|
||||
},
|
||||
};
|
||||
|
||||
this.tokenStore.set(token, authToken);
|
||||
return token;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// NPM AUTHENTICATION
|
||||
// ========================================================================
|
||||
@@ -33,19 +66,8 @@ export class AuthManager {
|
||||
throw new Error('NPM tokens are not enabled');
|
||||
}
|
||||
|
||||
const token = this.generateUuid();
|
||||
const authToken: IAuthToken = {
|
||||
type: 'npm',
|
||||
userId,
|
||||
scopes: readonly ? ['npm:*:*:read'] : ['npm:*:*:*'],
|
||||
readonly,
|
||||
metadata: {
|
||||
created: new Date().toISOString(),
|
||||
},
|
||||
};
|
||||
|
||||
this.tokenStore.set(token, authToken);
|
||||
return token;
|
||||
const scopes = readonly ? ['npm:*:*:read'] : ['npm:*:*:*'];
|
||||
return this.createUuidToken(userId, 'npm', scopes, readonly);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,8 +223,59 @@ export class AuthManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// MAVEN AUTHENTICATION
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Validate any token (NPM or OCI)
|
||||
* Create a Maven token
|
||||
* @param userId - User ID
|
||||
* @param readonly - Whether the token is readonly
|
||||
* @returns Maven UUID token
|
||||
*/
|
||||
public async createMavenToken(userId: string, readonly: boolean = false): Promise<string> {
|
||||
const scopes = readonly ? ['maven:*:*:read'] : ['maven:*:*:*'];
|
||||
return this.createUuidToken(userId, 'maven', scopes, readonly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a Maven token
|
||||
* @param token - Maven UUID token
|
||||
* @returns Auth token object or null
|
||||
*/
|
||||
public async validateMavenToken(token: string): Promise<IAuthToken | null> {
|
||||
if (!this.isValidUuid(token)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const authToken = this.tokenStore.get(token);
|
||||
if (!authToken || authToken.type !== 'maven') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check expiration if set
|
||||
if (authToken.expiresAt && authToken.expiresAt < new Date()) {
|
||||
this.tokenStore.delete(token);
|
||||
return null;
|
||||
}
|
||||
|
||||
return authToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke a Maven token
|
||||
* @param token - Maven UUID token
|
||||
*/
|
||||
public async revokeMavenToken(token: string): Promise<void> {
|
||||
this.tokenStore.delete(token);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// UNIFIED AUTHENTICATION
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Validate any token (NPM, Maven, or OCI)
|
||||
* @param tokenString - Token string (UUID or JWT)
|
||||
* @param protocol - Expected protocol type
|
||||
* @returns Auth token object or null
|
||||
@@ -211,12 +284,19 @@ export class AuthManager {
|
||||
tokenString: string,
|
||||
protocol?: TRegistryProtocol
|
||||
): Promise<IAuthToken | null> {
|
||||
// Try NPM token first (UUID format)
|
||||
// Try UUID-based tokens (NPM, Maven)
|
||||
if (this.isValidUuid(tokenString)) {
|
||||
// Try NPM token
|
||||
const npmToken = await this.validateNpmToken(tokenString);
|
||||
if (npmToken && (!protocol || protocol === 'npm')) {
|
||||
return npmToken;
|
||||
}
|
||||
|
||||
// Try Maven token
|
||||
const mavenToken = await this.validateMavenToken(tokenString);
|
||||
if (mavenToken && (!protocol || protocol === 'maven')) {
|
||||
return mavenToken;
|
||||
}
|
||||
}
|
||||
|
||||
// Try OCI JWT
|
||||
|
||||
@@ -267,4 +267,129 @@ export class RegistryStorage implements IStorageBackend {
|
||||
const safeName = packageName.replace('@', '').replace('/', '-');
|
||||
return `npm/packages/${packageName}/${safeName}-${version}.tgz`;
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// MAVEN STORAGE METHODS
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Get Maven artifact
|
||||
*/
|
||||
public async getMavenArtifact(
|
||||
groupId: string,
|
||||
artifactId: string,
|
||||
version: string,
|
||||
filename: string
|
||||
): Promise<Buffer | null> {
|
||||
const path = this.getMavenArtifactPath(groupId, artifactId, version, filename);
|
||||
return this.getObject(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Maven artifact
|
||||
*/
|
||||
public async putMavenArtifact(
|
||||
groupId: string,
|
||||
artifactId: string,
|
||||
version: string,
|
||||
filename: string,
|
||||
data: Buffer
|
||||
): Promise<void> {
|
||||
const path = this.getMavenArtifactPath(groupId, artifactId, version, filename);
|
||||
return this.putObject(path, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Maven artifact exists
|
||||
*/
|
||||
public async mavenArtifactExists(
|
||||
groupId: string,
|
||||
artifactId: string,
|
||||
version: string,
|
||||
filename: string
|
||||
): Promise<boolean> {
|
||||
const path = this.getMavenArtifactPath(groupId, artifactId, version, filename);
|
||||
return this.objectExists(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Maven artifact
|
||||
*/
|
||||
public async deleteMavenArtifact(
|
||||
groupId: string,
|
||||
artifactId: string,
|
||||
version: string,
|
||||
filename: string
|
||||
): Promise<void> {
|
||||
const path = this.getMavenArtifactPath(groupId, artifactId, version, filename);
|
||||
return this.deleteObject(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Maven metadata (maven-metadata.xml)
|
||||
*/
|
||||
public async getMavenMetadata(
|
||||
groupId: string,
|
||||
artifactId: string
|
||||
): Promise<Buffer | null> {
|
||||
const path = this.getMavenMetadataPath(groupId, artifactId);
|
||||
return this.getObject(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Maven metadata (maven-metadata.xml)
|
||||
*/
|
||||
public async putMavenMetadata(
|
||||
groupId: string,
|
||||
artifactId: string,
|
||||
data: Buffer
|
||||
): Promise<void> {
|
||||
const path = this.getMavenMetadataPath(groupId, artifactId);
|
||||
return this.putObject(path, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* List Maven versions for an artifact
|
||||
* Returns all version directories under the artifact path
|
||||
*/
|
||||
public async listMavenVersions(
|
||||
groupId: string,
|
||||
artifactId: string
|
||||
): Promise<string[]> {
|
||||
const groupPath = groupId.replace(/\./g, '/');
|
||||
const prefix = `maven/artifacts/${groupPath}/${artifactId}/`;
|
||||
|
||||
const objects = await this.listObjects(prefix);
|
||||
const versions = new Set<string>();
|
||||
|
||||
// Extract version from paths like: maven/artifacts/com/example/my-lib/1.0.0/my-lib-1.0.0.jar
|
||||
for (const obj of objects) {
|
||||
const relativePath = obj.substring(prefix.length);
|
||||
const parts = relativePath.split('/');
|
||||
if (parts.length >= 1 && parts[0]) {
|
||||
versions.add(parts[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(versions).sort();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// MAVEN PATH HELPERS
|
||||
// ========================================================================
|
||||
|
||||
private getMavenArtifactPath(
|
||||
groupId: string,
|
||||
artifactId: string,
|
||||
version: string,
|
||||
filename: string
|
||||
): string {
|
||||
const groupPath = groupId.replace(/\./g, '/');
|
||||
return `maven/artifacts/${groupPath}/${artifactId}/${version}/${filename}`;
|
||||
}
|
||||
|
||||
private getMavenMetadataPath(groupId: string, artifactId: string): string {
|
||||
const groupPath = groupId.replace(/\./g, '/');
|
||||
return `maven/metadata/${groupPath}/${artifactId}/maven-metadata.xml`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/**
|
||||
* Registry protocol types
|
||||
*/
|
||||
export type TRegistryProtocol = 'oci' | 'npm';
|
||||
export type TRegistryProtocol = 'oci' | 'npm' | 'maven';
|
||||
|
||||
/**
|
||||
* Unified action types across protocols
|
||||
@@ -89,6 +89,7 @@ export interface IRegistryConfig {
|
||||
auth: IAuthConfig;
|
||||
oci?: IProtocolConfig;
|
||||
npm?: IProtocolConfig;
|
||||
maven?: IProtocolConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user