Skip to content

Commit

Permalink
Merge branch 'main' into fe-6013-abort-info
Browse files Browse the repository at this point in the history
  • Loading branch information
ptpaterson committed Jan 17, 2025
2 parents e183672 + 68f87fe commit b2da774
Show file tree
Hide file tree
Showing 22 changed files with 94 additions and 109 deletions.
3 changes: 2 additions & 1 deletion src/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
checkForUpdates,
fixPaths,
logArgv,
scopeSecret,
} from "./lib/middleware.mjs";

/** @type {import('yargs').Argv} */
Expand Down Expand Up @@ -111,7 +112,7 @@ function buildYargs(argvInput) {
.config("config", configParser.bind(null, argvInput))
.middleware([checkForUpdates, logArgv], true)
.middleware(
[applyLocalArg, fixPaths, applyAccountUrl, buildCredentials],
[applyLocalArg, fixPaths, applyAccountUrl, buildCredentials, scopeSecret],
false,
)
.command(queryCommand)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/database/create.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function runCreateQuery(secret, argv) {
}

async function createDatabase(argv) {
const secret = await getSecret();
const secret = await getSecret(argv);
const logger = container.resolve("logger");

try {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/database/delete.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async function runDeleteQuery(secret, argv) {
}

async function deleteDatabase(argv) {
const secret = await getSecret();
const secret = await getSecret(argv);
const logger = container.resolve("logger");

try {
Expand Down
2 changes: 0 additions & 2 deletions src/commands/local.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ async function createDatabaseSchema(argv) {
},
),
);
// hack to let us push schema to the local database
argv.secret = `secret:${argv.database}:admin`;
await pushSchema({ ...argv, active: true, input: false });
logger.stderr(
colorize(
Expand Down
12 changes: 6 additions & 6 deletions src/commands/login.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ async function doLogin(argv) {
const promptLoginUrl = async () => {
const authCodeParams = oAuth.getOAuthParams({
clientId: argv.clientId,
noRedirect: argv.noRedirect,
redirect: argv.redirect,
});
const dashboardOAuthURL = await startOAuthRequest(authCodeParams);
logger.stdout(`To login, open a browser to:\n${dashboardOAuthURL}`);
return dashboardOAuthURL;
};

if (!argv.noRedirect) {
if (argv.redirect) {
oAuth.server.on("ready", async () => {
const dashboardOAuthURL = await promptLoginUrl();
open(dashboardOAuthURL);
Expand Down Expand Up @@ -110,12 +110,12 @@ function buildLoginCommand(yargs) {
required: false,
hidden: true,
},
"no-redirect": {
alias: "n",
redirect: {
alias: "r",
type: "boolean",
description:
"Log in without redirecting to a local callback server. Use this option if you are unable to open a browser on your local machine.",
default: false,
"Set the method of authenticating. Use 'false' or '--no-redirect' if you are unable to open a browser on your local machine.",
default: true,
},
user: {
alias: "u",
Expand Down
2 changes: 1 addition & 1 deletion src/commands/query.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async function queryCommand(argv) {
validateDatabaseOrSecret(argv);
validate(argv);

const secret = await getSecret();
const secret = await getSecret(argv);
const {
url,
timeout,
Expand Down
2 changes: 1 addition & 1 deletion src/commands/schema/abandon.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ async function doAbandon(argv) {
const makeFaunaRequest = container.resolve("makeFaunaRequest");
const logger = container.resolve("logger");
const confirm = container.resolve("confirm");
const secret = await getSecret();
const secret = await getSecret(argv);

if (!argv.input) {
const params = new URLSearchParams({
Expand Down
2 changes: 1 addition & 1 deletion src/commands/schema/commit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ async function doCommit(argv) {
const makeFaunaRequest = container.resolve("makeFaunaRequest");
const logger = container.resolve("logger");
const confirm = container.resolve("confirm");
const secret = await getSecret();
const secret = await getSecret(argv);

if (!argv.input) {
const params = new URLSearchParams({
Expand Down
2 changes: 1 addition & 1 deletion src/commands/schema/diff.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async function doDiff(argv) {
const gatherFSL = container.resolve("gatherFSL");
const logger = container.resolve("logger");
const makeFaunaRequest = container.resolve("makeFaunaRequest");
const secret = await getSecret();
const secret = await getSecret(argv);
const files = reformatFSL(await gatherFSL(argv.dir));

const { version, status, diff } = await makeFaunaRequest({
Expand Down
2 changes: 1 addition & 1 deletion src/commands/schema/pull.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ async function doPull(argv) {
const logger = container.resolve("logger");
const confirm = container.resolve("confirm");
const makeFaunaRequest = container.resolve("makeFaunaRequest");
const secret = await getSecret();
const secret = await getSecret(argv);

// Get the staged schema status
/** @type {{ status: "none" | "pending" | "ready" | "failed", version: string }} */
Expand Down
2 changes: 1 addition & 1 deletion src/commands/schema/push.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export async function pushSchema(argv) {
const gatherFSL = container.resolve("gatherFSL");

const isStagedPush = !argv.active;
const secret = await getSecret();
const secret = await getSecret(argv);
const fslFiles = await gatherFSL(argv.dir);
const hasLocalSchema = fslFiles.length > 0;
const absoluteDirPath = path.resolve(argv.dir);
Expand Down
2 changes: 1 addition & 1 deletion src/commands/schema/status.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async function doStatus(argv) {
const logger = container.resolve("logger");
const makeFaunaRequest = container.resolve("makeFaunaRequest");

const secret = await getSecret();
const secret = await getSecret(argv);
const absoluteDirPath = path.resolve(argv.dir);
const gatherFSL = container.resolve("gatherFSL");
const fslFiles = await gatherFSL(argv.dir);
Expand Down
4 changes: 2 additions & 2 deletions src/commands/shell.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async function shellCommand(argv) {

// Fast fail if the database is not queryable
const isQueryable = container.resolve("isQueryable");
await isQueryable({ ...argv, secret: await getSecret() });
await isQueryable({ ...argv, secret: await getSecret(argv) });

const logger = container.resolve("logger");
let completionPromise;
Expand Down Expand Up @@ -179,7 +179,7 @@ async function buildCustomEval(argv) {

let res;
try {
const secret = await getSecret();
const secret = await getSecret(argv);
const { color, timeout, typecheck, url } = argv;

res = await runQueryFromString(cmd, {
Expand Down
17 changes: 12 additions & 5 deletions src/lib/auth/credentials.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import { DatabaseKeys } from "./databaseKeys.mjs";

const validateCredentialArgs = (argv) => {
const logger = container.resolve("logger");
const illegalArgCombos = [
["accountKey", "secret", isLocal],
["secret", "database", isLocal],
["secret", "role", isLocal],
];
const illegalArgCombos = [["accountKey", "secret", isLocal]];
for (const [first, second, conditional] of illegalArgCombos) {
if (argv[first] && argv[second] && !conditional(argv)) {
throw new ValidationError(
Expand Down Expand Up @@ -67,6 +63,17 @@ export class Credentials {
});
this.accountKeys.key = accountKey;
}

/**
* Gets a secret for the current credentials.
* @return {Promise<string>} the secret
*/
async getSecret() {
if (!this.databaseKeys.key) {
return await this.databaseKeys.getOrRefreshKey();
}
return this.databaseKeys.key;
}
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/lib/auth/oauth-client.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ class OAuthClient {
* Gets the OAuth parameters for the OAuth request.
* @param {Object} [overrides] - The parameters for the OAuth request
* @param {string} [overrides.clientId] - The client ID
* @param {boolean} [overrides.noRedirect] - Whether to disable the redirect
* @param {boolean} [overrides.redirect] - Whether to disable the redirect
* @returns {Object} The OAuth parameters
*/
getOAuthParams({ clientId, noRedirect }) {
const redirectURI = noRedirect
getOAuthParams({ clientId, redirect }) {
const redirectURI = !redirect
? `${getDashboardUrl()}/auth/oauth/callback/cli`
: `${REDIRECT_URI}:${this.port}`;

Expand Down
11 changes: 6 additions & 5 deletions src/lib/fauna-client.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import { Format } from "./formatting/colorize.mjs";
* Gets a secret for the current credentials.
* @return {Promise<string>} the secret
*/
export async function getSecret() {
const credentials = container.resolve("credentials");
if (!credentials.databaseKeys.key) {
return await credentials.databaseKeys.getOrRefreshKey();
export async function getSecret(argv) {
if (argv.secret) {
return argv.secret;
}
return credentials.databaseKeys.key;

const credentials = container.resolve("credentials");
return await credentials.getSecret();
}

export const retryInvalidCredsOnce = async (initialSecret, fn) => {
Expand Down
49 changes: 38 additions & 11 deletions src/lib/middleware.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { container } from "../config/container.mjs";
import { fixPath } from "../lib/file-util.mjs";
import { setAccountUrl } from "./account-api.mjs";
import { ValidationError } from "./errors.mjs";
import { redactedStringify } from "./formatting/redact.mjs";
import { redact, redactedStringify } from "./formatting/redact.mjs";
import { QUERY_OPTIONS } from "./options.mjs";
const LOCAL_URL = "http://0.0.0.0:8443";
const LOCAL_SECRET = "secret";
Expand Down Expand Up @@ -131,16 +131,7 @@ function applyLocalToUrl(argv) {
function applyLocalToSecret(argv) {
const logger = container.resolve("logger");
if (!argv.secret && isLocal(argv)) {
if (argv.role && argv.database) {
argv.secret = `${LOCAL_SECRET}:${argv.database}:${argv.role}`;
} else if (argv.role) {
argv.secret = `${LOCAL_SECRET}:${argv.role}`;
} else if (argv.database) {
// no role
argv.secret = `${LOCAL_SECRET}:${argv.database}:admin`;
} else {
argv.secret = LOCAL_SECRET;
}
argv.secret = LOCAL_SECRET;
logger.debug(
`Set secret to '${argv.secret}' as --local was given, --secret was not, \
--database was ${argv.database ? `'${argv.database}'` : "not"}, and --role \
Expand All @@ -152,6 +143,42 @@ was ${argv.role ? `'${argv.role}'` : "not"}}`,
return argv;
}

/**
* Mutates argv.secret appropriately when --database and/or --role are
* provided along with --secret.
* @param {import('yargs').Arguments} argv
* @returns {import('yargs').Arguments}
*/
export function scopeSecret(argv) {
const logger = container.resolve("logger");
if (argv.secret) {
if (argv.database) {
// If --database path is provided with --secret, scope the secret.
// A default role must be provided.
const role = argv.role || "admin";
const debuggableSecret = `${redact(argv.secret)}:${argv.database}:${role}`;
argv.secret = `${argv.secret}:${argv.database}:${role}`;

logger.debug(
`Applying scope to secret '${debuggableSecret}', since --database was '${argv.database}' ${argv.role ? `with --role '${argv.role}'` : "with default role 'admin'"}`,
"argv",
argv,
);
} else if (argv.role) {
// If --role is provided with --secret, scope the secret to the role
const debuggableSecret = `${redact(argv.secret)}:${argv.role}`;
argv.secret = `${argv.secret}:${argv.role}`;

logger.debug(
`Applying scope to secret '${debuggableSecret}', since --role was '${argv.role}'"`,
"argv",
argv,
);
}
}
return argv;
}

/**
* Mutates argv.include appropriately for query options
* @param {Object} argv
Expand Down
5 changes: 2 additions & 3 deletions src/lib/options.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const ACCOUNT_OPTIONS = {
role: {
alias: "r",
type: "string",
description: "Role used to run the command. Can't be used with --secret.",
description: "Role used to run the command.",
group: "API:",
},
};
Expand Down Expand Up @@ -67,8 +67,7 @@ export const CORE_OPTIONS = {
},
secret: {
type: "string",
description:
"Secret used for authentication. Can't be used with --database or --role.",
description: "Secret used for authentication.",
required: false,
group: "API:",
},
Expand Down
2 changes: 1 addition & 1 deletion src/lib/schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export async function getAllSchemaFileContents(
const promises = [];
/** @type Record<string, string> */
const fileContentCollection = {};
const secret = await getSecret();
const secret = await getSecret(argv);

const params = new URLSearchParams({
version: version,
Expand Down
48 changes: 0 additions & 48 deletions test/commands/database/database.mjs

This file was deleted.

2 changes: 1 addition & 1 deletion test/commands/login.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ describe("login", function () {
const sampleCreds = btoa(JSON.stringify({ code: "asdf", state: "state" }));
input.resolves(sampleCreds);

await run(`login --no-redirect=true`, container);
await run(`login --no-redirect`, container);
const oauthClient = container.resolve("oauthClient");
const logger = container.resolve("logger");
const credentials = container.resolve("credentials");
Expand Down
Loading

0 comments on commit b2da774

Please sign in to comment.