@noemaresearch/ctf-cli
Version:
A CLI tool for navigating and playing cybersecurity challenges
131 lines (111 loc) • 4.18 kB
text/typescript
import dotenv from 'dotenv';
import path from 'path';
import fs from 'fs';
import { httpsCallable } from 'firebase/functions';
import { FirebaseApp, getApps, getApp, initializeApp } from 'firebase/app';
import { Auth, initializeAuth, getAuth, setPersistence, inMemoryPersistence, signInWithCustomToken } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getFunctions } from 'firebase/functions';
import { resolve } from 'node:path';
import { cwd } from 'node:process';
import chalk from 'chalk';
import boxen from 'boxen';
import { getLocalStorage } from './local_storage';
// Path to store the current environment
const ENV_STORE_PATH = path.join(__dirname, '..', '.current_env');
// Function to get the current environment
function getCurrentEnv(): string {
if (process.env.NODE_ENV) {
// If NODE_ENV is set, use it and store it for future use
fs.writeFileSync(ENV_STORE_PATH, process.env.NODE_ENV);
return process.env.NODE_ENV;
} else if (fs.existsSync(ENV_STORE_PATH)) {
// If NODE_ENV is not set, try to read from the stored file
return fs.readFileSync(ENV_STORE_PATH, 'utf8').trim();
}
// Default to 'development' if no environment is set or stored
return 'development';
}
const currentEnv = getCurrentEnv();
// Determine the correct .env file and load envvars
const envFile = currentEnv === 'production' ? '.env.production' : '.env.development';
const envPath = path.resolve(__dirname, '..', envFile);
dotenv.config({ path: envPath });
// Load Firebase Config
const firebaseConfig = {
projectId: process.env.FIREBASE_PROJECT_ID,
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.FIREBASE_APP_ID
};
global.localStorage = getLocalStorage();
// Singleton Firebase App Initialization
let app: FirebaseApp | null = null;
let initialized: boolean = getApps().length > 0;
app = initialized? getApp(): initializeApp(firebaseConfig);
// Firebase Auth with inMemoryPersistence
let auth: Auth;
if ( initialized ) {
auth = getAuth();
} else {
auth = initializeAuth(app, { persistence: inMemoryPersistence });
}
const db = getFirestore(app);
const functions = getFunctions(app);
// Utility functions for token persistence
const TOKEN_KEY = 'firebase_id_token';
export async function saveAuthToken(token: string) {
global.localStorage.setItem(TOKEN_KEY, token);
}
export async function loadAuthToken() {
const token: string | null = global.localStorage.getItem(TOKEN_KEY);
return token;
}
export async function removeAuthToken() {
global.localStorage.removeItem(TOKEN_KEY);
}
async function authenticate() {
app = getApps().length? getApp(): initializeApp(firebaseConfig);
auth = getAuth();
const token = await loadAuthToken();
if (token) {
// Reauthenticate using the saved token
try {
const getAuthToken = httpsCallable(functions, 'getAuthToken');
const result: any = await getAuthToken({ idToken: token });
await signInWithCustomToken(auth, result.data);
} catch (error) {
//console.error('Error on authentication:', error);
}
} else {
// No saved token, initiate login
// For example, you might sign in the user here or prompt for credentials
// After login, save the token
const newToken = await auth.currentUser?.getIdToken();
if (newToken) saveAuthToken(newToken);
}
return auth;
}
async function withAuth(auth: Auth, commandFn: (auth: Auth) => Promise<void>): Promise<void> {
if (!auth.currentUser?.email) {
console.log();
console.log(
boxen(
chalk.red(chalk.bold('Authentication Required')) + '\n\n' +
chalk.white('You must be logged in to use this command.') + '\n' +
chalk.white('Please run ') + chalk.cyan('`ctf login`') + chalk.white(' first.'),
{
padding: 1,
borderColor: 'red',
borderStyle: 'round',
}
)
);
console.log();
return;
}
await commandFn(auth);
}
export { db, functions, authenticate, withAuth, auth, app };