diff --git a/orchestrator/README.md b/orchestrator/README.md index e984de4d..6b904911 100644 --- a/orchestrator/README.md +++ b/orchestrator/README.md @@ -2,4 +2,11 @@ Orca's web server is designed to receive grading jobs from Bottlenose and push them onto the grading queue accordingly. -## Running the Server +## API Key Scripts + +Users running the orchestator can run the following scripts for interacting with API keys: +* `yarn generate-api-key -h ` +* `yarn list-api-keys -h ` +* `yarn delete-api-key -h -k ` + +**NOTE:** The `POSTGRES_URL` envirnoment variable _must be set_ in order to run these scripts. diff --git a/orchestrator/package.json b/orchestrator/package.json index b8bd6e01..11fba2f4 100644 --- a/orchestrator/package.json +++ b/orchestrator/package.json @@ -8,6 +8,8 @@ "private": "true", "scripts": { "generate-api-key": "yarn workspace @codegrade-orca/db generate-api-key", + "delete-api-key": "yarn workspace @codegrade-orca/db delete-api-key", + "list-api-keys": "yarn workspace @codegrade-orca/db list-api-keys", "server": "yarn workspace @codegrade-orca/api start", "server:dev": "yarn workspace @codegrade-orca/api dev", "image-builder": "yarn workspace @codegrade-orca/image-build-service start", diff --git a/orchestrator/packages/db/package.json b/orchestrator/packages/db/package.json index 720598e0..db355962 100644 --- a/orchestrator/packages/db/package.json +++ b/orchestrator/packages/db/package.json @@ -9,7 +9,9 @@ "scripts": { "build": "tsc --build", "check": "tsc --noEmit", - "generate-api-key": "ts-node src/scripts/generate-api-key.ts" + "generate-api-key": "ts-node src/scripts/generate-api-key.ts", + "list-api-keys": "ts-node src/scripts/list-api-keys.ts", + "delete-api-key": "ts-node src/scripts/delete-api-key.ts" }, "devDependencies": { "jest-mock-extended": "^3.0.5", diff --git a/orchestrator/packages/db/src/api-key-operations/index.ts b/orchestrator/packages/db/src/api-key-operations/index.ts index 42e58435..a9f32e23 100644 --- a/orchestrator/packages/db/src/api-key-operations/index.ts +++ b/orchestrator/packages/db/src/api-key-operations/index.ts @@ -18,4 +18,6 @@ export const createAPIKey = async (hostname: string): Promise => export const validAPIKey = async (hostname: string, value: string) => Boolean(await prismaInstance.apiKey.count({ where: { hostname, value } })) -const generateKey = (): string => randomBytes(KEY_LENGTH).toString('hex'); +// randomBytes into hex string will return a value with double the length passed +// to randomBytes +const generateKey = (): string => randomBytes(KEY_LENGTH / 2).toString('hex'); diff --git a/orchestrator/packages/db/src/scripts/delete-api-key.ts b/orchestrator/packages/db/src/scripts/delete-api-key.ts new file mode 100644 index 00000000..ef66074d --- /dev/null +++ b/orchestrator/packages/db/src/scripts/delete-api-key.ts @@ -0,0 +1,30 @@ +import { parseArgs } from "util"; +import prismaInstance from "../prisma-instance"; + +const main = async () => { + const { values } = parseArgs( + { + options: + { + hostname: { type: "string", short: "h" }, + key: { type: "string", short: "k" } + }, + } + ); + const missing = Object.entries(values).filter(([_, v]) => v === undefined).map(([k, _]) => k); + if (missing.length) { + process.stderr.write(`Missing the following options: ${missing.join(', ')}`); + process.exit(1); + } + const { hostname, key } = values; + const deletedResult = await prismaInstance.$transaction(async (tx) => await tx.apiKey.deleteMany( + { where: { hostname, value: key } } + )); + const { count } = deletedResult; + if (!count) { + process.stderr.write("The given key does not exist under the given host name."); + process.exit(1); + } +}; + +main(); diff --git a/orchestrator/packages/db/src/scripts/generate-api-key.ts b/orchestrator/packages/db/src/scripts/generate-api-key.ts index 22339cc9..aec9da67 100644 --- a/orchestrator/packages/db/src/scripts/generate-api-key.ts +++ b/orchestrator/packages/db/src/scripts/generate-api-key.ts @@ -8,7 +8,8 @@ const main = async () => { console.error("Must provide hostname for api key generation with flag '-h'."); process.exit(1); } - await createAPIKey(hostname as string); + const newKey = await createAPIKey(hostname as string); + process.stdout.write(newKey + "\n"); } main(); diff --git a/orchestrator/packages/db/src/scripts/list-api-keys.ts b/orchestrator/packages/db/src/scripts/list-api-keys.ts new file mode 100644 index 00000000..60fc6ddc --- /dev/null +++ b/orchestrator/packages/db/src/scripts/list-api-keys.ts @@ -0,0 +1,19 @@ +import { parseArgs } from "util"; +import prismaInstance from "../prisma-instance"; + +const main = async () => { + const { values } = parseArgs({ options: { hostname: { type: "string", short: "h" } } }); + const { hostname } = values; + if (!hostname) { + process.stderr.write("Must provide hostname option."); + process.exit(1); + } + const keys = await prismaInstance.apiKey.findMany({ where: { hostname } }).then((vals) => vals.map((v) => v.value)); + if (!keys.length) { + process.stderr.write("No keys found under given host name."); + process.exit(0); + } + process.stdout.write(keys.join("\n") + "\n"); +}; + +main();