From 76342ae0c3969ece4bea0f0bbcc1776b8e204f51 Mon Sep 17 00:00:00 2001 From: Sean Grove Date: Sun, 1 Aug 2021 18:01:52 -0700 Subject: [PATCH] Bump to new netlify-functions package with better TypeScript defs, show metaprogramming for auth scope detection at runtime. --- lib.ts | 21 +++++++++++++++ netlify/functions/addIssueComment.ts | 25 +++++++++++++++++- netlify/functions/conditionalSecrets.ts | 33 ++++------------------- netlify/functions/secrets.ts | 35 +++++-------------------- package-lock.json | 15 ++++++----- package.json | 2 +- public/index.html | 1 + 7 files changed, 66 insertions(+), 66 deletions(-) create mode 100644 lib.ts create mode 100644 public/index.html diff --git a/lib.ts b/lib.ts new file mode 100644 index 0000000..e285af6 --- /dev/null +++ b/lib.ts @@ -0,0 +1,21 @@ +// This function replaces all but the last four digits of a string with zeroes +const sanitizeToken = (str: string, length = 16): string => { + const len = str.length; + const displayed = len > 4 ? str.substr(len - 4, len) : str; + + const padLength = length - displayed.length; + return displayed.padStart(padLength, "*"); +}; + +export const formatSecret = (secret: any): any => { + if (!secret) { + return; + } + + return { + bearerToken: secret.bearerToken ? sanitizeToken(secret.bearerToken) : null, + friendlyServiceName: secret.friendlyServiceName, + service: secret.service, + grantedScopes: secret.grantedScopes?.map((scope: any) => scope.scope), + }; +}; diff --git a/netlify/functions/addIssueComment.ts b/netlify/functions/addIssueComment.ts index d952a61..0c24cf7 100644 --- a/netlify/functions/addIssueComment.ts +++ b/netlify/functions/addIssueComment.ts @@ -2,6 +2,8 @@ import { withSecrets } from "@sgrove/netlify-functions"; import { Octokit } from "@octokit/core"; import { restEndpointMethods } from "@octokit/plugin-rest-endpoint-methods"; +const sufficientScopes = ["public_repo", "repo"]; + export const handler = withSecrets(async (event, { secrets }) => { const issueComment = ( event.queryStringParameters.comment || "" @@ -31,6 +33,27 @@ export const handler = withSecrets(async (event, { secrets }) => { }; } + const tokenHasScope = secrets.gitHub.grantedScopes?.some((grantedScope) => + sufficientScopes.includes(grantedScope.scope) + ); + + // Libraries, applications, and packages can metaprogram checks at run time to make the DX + // buttery smooth for the end developers. Notice here how we tell the developer exactly what's missing. + // With just a bit more work, we could even link them directly into the dashboard to fix this in two clicks. + if (!tokenHasScope) { + return { + statusCode: 412, + body: JSON.stringify({ + error: `You have enabled GitHub auth in your Authlify dashboard, but it's missing a required scope. The auth must have one (or both) of the scopes: ${sufficientScopes.join( + ", " + )}`, + }), + headers: { + "Content-Type": "application/json", + }, + }; + } + const MyOctokit = Octokit.plugin(restEndpointMethods); const octokit = new MyOctokit({ auth: secrets.gitHub?.bearerToken }); @@ -43,7 +66,7 @@ export const handler = withSecrets(async (event, { secrets }) => { return { statusCode: 200, - body: JSON.stringify(result.data), + body: JSON.stringify(result), headers: { "Content-Type": "application/json", }, diff --git a/netlify/functions/conditionalSecrets.ts b/netlify/functions/conditionalSecrets.ts index ce80e21..7161e08 100644 --- a/netlify/functions/conditionalSecrets.ts +++ b/netlify/functions/conditionalSecrets.ts @@ -1,13 +1,5 @@ import { getSecrets, NetlifySecrets } from "@sgrove/netlify-functions"; - -// This function replaces all but the last four digits of a string with zeroes -const sanitizeToken = (str: string, length = 16): string => { - const len = str.length; - const displayed = len > 4 ? str.substr(len - 4, len) : str; - - const padLength = length - displayed.length; - return displayed.padStart(padLength, "*"); -}; +import { formatSecret } from "../../lib"; export const handler = async (event) => { const skipSecrets = event.queryStringParameters.skipSecrets === "true"; @@ -16,28 +8,13 @@ export const handler = async (event) => { if (!skipSecrets) { secrets = await getSecrets(); - const { gitHub, salesforce, spotify } = secrets; // Sanitize the secrets before showing them to the user secrets = { - gitHub: { - ...gitHub, - bearerToken: gitHub?.bearerToken - ? sanitizeToken(gitHub.bearerToken) - : null, - }, - salesforce: { - ...salesforce, - bearerToken: salesforce?.bearerToken - ? sanitizeToken(salesforce.bearerToken) - : null, - }, - spotify: { - ...spotify, - bearerToken: spotify?.bearerToken - ? sanitizeToken(spotify.bearerToken) - : null, - }, + gitHub: formatSecret(secrets.gitHub), + salesforce: formatSecret(secrets.salesforce), + spotify: formatSecret(secrets.spotify), + stripe: formatSecret(secrets.stripe), }; } diff --git a/netlify/functions/secrets.ts b/netlify/functions/secrets.ts index 9d2fc0a..062c3d4 100644 --- a/netlify/functions/secrets.ts +++ b/netlify/functions/secrets.ts @@ -1,37 +1,14 @@ import { NetlifySecrets, withSecrets } from "@sgrove/netlify-functions"; - -// This function replaces all but the last four digits of a string with zeroes -const sanitizeToken = (str: string, length = 16): string => { - const len = str.length; - const displayed = len > 4 ? str.substr(len - 4, len) : str; - - const padLength = length - displayed.length; - return displayed.padStart(padLength, "*"); -}; +import { formatSecret } from "../../lib"; export const handler = withSecrets(async (event, { secrets }) => { - const { gitHub, salesforce, spotify } = secrets; - + console.log("All secrets JSON: ", JSON.stringify(secrets, null, 2)); // Sanitize the secrets before showing them to the user const sanitizedSecrets: NetlifySecrets = { - gitHub: { - ...gitHub, - bearerToken: gitHub?.bearerToken - ? sanitizeToken(gitHub.bearerToken) - : null, - }, - salesforce: { - ...salesforce, - bearerToken: salesforce?.bearerToken - ? sanitizeToken(salesforce.bearerToken) - : null, - }, - spotify: { - ...spotify, - bearerToken: spotify?.bearerToken - ? sanitizeToken(spotify.bearerToken) - : null, - }, + gitHub: formatSecret(secrets.gitHub), + salesforce: formatSecret(secrets.salesforce), + spotify: formatSecret(secrets.spotify), + stripe: formatSecret(secrets.stripe), }; return { diff --git a/package-lock.json b/package-lock.json index 46363a4..edfa732 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@octokit/core": "^3.5.1", "@octokit/plugin-rest-endpoint-methods": "^5.5.2", - "@sgrove/netlify-functions": "^0.7.3-handle-secrets.3" + "@sgrove/netlify-functions": "^0.7.3-handle-secrets.7" }, "devDependencies": { "@octokit/openapi-types": "^9.2.0", @@ -108,9 +108,10 @@ } }, "node_modules/@sgrove/netlify-functions": { - "version": "0.7.3-handle-secrets.3", - "resolved": "https://registry.npmjs.org/@sgrove/netlify-functions/-/netlify-functions-0.7.3-handle-secrets.3.tgz", - "integrity": "sha512-qzxZqRX9mnraNJF/TqYQm1fycsSVrQM0VNtlLanXhCkdLwOSTuKU8/MaaMZu319yblohfEpSt+HmGJE+JGz0tQ==", + "version": "0.7.3-handle-secrets.7", + "resolved": "https://registry.npmjs.org/@sgrove/netlify-functions/-/netlify-functions-0.7.3-handle-secrets.7.tgz", + "integrity": "sha512-JSb0+7VwG5sWUdDU6lk63+zmWLiTdeqMEPxksMp3hHeoqkxz8WKTglublqy9aUMWswIAl5zlQeKzX7iTmaXYDA==", + "license": "MIT", "dependencies": { "is-promise": "^4.0.0" }, @@ -263,9 +264,9 @@ } }, "@sgrove/netlify-functions": { - "version": "0.7.3-handle-secrets.3", - "resolved": "https://registry.npmjs.org/@sgrove/netlify-functions/-/netlify-functions-0.7.3-handle-secrets.3.tgz", - "integrity": "sha512-qzxZqRX9mnraNJF/TqYQm1fycsSVrQM0VNtlLanXhCkdLwOSTuKU8/MaaMZu319yblohfEpSt+HmGJE+JGz0tQ==", + "version": "0.7.3-handle-secrets.7", + "resolved": "https://registry.npmjs.org/@sgrove/netlify-functions/-/netlify-functions-0.7.3-handle-secrets.7.tgz", + "integrity": "sha512-JSb0+7VwG5sWUdDU6lk63+zmWLiTdeqMEPxksMp3hHeoqkxz8WKTglublqy9aUMWswIAl5zlQeKzX7iTmaXYDA==", "requires": { "is-promise": "^4.0.0" } diff --git a/package.json b/package.json index 9936113..3991c72 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "@octokit/core": "^3.5.1", "@octokit/plugin-rest-endpoint-methods": "^5.5.2", - "@sgrove/netlify-functions": "^0.7.3-handle-secrets.3" + "@sgrove/netlify-functions": "^0.7.3-handle-secrets.7" }, "devDependencies": { "@octokit/openapi-types": "^9.2.0", diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..bdccc54 --- /dev/null +++ b/public/index.html @@ -0,0 +1 @@ +

Hi there!

\ No newline at end of file