@workspace-fs/core
Version:
Multi-project workspace manager for Firesystem with support for multiple sources
215 lines (184 loc) • 5.82 kB
text/typescript
import type { SourceAuth, ProjectSource } from "../types";
export interface CredentialProvider {
/**
* Get credentials for a specific project
*/
getCredentials(projectId: string, source: ProjectSource): Promise<any>;
/**
* Store credentials securely
*/
storeCredentials(projectId: string, credentials: any): Promise<void>;
/**
* Remove stored credentials
*/
removeCredentials(projectId: string): Promise<void>;
}
/**
* Manages credentials for different project sources
*/
export class CredentialManager {
private providers = new Map<string, CredentialProvider>();
private memoryCache = new Map<string, any>();
/**
* Register a credential provider
*/
registerProvider(name: string, provider: CredentialProvider): void {
this.providers.set(name, provider);
}
/**
* Get credentials for a project source
*/
async getCredentials(projectId: string, source: ProjectSource): Promise<any> {
// Check memory cache first
const cacheKey = `${projectId}:${source.type}`;
if (this.memoryCache.has(cacheKey)) {
return this.memoryCache.get(cacheKey);
}
// Use provider if available
const provider = this.providers.get(source.type);
if (provider) {
const credentials = await provider.getCredentials(projectId, source);
this.memoryCache.set(cacheKey, credentials);
return credentials;
}
// Fallback to source auth if provided
if (source.auth) {
return this.extractCredentials(source.auth);
}
// Return empty object if no credentials needed
return {};
}
/**
* Extract credentials from auth config
*/
private extractCredentials(auth: SourceAuth): any {
switch (auth.type) {
case "bearer":
return { token: auth.credentials.token };
case "basic":
return {
username: auth.credentials.username,
password: auth.credentials.password,
};
case "token":
return { apiKey: auth.credentials.apiKey };
case "oauth2":
return {
accessToken: auth.credentials.accessToken,
refreshToken: auth.credentials.refreshToken,
};
default:
return auth.credentials;
}
}
/**
* Clear cached credentials
*/
clearCache(projectId?: string): void {
if (projectId) {
// Clear specific project
for (const [key] of this.memoryCache) {
if (key.startsWith(`${projectId}:`)) {
this.memoryCache.delete(key);
}
}
} else {
// Clear all
this.memoryCache.clear();
}
}
}
/**
* Browser-based credential provider using Web Crypto API
*/
export class BrowserCredentialProvider implements CredentialProvider {
private dbName = "@firesystem/credentials";
private storeName = "credentials";
async getCredentials(projectId: string, source: ProjectSource): Promise<any> {
// For demo purposes, return config as-is
// In production, this would decrypt from secure storage
return source.config;
}
async storeCredentials(projectId: string, credentials: any): Promise<void> {
// In production, encrypt before storing
console.warn(
"Storing credentials in browser - consider security implications",
);
}
async removeCredentials(projectId: string): Promise<void> {
// Remove from secure storage
}
}
/**
* Environment variable credential provider (for Node.js)
*/
export class EnvCredentialProvider implements CredentialProvider {
async getCredentials(projectId: string, source: ProjectSource): Promise<any> {
const prefix = `FIRESYSTEM_${source.type.toUpperCase()}_`;
switch (source.type) {
case "s3":
return {
accessKeyId: process.env[`${prefix}ACCESS_KEY_ID`],
secretAccessKey: process.env[`${prefix}SECRET_ACCESS_KEY`],
region: process.env[`${prefix}REGION`] || "us-east-1",
bucket: source.config.bucket, // Bucket from config
};
case "github":
return {
token: process.env[`${prefix}TOKEN`] || process.env.GITHUB_TOKEN,
};
case "api":
return {
apiKey: process.env[`${prefix}API_KEY`],
baseUrl: source.config.baseUrl,
};
default:
return source.config;
}
}
async storeCredentials(): Promise<void> {
throw new Error("Cannot store credentials in environment variables");
}
async removeCredentials(): Promise<void> {
throw new Error("Cannot remove credentials from environment variables");
}
}
/**
* Interactive credential provider that prompts user
*/
export class InteractiveCredentialProvider implements CredentialProvider {
constructor(
private prompter: (message: string, secure?: boolean) => Promise<string>,
) {}
async getCredentials(projectId: string, source: ProjectSource): Promise<any> {
console.log(`Credentials needed for ${source.type} project: ${projectId}`);
switch (source.type) {
case "s3":
return {
accessKeyId: await this.prompter("AWS Access Key ID:"),
secretAccessKey: await this.prompter("AWS Secret Access Key:", true),
region:
(await this.prompter("AWS Region (default: us-east-1):")) ||
"us-east-1",
bucket: source.config.bucket,
};
case "github":
return {
token: await this.prompter("GitHub Personal Access Token:", true),
};
case "api":
return {
apiKey: await this.prompter("API Key:", true),
baseUrl: source.config.baseUrl,
};
default:
return source.config;
}
}
async storeCredentials(): Promise<void> {
// No storage in interactive mode
}
async removeCredentials(): Promise<void> {
// No storage in interactive mode
}
}