From 063c529455927e554563e477cb1ddbaaa5c4971a Mon Sep 17 00:00:00 2001 From: erxclau Date: Wed, 14 Jun 2023 16:18:54 -0400 Subject: [PATCH] Add programmatic service account command --- README.md | 10 ++++++++ package.json | 1 + src/sink-auth.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sink.js | 3 ++- yarn.lock | 7 +++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/sink-auth.js diff --git a/README.md b/README.md index f33b3d8..d500390 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,16 @@ In order to create a service account key file (i.e., `.sink-google-auth-service- For security purposes, the service account and associated client email should be regenerated periodically. +#### Programmatic service account creation + +It may be useful to programmatically create service accounts. For example, you may want to share different Google Drive files with different service account emails. + +1. Create a project and add a service account to it. Make note of the service account email address. +2. Enable the Identity and Access Management (IAM) API for the project. +3. Under the project IAM tab, grant access for the "Service Account Admin" and "Service Account Key Admin" roles to the service account email address as principal. +4. Add a new key to the service account and download the JSON credentials file. +5. Run `yarn sink auth --credentials --account-id `. This will output a new credentials file at `./sink-google-auth-service-account-.json`. + ## AWS S3 deployment with cache invalidation Create a configuration file. The file should have a `deployment` property with an object value. The value should include the following properties: `region`, `bucket`, `key`, `build`, and `profile`. The value can optionally include a `distribution` property. diff --git a/package.json b/package.json index baf875b..9d2df68 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@aws-sdk/client-s3": "^3.352.0", "@aws-sdk/credential-providers": "^3.352.0", "@googleapis/drive": "^5.1.0", + "@googleapis/iam": "^7.1.0", "@googleapis/sheets": "^4.0.2", "archieml": "^0.5.0", "chalk": "^5.2.0", diff --git a/src/sink-auth.js b/src/sink-auth.js new file mode 100644 index 0000000..818649f --- /dev/null +++ b/src/sink-auth.js @@ -0,0 +1,66 @@ +import { fileURLToPath } from "node:url"; +import { writeFileSync } from "node:fs"; + +import { program } from "commander"; +import { fatal_error, get_auth } from "./_utils.js"; +import { iam } from "@googleapis/iam"; + +const self = fileURLToPath(import.meta.url); + +const main = async ({ credentials, accountId }) => { + const scopes = ["https://www.googleapis.com/auth/cloud-platform"]; + const authObject = get_auth(credentials, scopes); + const admin = iam({ version: "v1", auth: authObject }); + + const projectId = await authObject.getProjectId(); + + let serviceAccount; + try { + serviceAccount = await admin.projects.serviceAccounts.create({ + name: `projects/${projectId}`, + requestBody: { + accountId, + }, + }); + } catch (e) { + fatal_error(` + Error when creating service account ${accountId} in ${projectId}. + ${e.stack} + `); + } + + let serviceAccountKey; + try { + if (serviceAccount.data.name) { + serviceAccountKey = await admin.projects.serviceAccounts.keys.create({ + name: serviceAccount.data.name + }) + } + } catch (e) { + fatal_error(` + Error when creating service account key for ${serviceAccount.data.displayName}. + ${e.stack} + `) + } + + writeFileSync( + `./sink-google-auth-service-account-${accountId}.json`, + Buffer.from(serviceAccountKey.data.privateKeyData, "base64").toString("utf-8") + ) +}; + +if (process.argv[1] === self) { + program + .version("2.8.0") + .requiredOption( + "-c, --credentials ", + "path to the project's service account credentials file" + ) + .requiredOption( + "-a, --account-id ", + "account id that is used to generate the service account email address and a stable unique id. It is unique within a project, must be 6-30 characters long, and match the regular expression [a-z]([-a-z0-9]*[a-z0-9]) to comply with RFC1035." + ) + .parse(); + + main(program.opts()); +} diff --git a/src/sink.js b/src/sink.js index 1f719fa..59b7287 100755 --- a/src/sink.js +++ b/src/sink.js @@ -11,6 +11,7 @@ program .command("json", "fetch JSON files from Google Drive") .command("text", "fetch text files from Google Drive") .command("fetch", "fetch all Google Docs and Sheets") - .command("deploy", "deploy a build directory to AWS S3"); + .command("deploy", "deploy a build directory to AWS S3") + .command("auth", "create a GCP service account credential file") program.parse(process.argv); diff --git a/yarn.lock b/yarn.lock index 2e0cebd..36bafb9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1072,6 +1072,13 @@ dependencies: googleapis-common "^6.0.3" +"@googleapis/iam@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@googleapis/iam/-/iam-7.1.0.tgz#022fb2b5cfd44fc58b36eb19805961ce4a98bef4" + integrity sha512-eau3JwYbKYfRH1S3LnMBfAM8OrSP3xNocZkLnQBV5ion4N/njK6ZAYYwHpFPhdFAGZoEnJmkuB5gggObPk9oLA== + dependencies: + googleapis-common "^6.0.3" + "@googleapis/sheets@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@googleapis/sheets/-/sheets-4.0.2.tgz#8b6218cab8a6a242a45df1d5581e38c46adfdaf2"