diff --git a/packages/internal-cli/README.md b/packages/internal-cli/README.md index 8b58149c0..f00a67fb0 100644 --- a/packages/internal-cli/README.md +++ b/packages/internal-cli/README.md @@ -26,26 +26,24 @@ cd packages/internal-cli 2. Run the script ```bash -pnpm run cli +pnpm run cli +``` + +# Example provider JSON file + +```json +{ + "github": { + "client_id": "" + }, + "slack": { + "client_id": "" + } +} ``` # Options -## Environment - -`-e` or `--environment` - -| Option | Description | Default | -| ------------- | --------------------------- | ------- | -| `development` | The development environment | x | -| `production` | The production environment | | - -Example: - -```bash -pnpm run cli -e production -``` - ## Pizzly host `-p` or `--pizzlyhost` @@ -56,7 +54,7 @@ Pass the base url for Pizzly, defaults to `http://localhost:3004`. `-s` or `--pizzlysecretkey` -Pass the secret key for Pizzly, defaults to undefined which will work locally in the default configuration. +Pass the secret key for Pizzly, defaults to undefined which will work locally in the default configuration. In production you will want to set a Pizzly secret key, see their docs for details. ## AWS profile diff --git a/packages/internal-cli/src/index.ts b/packages/internal-cli/src/index.ts index 0d942be82..6c24a09f8 100644 --- a/packages/internal-cli/src/index.ts +++ b/packages/internal-cli/src/index.ts @@ -10,25 +10,61 @@ import { } from "@aws-sdk/client-secrets-manager"; import fetch from "node-fetch"; +const providersSchema = z.record( + z.string(), + z.object({ + client_id: z.string(), + }) +); + const program = new Command(); program .command("update") .description("Update the catalog") - .option("-e, --environment ", "The environment to update") + .argument( + "", + "The file path to the integration file. Probably ../../apps/webapp/integrations.yml" + ) .option("-p, --pizzlyhost ", "Pizzly host") .option("-s, --pizzlysecretkey ", "Pizzly secret key") .option("-a, --awsprofile ", "AWS profile name") .action( - async (options: { - environment?: string; - pizzlyhost?: string; - pizzlysecretkey?: string; - awsprofile?: string; - }) => { - const environment = options.environment ?? "development"; - const pizzly_host = options.pizzlyhost ?? "http://localhost:3004"; + async ( + integration_file_path: string, + options: { + pizzlyhost?: string; + pizzlysecretkey?: string; + awsprofile?: string; + } + ) => { + if (!integration_file_path) { + console.error( + "Missing integration file path.", + `You need to pass in the path to a JSON file which has this format: + { + "github": { + "client_id": "" + } + } + ` + ); + return; + } + const file = fs.readFileSync(integration_file_path, "utf8"); + const json = JSON.parse(file); + const result = providersSchema.safeParse(json); + + if (!result.success) { + console.error( + `Integration file ${integration_file_path} is in the wrong file format`, + result.error.format() + ); + return; + } + const authProviders = result.data; + const pizzly_host = options.pizzlyhost ?? "http://localhost:3004"; const providers = getProviders(true); const client = new SecretsManagerClient({ @@ -36,74 +72,81 @@ program credentials: fromIni({ profile: options.awsprofile ?? "default" }), }); - const promises = providers.map(async (integration) => { - if (integration.authentication.type !== "oauth") - return Promise.resolve(); + const promises = Object.entries(authProviders).map( + async ([service, authentication]) => { + const environmentClientId = authentication.client_id; - const environmentClientId = - integration.authentication.environments[environment!]?.client_id; - - if (!environmentClientId) { - console.log(`No client id for ${integration.slug} in ${environment}`); - console.log("Skipping…"); - return Promise.resolve(); - } - - const secretId = `integrations/${integration.slug}/${environmentClientId}`; - try { - console.log(`Finding secret for id: ${secretId}`); - - const response = await client.send( - new GetSecretValueCommand({ - SecretId: secretId, - VersionStage: "AWSCURRENT", // VersionStage defaults to AWSCURRENT if unspecified - }) - ); - - const secretData = response.SecretString; - invariant(secretData, `Secret data is missing: ${secretId}`); - const secretObject = JSON.parse(secretData); - const { client_secret } = z - .object({ - client_secret: z.string(), - }) - .parse(secretObject); - - console.log(`Found secret for id: ${secretId}`); - - const hasExistingConfig = await hasConfig( - pizzly_host, - integration.slug, - options.pizzlysecretkey - ); - - if (hasExistingConfig) { - const response = await updateConfig( - pizzly_host, - integration.slug, - environmentClientId, - client_secret, - integration.authentication.scopes, - options.pizzlysecretkey - ); - console.log(`Updated config for ${integration.slug}`); - } else { - const response = await createConfig( - pizzly_host, - integration.slug, - environmentClientId, - client_secret, - integration.authentication.scopes, - options.pizzlysecretkey - ); - console.log(`Created config for ${integration.slug}`); + if (!environmentClientId) { + console.log(`No client id for ${service}`); + console.log("Skipping…"); + return Promise.resolve(); + } + + const provider = providers.find((p) => p.slug === service); + if (provider?.authentication.type !== "oauth") { + console.log( + `The provider ${service} is the wrong type ${provider?.authentication.type}. Must be oauth` + ); + console.log("Skipping…"); + return Promise.resolve(); + } + + const secretId = `integrations/${service}/${environmentClientId}`; + try { + console.log(`Finding secret for id: ${secretId}`); + + const response = await client.send( + new GetSecretValueCommand({ + SecretId: secretId, + VersionStage: "AWSCURRENT", // VersionStage defaults to AWSCURRENT if unspecified + }) + ); + + const secretData = response.SecretString; + invariant(secretData, `Secret data is missing: ${secretId}`); + const secretObject = JSON.parse(secretData); + const { client_secret } = z + .object({ + client_secret: z.string(), + }) + .parse(secretObject); + + console.log(`Found secret for id: ${secretId}`); + + const hasExistingConfig = await hasConfig( + pizzly_host, + service, + options.pizzlysecretkey + ); + + if (hasExistingConfig) { + const response = await updateConfig( + pizzly_host, + service, + environmentClientId, + client_secret, + provider.authentication.scopes, + options.pizzlysecretkey + ); + console.log(`Updated config for ${service}`); + } else { + const response = await createConfig( + pizzly_host, + service, + environmentClientId, + client_secret, + provider.authentication.scopes, + options.pizzlysecretkey + ); + console.log(`Created config for ${service}`); + } + } catch (error) { + // For a list of exceptions thrown, see + // https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html + throw error; } - } catch (error) { - // For a list of exceptions thrown, see - // https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html - throw error; } - }); + ); await Promise.all(promises); console.log(`Added ${promises.length} secrets`); diff --git a/packages/internal-providers/src/providers/github/index.ts b/packages/internal-providers/src/providers/github/index.ts index 02c1aa0fb..273cd9dc6 100644 --- a/packages/internal-providers/src/providers/github/index.ts +++ b/packages/internal-providers/src/providers/github/index.ts @@ -8,14 +8,6 @@ export const github = { authentication: { type: "oauth", scopes: ["repo"], - environments: { - development: { - client_id: "cd763219ce4005e58c00", - }, - production: { - client_id: "98922f3fbb27485bae70", - }, - }, }, schemas, }; diff --git a/packages/internal-providers/src/providers/slack/index.ts b/packages/internal-providers/src/providers/slack/index.ts index 66c4a1650..9d2ea2ec2 100644 --- a/packages/internal-providers/src/providers/slack/index.ts +++ b/packages/internal-providers/src/providers/slack/index.ts @@ -8,14 +8,6 @@ export const slack = { authentication: { type: "oauth", scopes: ["channels:read", "channels:join", "chat:write"], - environments: { - development: { - client_id: "276370297397.4579145654276", - }, - production: { - client_id: "276370297397.4639924595715", - }, - }, }, schemas, }; diff --git a/packages/internal-providers/src/types.ts b/packages/internal-providers/src/types.ts index 0c2d0884c..0cb8a3157 100644 --- a/packages/internal-providers/src/types.ts +++ b/packages/internal-providers/src/types.ts @@ -12,7 +12,6 @@ export type Provider = { export type OAuthAuthentication = { type: "oauth"; scopes: string[]; - environments: Record; }; export type APIKeyAuthentication = { diff --git a/providers.auth.development.json b/providers.auth.development.json new file mode 100644 index 000000000..6ff2adbd1 --- /dev/null +++ b/providers.auth.development.json @@ -0,0 +1,8 @@ +{ + "github": { + "client_id": "cd763219ce4005e58c00" + }, + "slack": { + "client_id": "276370297397.4579145654276" + } +} diff --git a/providers.auth.production.json b/providers.auth.production.json new file mode 100644 index 000000000..6be4bc342 --- /dev/null +++ b/providers.auth.production.json @@ -0,0 +1,8 @@ +{ + "github": { + "client_id": "98922f3fbb27485bae70" + }, + "slack": { + "client_id": "276370297397.4639924595715" + } +}