From 2b0cd3dcfc1afcada8ca758df96958c1173eb2eb Mon Sep 17 00:00:00 2001 From: fabgo Date: Fri, 18 Oct 2024 07:58:34 -0700 Subject: [PATCH] ENG-2884 Add multi-host support (#132) Adds multi-host support to the CLI by optionally looking up the CLI configuration, including the backend host, from Firestore. This allows us to specify separate backend hosts for each tenant. - Adds an optional config to the /orgs/{orgId} document - Upon login, If the config is found, the config in stored locally - When executing a command, the local config is used, if found --- package.json | 1 + src/commands/.DS_Store | Bin 0 -> 6148 bytes .../__snapshots__/login.test.ts.snap | 4 +- src/commands/__tests__/login.test.ts | 18 ++++- src/commands/allow.ts | 4 +- src/commands/aws/role.ts | 6 +- src/commands/grant.ts | 4 +- src/commands/kubeconfig.ts | 4 +- src/commands/login.ts | 18 +++-- src/commands/ls.ts | 4 +- src/commands/request.ts | 4 +- src/commands/scp.ts | 4 +- src/commands/ssh.ts | 4 +- src/drivers/api.ts | 5 +- src/drivers/auth.ts | 29 ++----- src/drivers/config.ts | 36 +++++++++ src/drivers/env.ts | 2 +- src/drivers/firestore.ts | 72 ++++++++++++++---- src/plugins/google/login.ts | 10 ++- src/types/org.ts | 4 + src/util.ts | 6 +- 21 files changed, 167 insertions(+), 72 deletions(-) create mode 100644 src/commands/.DS_Store create mode 100644 src/drivers/config.ts diff --git a/package.json b/package.json index 45516b4..eb7bd09 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ }, "scripts": { "build": "tsc && cp -r public dist/", + "test:unit": "NODE_ENV=unit jest --color", "clean": "rm -f tsconfig.tsbuildinfo && rm -rf dist/", "lint": "yarn prettier --check . && yarn run eslint --max-warnings 0 .", "p0": "node --no-deprecation ./p0", diff --git a/src/commands/.DS_Store b/src/commands/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..af692fade7af44202164b7f8c6460d49362514cc GIT binary patch literal 6148 zcmeHKJ5EDE474Fd5KT(Ty#hB_Md1WFKqwN?pdgV?{i>XcqcLM!QKAHlEW*c2^k(!g0sW_VAUhj>}Q??F6|GWPrK3NR3`(vfdf*9(Wlq?_09;q_#%3B}{-xW7fYc~4Z70#e{w zf&1Lf*#BSPKg|EvB<-Yt6!=#P_++tM%<)RqTSqU)UfbZWaMpamX;=pZA=)u8+A%h4 e$4^m|b&YGB_rf7D=*R~hsGkAqB9j7tt-v>Tp%sAu literal 0 HcmV?d00001 diff --git a/src/commands/__tests__/__snapshots__/login.test.ts.snap b/src/commands/__tests__/__snapshots__/login.test.ts.snap index 8475ae4..1f4d31e 100644 --- a/src/commands/__tests__/__snapshots__/login.test.ts.snap +++ b/src/commands/__tests__/__snapshots__/login.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`login organization exists should write the user's identity to the file system 1`] = ` +exports[`login organization exists should write the user's identity & config to the file system 1`] = ` [ [ - "/path/to/home/.p0", + "/dummy/identity/file/path", "{ "credential": { "access_token": "test-access-token", diff --git a/src/commands/__tests__/login.test.ts b/src/commands/__tests__/login.test.ts index 481a689..698f565 100644 --- a/src/commands/__tests__/login.test.ts +++ b/src/commands/__tests__/login.test.ts @@ -8,6 +8,7 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ +import { bootstrapConfig } from "../../drivers/env"; import { pluginLoginMap } from "../../plugins/login"; import { mockGetDoc } from "../../testing/firestore"; import { login } from "../login"; @@ -18,7 +19,12 @@ jest.spyOn(Date, "now").mockReturnValue(1.6e12); jest.mock("fs/promises"); jest.mock("../../drivers/auth", () => ({ ...jest.requireActual("../../drivers/auth"), - IDENTITY_FILE_PATH: "/path/to/home/.p0", + IDENTITY_FILE_PATH: "/dummy/identity/file/path", +})); +jest.mock("../../drivers/config", () => ({ + ...jest.requireActual("../../drivers/config"), + saveConfig: jest.fn(), + getTenantConfig: jest.fn(() => bootstrapConfig), })); jest.mock("../../drivers/stdio"); jest.mock("../../plugins/login"); @@ -34,6 +40,7 @@ describe("login", () => { `"Could not find organization"` ); }); + it("should print a friendly error if unsupported login", async () => { mockGetDoc({ slug: "test-org", @@ -44,6 +51,7 @@ describe("login", () => { `"Unsupported login for your organization"` ); }); + describe("organization exists", () => { let credentialData: string = ""; mockReadFile.mockImplementation(async () => @@ -60,27 +68,33 @@ describe("login", () => { }, }) ); + beforeEach(() => { credentialData = ""; jest.clearAllMocks(); + mockGetDoc({ slug: "test-org", tenantId: "test-tenant", ssoProvider: "google", }); }); + it("should call the provider's login function", async () => { await login({ org: "test-org" }); expect(pluginLoginMap.google).toHaveBeenCalled(); }); - it("should write the user's identity to the file system", async () => { + + it("should write the user's identity & config to the file system", async () => { await login({ org: "test-org" }); expect(mockWriteFile.mock.calls).toMatchSnapshot(); }); + it("validates authentication", async () => { await login({ org: "test-org" }); expect((signInWithCredential as jest.Mock).mock.calls).toMatchSnapshot(); }); + it("returns an error message if firebase cannot determine the user's email", async () => { mockSignInWithCredential.mockResolvedValueOnce({ user: {}, diff --git a/src/commands/allow.ts b/src/commands/allow.ts index c1aa03b..fc98e9e 100644 --- a/src/commands/allow.ts +++ b/src/commands/allow.ts @@ -10,7 +10,7 @@ You should have received a copy of the GNU General Public License along with @p0 **/ import { fetchCommand } from "../drivers/api"; import { authenticate } from "../drivers/auth"; -import { guard } from "../drivers/firestore"; +import { fsShutdownGuard } from "../drivers/firestore"; import { print2 } from "../drivers/stdio"; import { AllowResponse } from "../types/allow"; import { Authn } from "../types/identity"; @@ -37,7 +37,7 @@ export const allowCommand = (yargs: yargs.Argv) => "allow [arguments..]", "Create standing access for a resource", allowArgs, - guard(allow) + fsShutdownGuard(allow) ); export const allow = async ( diff --git a/src/commands/aws/role.ts b/src/commands/aws/role.ts index 3a44f00..54e8a89 100644 --- a/src/commands/aws/role.ts +++ b/src/commands/aws/role.ts @@ -10,7 +10,7 @@ You should have received a copy of the GNU General Public License along with @p0 **/ import { parseXml } from "../../common/xml"; import { authenticate } from "../../drivers/auth"; -import { guard } from "../../drivers/firestore"; +import { fsShutdownGuard } from "../../drivers/firestore"; import { print1, print2 } from "../../drivers/stdio"; import { getAwsConfig } from "../../plugins/aws/config"; import { AwsFederatedLogin, AwsItem } from "../../plugins/aws/types"; @@ -29,7 +29,7 @@ export const role = (yargs: yargs.Argv<{ account: string | undefined }>) => "List available AWS roles", identity, // TODO: select based on uidLocation - guard(oktaAwsListRoles) + fsShutdownGuard(oktaAwsListRoles) ) .command( "assume ", @@ -41,7 +41,7 @@ export const role = (yargs: yargs.Argv<{ account: string | undefined }>) => describe: "An AWS role name", }), // TODO: select based on uidLocation - guard(oktaAwsAssumeRole) + fsShutdownGuard(oktaAwsAssumeRole) ) .demandCommand(1) ); diff --git a/src/commands/grant.ts b/src/commands/grant.ts index 5b77f9b..3eea0fa 100644 --- a/src/commands/grant.ts +++ b/src/commands/grant.ts @@ -8,7 +8,7 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ -import { guard } from "../drivers/firestore"; +import { fsShutdownGuard } from "../drivers/firestore"; import { request, requestArgs } from "./shared/request"; import yargs from "yargs"; @@ -17,5 +17,5 @@ export const grantCommand = (yargs: yargs.Argv) => "grant [arguments..]", "Grant access to another identity", requestArgs, - guard(request("grant")) + fsShutdownGuard(request("grant")) ); diff --git a/src/commands/kubeconfig.ts b/src/commands/kubeconfig.ts index e00eb22..8e59c6f 100644 --- a/src/commands/kubeconfig.ts +++ b/src/commands/kubeconfig.ts @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with @p0 import { retryWithSleep } from "../common/retry"; import { AnsiSgr } from "../drivers/ansi"; import { authenticate } from "../drivers/auth"; -import { guard } from "../drivers/firestore"; +import { fsShutdownGuard } from "../drivers/firestore"; import { print2, spinUntil } from "../drivers/stdio"; import { parseArn } from "../plugins/aws/utils"; import { @@ -65,7 +65,7 @@ export const kubeconfigCommand = (yargs: yargs.Argv) => describe: "Requested duration for access (format like '10 minutes', '2 hours', '5 days', or '1 week')", }), - guard(kubeconfigAction) + fsShutdownGuard(kubeconfigAction) ); const kubeconfigAction = async ( diff --git a/src/commands/login.ts b/src/commands/login.ts index 7c098c3..3f2db6f 100644 --- a/src/commands/login.ts +++ b/src/commands/login.ts @@ -9,11 +9,13 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ import { + authenticate, IDENTITY_CACHE_PATH, IDENTITY_FILE_PATH, - authenticate, } from "../drivers/auth"; -import { doc, guard } from "../drivers/firestore"; +import { saveConfig } from "../drivers/config"; +import { bootstrapConfig } from "../drivers/env"; +import { fsShutdownGuard, publicDoc } from "../drivers/firestore"; import { print2 } from "../drivers/stdio"; import { pluginLoginMap } from "../plugins/login"; import { TokenResponse } from "../types/oidc"; @@ -32,10 +34,14 @@ export const login = async ( args: { org: string }, options?: { skipAuthenticate?: boolean } ) => { - const orgDoc = await getDoc(doc(`orgs/${args.org}`)); + const orgDoc = await getDoc( + publicDoc(`orgs/${args.org}`) + ); const orgData = orgDoc.data(); if (!orgData) throw "Could not find organization"; + await saveConfig(orgData.config ?? bootstrapConfig); + const orgWithSlug: OrgData = { ...orgData, slug: args.org }; const plugin = orgWithSlug?.ssoProvider; @@ -49,7 +55,9 @@ export const login = async ( await writeIdentity(orgWithSlug, tokenResponse); // validate auth - if (!options?.skipAuthenticate) await authenticate({ noRefresh: true }); + if (!options?.skipAuthenticate) { + await authenticate(); + } print2(`You are now logged in, and can use the p0 CLI.`); }; @@ -95,5 +103,5 @@ export const loginCommand = (yargs: yargs.Argv) => type: "string", describe: "Your P0 organization ID", }), - guard(login) + fsShutdownGuard(login) ); diff --git a/src/commands/ls.ts b/src/commands/ls.ts index e548856..42da519 100644 --- a/src/commands/ls.ts +++ b/src/commands/ls.ts @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with @p0 import { AnsiSgr } from "../drivers/ansi"; import { fetchCommand } from "../drivers/api"; import { authenticate } from "../drivers/auth"; -import { guard } from "../drivers/firestore"; +import { fsShutdownGuard } from "../drivers/firestore"; import { print2, print1, spinUntil } from "../drivers/stdio"; import { max, orderBy } from "lodash"; import pluralize from "pluralize"; @@ -45,7 +45,7 @@ export const lsCommand = (yargs: yargs.Argv) => "ls [arguments..]", "List request-command arguments", lsArgs, - guard(ls) + fsShutdownGuard(ls) ); const ls = async ( diff --git a/src/commands/request.ts b/src/commands/request.ts index a9d5f4c..8458cd8 100644 --- a/src/commands/request.ts +++ b/src/commands/request.ts @@ -8,7 +8,7 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ -import { guard } from "../drivers/firestore"; +import { fsShutdownGuard } from "../drivers/firestore"; import { request, requestArgs } from "./shared/request"; import yargs from "yargs"; @@ -17,5 +17,5 @@ export const requestCommand = (yargs: yargs.Argv) => "request [arguments..]", "Manually request permissions on a resource", requestArgs, - guard(request("request")) + fsShutdownGuard(request("request")) ); diff --git a/src/commands/scp.ts b/src/commands/scp.ts index e3fb44f..b067061 100644 --- a/src/commands/scp.ts +++ b/src/commands/scp.ts @@ -9,7 +9,7 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ import { authenticate } from "../drivers/auth"; -import { guard } from "../drivers/firestore"; +import { fsShutdownGuard } from "../drivers/firestore"; import { sshOrScp } from "../plugins/ssh"; import { SshRequest, SupportedSshProviders } from "../types/ssh"; import { prepareRequest, ScpCommandArgs } from "./shared/ssh"; @@ -69,7 +69,7 @@ export const scpCommand = (yargs: yargs.Argv) => The '--' argument must be specified between P0-specific args on the left and SCP_ARGS on the right.` ), - guard(scpAction) + fsShutdownGuard(scpAction) ); /** Transfers files between a local and remote hosts using SSH. diff --git a/src/commands/ssh.ts b/src/commands/ssh.ts index b2d4555..e643be1 100644 --- a/src/commands/ssh.ts +++ b/src/commands/ssh.ts @@ -9,7 +9,7 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ import { authenticate } from "../drivers/auth"; -import { guard } from "../drivers/firestore"; +import { fsShutdownGuard } from "../drivers/firestore"; import { sshOrScp } from "../plugins/ssh"; import { SshCommandArgs, prepareRequest } from "./shared/ssh"; import yargs from "yargs"; @@ -69,7 +69,7 @@ export const sshCommand = (yargs: yargs.Argv) => $ p0 ssh example-instance --provider gcloud -- -NR '*:8080:localhost:8088' -o 'GatewayPorts yes'` ), - guard(sshAction) + fsShutdownGuard(sshAction) ); /** Connect to an SSH backend diff --git a/src/drivers/api.ts b/src/drivers/api.ts index 7a14e21..64976d6 100644 --- a/src/drivers/api.ts +++ b/src/drivers/api.ts @@ -8,13 +8,12 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ -import { config } from "../drivers/env"; import { Authn } from "../types/identity"; +import { getTenantConfig } from "./config"; import * as path from "node:path"; import yargs from "yargs"; -const tenantUrl = (tenant: string) => `${config.appUrl}/o/${tenant}`; - +const tenantUrl = (tenant: string) => `${getTenantConfig().appUrl}/o/${tenant}`; const commandUrl = (tenant: string) => `${tenantUrl(tenant)}/command/`; export const fetchCommand = async ( diff --git a/src/drivers/auth.ts b/src/drivers/auth.ts index 9173d0c..0e40b21 100644 --- a/src/drivers/auth.ts +++ b/src/drivers/auth.ts @@ -11,13 +11,9 @@ You should have received a copy of the GNU General Public License along with @p0 import { login } from "../commands/login"; import { Authn, Identity } from "../types/identity"; import { P0_PATH } from "../util"; -import { auth } from "./firestore"; +import { loadConfig } from "./config"; +import { authenticateToFirebase, initializeFirebase } from "./firestore"; import { print2 } from "./stdio"; -import { - OAuthProvider, - SignInMethod, - signInWithCredential, -} from "firebase/auth"; import * as fs from "fs/promises"; import * as path from "path"; @@ -96,24 +92,11 @@ export const loadCredentials = async (options?: { export const authenticate = async (options?: { noRefresh?: boolean; }): Promise => { - const identity = await loadCredentials(options); - const { credential } = identity; + await loadConfig(); + initializeFirebase(); - // TODO: Move to map lookup - const provider = new OAuthProvider( - identity.org.ssoProvider === "google" - ? SignInMethod.GOOGLE - : identity.org.providerId - ); - const firebaseCredential = provider.credential({ - accessToken: credential.access_token, - idToken: credential.id_token, - }); - auth.tenantId = identity.org.tenantId; - const userCredential = await signInWithCredential(auth, firebaseCredential); - if (!userCredential?.user?.email) { - throw "Can not sign in: this user has previously signed in with a different identity provider.\nPlease contact support@p0.dev to enable this user."; - } + const identity = await loadCredentials(options); + const userCredential = await authenticateToFirebase(identity); return { userCredential, identity }; }; diff --git a/src/drivers/config.ts b/src/drivers/config.ts new file mode 100644 index 0000000..cd29159 --- /dev/null +++ b/src/drivers/config.ts @@ -0,0 +1,36 @@ +/** Copyright © 2024-present P0 Security + +This file is part of @p0security/cli + +@p0security/cli is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + +@p0security/cli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . +**/ +import { Config } from "../types/org"; +import { P0_PATH } from "../util"; +import { print2 } from "./stdio"; +import fs from "fs/promises"; +import path from "path"; + +export const CONFIG_FILE_PATH = path.join(P0_PATH, "config.json"); + +let tenantConfig: Config; + +export function getTenantConfig(): Config { + return tenantConfig; +} + +export async function saveConfig(config: Config) { + print2(`Saving config to ${CONFIG_FILE_PATH}.`); + const dir = path.dirname(CONFIG_FILE_PATH); + await fs.mkdir(dir, { recursive: true }); + await fs.writeFile(CONFIG_FILE_PATH, JSON.stringify(config), { mode: "600" }); + tenantConfig = config; +} + +export async function loadConfig() { + const buffer = await fs.readFile(CONFIG_FILE_PATH); + tenantConfig = JSON.parse(buffer.toString()); +} diff --git a/src/drivers/env.ts b/src/drivers/env.ts index 84bf4e9..442056e 100644 --- a/src/drivers/env.ts +++ b/src/drivers/env.ts @@ -14,7 +14,7 @@ dotenv.config(); const { env } = process; -export const config = { +export const bootstrapConfig = { fs: { // Falls back to public production Firestore credentials apiKey: env.P0_FS_API_KEY ?? "AIzaSyCaL-Ik_l_5tdmgNUNZ4Nv6NuR4o5_PPfs", diff --git a/src/drivers/firestore.ts b/src/drivers/firestore.ts index 714aaf8..f196738 100644 --- a/src/drivers/firestore.ts +++ b/src/drivers/firestore.ts @@ -8,9 +8,16 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ -import { config } from "./env"; -import { initializeApp } from "firebase/app"; -import { getAuth } from "firebase/auth"; +import { Identity } from "../types/identity"; +import { getTenantConfig } from "./config"; +import { bootstrapConfig } from "./env"; +import { FirebaseApp, initializeApp } from "firebase/app"; +import { + getAuth, + OAuthProvider, + SignInMethod, + signInWithCredential, +} from "firebase/auth"; import { collection as fsCollection, CollectionReference, @@ -18,37 +25,76 @@ import { DocumentReference, getFirestore, terminate, + Firestore, } from "firebase/firestore"; -// Your web app's Firebase configuration -const firebaseConfig = config.fs; +const bootstrapApp = initializeApp(bootstrapConfig.fs, "bootstrapApp"); +const bootstrapFirestore = getFirestore(bootstrapApp); + +let app: FirebaseApp; +let firestore: Firestore; + +export function initializeFirebase() { + const tenantConfig = getTenantConfig(); + app = initializeApp(tenantConfig.fs, "authFirebase"); + firestore = getFirestore(app); +} + +export async function authenticateToFirebase(identity: Identity) { + const { credential } = identity; + const tenantId = identity.org.tenantId; + + // TODO: Move to map lookup + const provider = new OAuthProvider( + identity.org.ssoProvider === "google" + ? SignInMethod.GOOGLE + : identity.org.providerId + ); + + const firebaseCredential = provider.credential({ + accessToken: credential.access_token, + idToken: credential.id_token, + }); -// Initialize Firebase -const app = initializeApp(firebaseConfig); -export const FIRESTORE = getFirestore(app); -export const auth = getAuth(); + const auth = getAuth(app); + auth.tenantId = tenantId; + + const userCredential = await signInWithCredential(auth, firebaseCredential); + + if (!userCredential?.user?.email) { + throw "Can not sign in: this user has previously signed in with a different identity provider.\nPlease contact support@p0.dev to enable this user."; + } + + return userCredential; +} export const collection = (path: string, ...pathSegments: string[]) => { return fsCollection( - FIRESTORE, + firestore, path, ...pathSegments ) as CollectionReference; }; + export const doc = (path: string) => { - return fsDoc(FIRESTORE, path) as DocumentReference; + return fsDoc(firestore, path) as DocumentReference; +}; + +export const publicDoc = (path: string) => { + return fsDoc(bootstrapFirestore, path) as DocumentReference; }; /** Ensures that Firestore is shutdown at command termination * * This prevents Firestore from holding the command on execution completion or failure. */ -export const guard = +export const fsShutdownGuard = (cb: (args: P) => Promise) => async (args: P) => { try { await cb(args); } finally { - void terminate(FIRESTORE); + if (bootstrapFirestore) void terminate(bootstrapFirestore); + if (firestore) void terminate(firestore); } }; diff --git a/src/plugins/google/login.ts b/src/plugins/google/login.ts index 089654a..93c2c07 100644 --- a/src/plugins/google/login.ts +++ b/src/plugins/google/login.ts @@ -11,7 +11,7 @@ You should have received a copy of the GNU General Public License along with @p0 import { OIDC_HEADERS } from "../../common/auth/oidc"; import { withRedirectServer } from "../../common/auth/server"; import { urlEncode, validateResponse } from "../../common/fetch"; -import { config } from "../../drivers/env"; +import { getTenantConfig } from "../../drivers/config"; import { print2 } from "../../drivers/stdio"; import { AuthorizeRequest, TokenResponse } from "../../types/oidc"; import open from "open"; @@ -28,10 +28,11 @@ const GOOGLE_OIDC_REDIRECT_URL = `http://127.0.0.1:${GOOGLE_OIDC_REDIRECT_PORT}` const PKCE_LENGTH = 128; const requestAuth = async () => { + const tenantConfig = getTenantConfig(); const pkceChallenge = (await import("pkce-challenge")).default as any; const pkce = await pkceChallenge(PKCE_LENGTH); const authBody: AuthorizeRequest = { - client_id: config.google.clientId, + client_id: tenantConfig.google.clientId, code_challenge: pkce.code_challenge, code_challenge_method: "S256", redirect_uri: GOOGLE_OIDC_REDIRECT_URL, @@ -51,9 +52,10 @@ const requestToken = async ( code: string, pkce: { code_challenge: string; code_verifier: string } ) => { + const tenantConfig = getTenantConfig(); const body = { - client_id: config.google.clientId, - client_secret: config.google.clientSecret, + client_id: tenantConfig.google.clientId, + client_secret: tenantConfig.google.clientSecret, code, code_verifier: pkce.code_verifier, grant_type: "authorization_code", diff --git a/src/types/org.ts b/src/types/org.ts index 02868a2..86481db 100644 --- a/src/types/org.ts +++ b/src/types/org.ts @@ -8,6 +8,9 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ +import { bootstrapConfig } from "../drivers/env"; + +export type Config = typeof bootstrapConfig; type BaseOrgData = { clientId: string; @@ -21,6 +24,7 @@ type BaseOrgData = { | "oidc-pkce" | "okta"; tenantId: string; + config: Config; }; /** Publicly readable organization data */ diff --git a/src/util.ts b/src/util.ts index 68dfd3e..3b4f51c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -8,14 +8,16 @@ This file is part of @p0security/cli You should have received a copy of the GNU General Public License along with @p0security/cli. If not, see . **/ -import { config } from "./drivers/env"; +import { bootstrapConfig } from "./drivers/env"; import child_process from "node:child_process"; import os from "node:os"; import path from "node:path"; export const P0_PATH = path.join( os.homedir(), - config.environment === "production" ? ".p0" : `.p0-${config.environment}` + bootstrapConfig.environment === "production" + ? ".p0" + : `.p0-${bootstrapConfig.environment}` ); /** Waits the specified delay (in ms)