cli: Expand admin command to setup basic authenticaton for gihub

Signed-off-by: Marcus Eide <eide@spotify.com>
This commit is contained in:
Marcus Eide
2023-02-09 14:33:17 +01:00
committed by Philipp Hugenroth
parent 1322688118
commit 7af6c36adf
3 changed files with 202 additions and 4 deletions
+50 -4
View File
@@ -17,15 +17,61 @@
import chalk from 'chalk';
import inquirer from 'inquirer';
import { Task } from '../../lib/tasks';
import { github } from './github';
import { updateConfigFile, updateEnvFile } from './file';
export async function command(): Promise<void> {
const answers = await inquirer.prompt<{ github: boolean }>([
const answers = await inquirer.prompt<{
shouldSetupAuth: boolean;
useEnvForSecrets?: boolean;
provider?: string;
}>([
{
type: 'confirm',
name: 'github',
message: chalk.blue('Do you want to set up GitHub Authentication?'),
name: 'shouldSetupAuth',
message: chalk.blue(
'Do you want to set up Authentication for this project?',
),
},
{
type: 'confirm',
name: 'useEnvForSecrets',
message:
'Would you like to store sensitive configuration details such as secrets as environment variables? (recommended)',
when: ({ shouldSetupAuth }) => shouldSetupAuth,
},
{
type: 'list',
name: 'provider',
message: 'Please select a provider:',
choices: ['github'],
when: ({ shouldSetupAuth }) => shouldSetupAuth,
},
]);
Task.log(answers.github ? 'Ok!' : 'Maybe next time');
if (!answers.shouldSetupAuth) {
// TODO(eide): Can we add a Task.warning() method?
console.log(
chalk.yellow(
'If you change your mind, feel free to re-run this command.',
),
);
process.exit(1);
}
switch (answers.provider) {
case 'github': {
const { useEnvForSecrets } = answers;
const config = await github(useEnvForSecrets);
await updateConfigFile(config);
if (useEnvForSecrets) {
await updateEnvFile(config);
}
break;
}
default:
throw new Error(`Provider ${answers.provider} not implemented yet.`);
}
Task.log(`Done setting up ${answers.provider}!`);
}
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as path from 'path';
import * as fs from 'fs-extra';
import yaml from 'yaml';
import { findPaths } from '@backstage/cli-common';
import { GithubAuthConfig } from './github';
/* eslint-disable-next-line no-restricted-syntax */
const { targetRoot } = findPaths(__dirname);
const APP_CONFIG_FILE = path.join(targetRoot, 'app-config.development.yaml');
const ENV_CONFIG_FILE = path.join(targetRoot, '.env.development');
// export const readAppConfig = async (file: string) => {
// return yaml.parse(await fs.readFile(file, 'utf8'));
// };
export const updateConfigFile = async (config: GithubAuthConfig) => {
return await fs.writeFile(
APP_CONFIG_FILE,
yaml.stringify(config, {
indent: 2,
}),
'utf8',
);
};
export const updateEnvFile = async (config: GithubAuthConfig) => {
const content = `
AUTH_GITHUB_CLIENT_ID=${config.auth.providers.github.clientId}
AUTH_GITHUB_CLIENT_SECRET=${config.auth.providers.github.clientId}`;
return await fs.writeFile(ENV_CONFIG_FILE, content, 'utf8');
};
+104
View File
@@ -0,0 +1,104 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import chalk from 'chalk';
import inquirer from 'inquirer';
import { Task } from '../../lib/tasks';
export type GithubAuthConfig = {
auth: {
providers: {
github: {
clientId: string;
clientSecret: string;
enterpriseInstanceUrl?: string;
};
};
};
};
export const github = async (
useEnvForSecrets?: boolean,
): Promise<GithubAuthConfig> => {
Task.log(`
To add GitHub authentication, you must create an OAuth App from the GitHub developer settings: ${chalk.blue(
'https://github.com/settings/developers',
)}
The Homepage URL should point to Backstage's frontend, while the Authorization callback URL will point to the auth backend.
You can find the full documentation page here: ${chalk.blue(
'https://backstage.io/docs/auth/github/provider',
)}
`);
const answers = await inquirer.prompt<{
clientSecret: string;
clientId: string;
hasGithubEnterprise: boolean;
enterpriseInstanceUrl?: string;
}>([
{
type: 'input',
name: 'clientSecret',
message: 'What is your Client Secret?',
// TODO(eide): Is there another way to validate?
// validate(input) {
// if (/([a-f0-9]{40})/g.test(input)) {
// return true;
// }
// throw Error('Please provide a valid client secret.');
// },
},
{
type: 'input',
name: 'clientId',
message: 'What is your Client Id?',
},
{
type: 'confirm',
name: 'hasGithubEnterprise',
message: 'Are you using Github Enterprise?',
},
{
type: 'input',
name: 'enterpriseInstanceUrl',
message: 'What is your URL for Github Enterprise?',
when: ({ hasGithubEnterprise }) => hasGithubEnterprise,
validate(input: string) {
return Boolean(new URL(input));
},
},
]);
return {
auth: {
providers: {
github: {
clientId: useEnvForSecrets
? '${AUTH_GITHUB_CLIENT_ID}'
: answers.clientId,
clientSecret: useEnvForSecrets
? '${AUTH_GITHUB_CLIENT_SECRET}'
: answers.clientSecret,
...(answers.hasGithubEnterprise && {
enterpriseInstanceUrl: answers.enterpriseInstanceUrl,
}),
},
},
},
};
};