Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve credential handling #432

Merged
merged 25 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fff1863
refresh credentials during fetch calls
mwilde345 Nov 22, 2024
15096a3
Merge branch 'v3' into no-preflight
mwilde345 Nov 22, 2024
7be933c
don't create dupe keys
mwilde345 Nov 22, 2024
b25d691
Update src/config/setup-test-container.mjs
mwilde345 Nov 22, 2024
f0cf18e
logger component
mwilde345 Nov 23, 2024
c8b5174
Merge branch 'v3' into no-preflight
mwilde345 Nov 23, 2024
2868551
Merge branch 'v3' into no-preflight
mwilde345 Nov 23, 2024
d1f0dc0
refactor credentials
mwilde345 Nov 26, 2024
4f1e31d
refactor credentials
mwilde345 Nov 26, 2024
1aa6db2
separate concerns of account and database creds into separate classes
mwilde345 Nov 26, 2024
9792a2b
revert create database changes
mwilde345 Nov 26, 2024
32e1a0f
get rid of authnz middleware
mwilde345 Nov 26, 2024
6c86634
further separate concerns
mwilde345 Nov 26, 2024
fa472a5
Merge branch 'v3' into no-preflight
mwilde345 Nov 26, 2024
7ac122f
fix directory for creds. add some env var options
mwilde345 Nov 26, 2024
8ab062a
fix lint
mwilde345 Nov 26, 2024
48ba039
skip busted test
mwilde345 Nov 26, 2024
b0c6dd9
unsafe accessor fix
mwilde345 Nov 26, 2024
dc96e44
try test fix
mwilde345 Nov 26, 2024
d6f9d3a
Merge branch 'v3' into no-preflight
mwilde345 Nov 26, 2024
4d9d837
Merge branch 'v3' into no-preflight
mwilde345 Nov 26, 2024
c35346e
fix tests
mwilde345 Nov 26, 2024
ff7cbf4
don't set a default role in the args. resolve it in credentials. mock…
mwilde345 Nov 27, 2024
ca34e38
readme and remove .fauna dir
mwilde345 Nov 27, 2024
28bfeff
don't use exit just throw an error
mwilde345 Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import chalk from "chalk";
import yargs from "yargs";

import keyCommand from "./commands/key.mjs";
import databaseCommand from "./commands/database/database.mjs";
import evalCommand from "./commands/eval.mjs";
import shellCommand from "./commands/shell.mjs";
import keyCommand from "./commands/key.mjs";
import loginCommand from "./commands/login.mjs";
import schemaCommand from "./commands/schema/schema.mjs";
import databaseCommand from "./commands/database/database.mjs";

import { authNZMiddleware } from "./lib/auth/authNZ.mjs";
import shellCommand from "./commands/shell.mjs";
import { checkForUpdates, fixPaths, logArgv } from "./lib/middleware.mjs";
import { cleanupSecretsFile } from "./lib/auth/authNZ.mjs";

/** @typedef {import('awilix').AwilixContainer<import('./config/setup-container.mjs').modifiedInjectables>} cliContainer */

Expand All @@ -36,8 +35,10 @@ export async function run(argvInput, _container) {
builtYargs = buildYargs(argvInput);
await parseYargs(builtYargs);
} catch (e) {
let subMessage = chalk.reset("Use 'fauna <command> --help' for more information about a command.");

let subMessage = chalk.reset(
"Use 'fauna <command> --help' for more information about a command.",
);

if (argvInput.length > 0) {
subMessage = chalk.red(e.message);
}
Expand Down Expand Up @@ -97,7 +98,7 @@ function buildYargs(argvInput) {
return yargsInstance
.scriptName("fauna")
.middleware([checkForUpdates, logArgv], true)
.middleware([fixPaths, authNZMiddleware], false)
.middleware([fixPaths, cleanupSecretsFile], false)
.command("eval", "evaluate a query", evalCommand)
.command("shell", "start an interactive shell", shellCommand)
.command("login", "login via website", loginCommand)
Expand Down
90 changes: 90 additions & 0 deletions src/commands/database.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//@ts-check

import { container } from "../cli.mjs";
import { performQuery } from "./eval.mjs";

async function listDatabases(profile) {
const logger = container.resolve("logger");
const AccountClient = container.resolve("AccountClient");
logger.stdout("Listing Databases...");
const databases = await new AccountClient(profile).listDatabases();
logger.stdout(databases);
}

async function createDatabase(argv) {
const client = await container.resolve("getSimpleClient")(argv);
const logger = container.resolve("logger");
// performQuery only gives us an error code if we ask for the json-tagged format.
// so gotta go deeper to keep it agnostic of the format.
// And it can't be only on initial client init, because repl needs to
// refresh on the fly w/out kicking out.
const result = await performQuery(client, "1 + 1", undefined, {
...argv,
format: "json-tagged",
});
const result2 = await performQuery(client, "2 + 2", undefined, {
...argv,
format: "json-tagged",
});
logger.stdout(result, result2);
}

function buildDatabaseCommand(yargs) {
return yargs
.positional("method", {
type: "string",
choices: ["create", "list", "delete"],
describe: "choose a method to interact with your databases",
})
.options({
profile: {
type: "string",
description: "a user profile",
default: "default",
},
authRequired: {
default: true,
},
url: {
type: "string",
description: "the Fauna URL to query",
default: "https://db.fauna.com:443",
},
secret: {
type: "string",
description: "the secret to use",
// default: "somesecret",
},
})
.help("help", "show help")
.example([["$0 db list"]]);
}

async function databaseHandler(argv) {
const logger = container.resolve("logger");
const method = argv.method;
let result;
switch (method) {
case "create":
logger.stdout("Creating database...");
result = await createDatabase(argv);
break;
case "delete":
logger.stdout("Deleting database...");
break;
case "list":
result = await listDatabases(argv.profile);
break;
default:
break;
}
return result;
}

export default {
command: "database <method>",
aliases: ["db"],
description: "Interact with your databases:",
builder: buildDatabaseCommand,
handler: databaseHandler,
};
1 change: 0 additions & 1 deletion src/commands/eval.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ export async function performV4Query(client, fqlQuery, outputFile, flags) {
export async function performQuery(client, fqlQuery, outputFile, argv) {
const performV4Query = container.resolve("performV4Query");
const performV10Query = container.resolve("performV10Query");

if (argv.version === "4") {
return performV4Query(client, fqlQuery, outputFile, argv);
} else {
Expand Down
3 changes: 1 addition & 2 deletions src/commands/key.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getAccountKey, getDBKey } from "../lib/auth/authNZ.mjs";
// consider an optional flag that will save this secret to the creds file, overwriting
// the existing secret if it exists at key/path/role
async function createKey(argv) {
const { database, profile, role, url } = argv;
const { database, profile, role } = argv;
const logger = container.resolve("logger");
const accountKey = await getAccountKey(profile);
// TODO: after logging in, should we list the top level databases and create db keys for them?
Expand All @@ -23,7 +23,6 @@ async function createKey(argv) {
accountKey,
path: database,
role,
url,
});
logger.stdout("got account key", accountKey);
logger.stdout("got db secret", dbSecret);
Expand Down
8 changes: 4 additions & 4 deletions src/commands/login.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import { container } from "../cli.mjs";
async function doLogin(argv) {
const logger = container.resolve("logger");
const open = container.resolve("open");
const accountClient = container.resolve("accountClient");
const AccountClient = new (container.resolve("AccountClient"))(argv.profile);
const oAuth = container.resolve("oauthClient");
const accountCreds = container.resolve("accountCreds");
oAuth.server.on("ready", async () => {
const authCodeParams = oAuth.getOAuthParams();
const dashboardOAuthURL =
await accountClient.startOAuthRequest(authCodeParams);
await AccountClient.startOAuthRequest(authCodeParams);
open(dashboardOAuthURL);
logger.stdout(`To login, open your browser to:\n ${dashboardOAuthURL}`);
});
oAuth.server.on("auth_code_received", async () => {
try {
const tokenParams = oAuth.getTokenParams();
const accessToken = await accountClient.getToken(tokenParams);
const accessToken = await AccountClient.getToken(tokenParams);
const { accountKey, refreshToken } =
await accountClient.getSession(accessToken);
await AccountClient.getSession(accessToken);
accountCreds.save({
creds: { accountKey, refreshToken },
key: argv.profile,
Expand Down
4 changes: 1 addition & 3 deletions src/config/setup-container.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@ export const injectables = {
performV4Query: awilix.asValue(performV4Query),
performV10Query: awilix.asValue(performV10Query),
getSimpleClient: awilix.asValue(getSimpleClient),
accountClient: awilix.asClass(FaunaAccountClient, {
lifetime: Lifetime.SCOPED,
}),
AccountClient: awilix.asValue(FaunaAccountClient),
oauthClient: awilix.asClass(OAuthClient, { lifetime: Lifetime.SCOPED }),
makeAccountRequest: awilix.asValue(makeAccountRequest),
makeFaunaRequest: awilix.asValue(makeFaunaRequest),
Expand Down
2 changes: 1 addition & 1 deletion src/config/setup-test-container.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function setupTestContainer() {
getSimpleClient: awilix.asValue(
stub().returns({ close: () => Promise.resolve() }),
),
accountClient: awilix.asFunction(stub()),
AccountClient: awilix.asValue(stub()),
mwilde345 marked this conversation as resolved.
Show resolved Hide resolved
oauthClient: awilix.asFunction(stub()),
accountCreds: awilix.asClass(AccountKey).scoped(),
secretCreds: awilix.asClass(SecretKey).scoped(),
Expand Down
1 change: 1 addition & 0 deletions src/lib/account.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ async function parseResponse(response, shouldThrow) {
}
switch (response.status) {
case 401:
// TODO: try and refresh creds and then redo the call, if not then throw.
throw new InvalidCredsError(message);
case 403:
throw new UnauthorizedError(message);
Expand Down
Loading
Loading