UNPKG

@google/clasp

Version:

Develop Apps Script Projects locally

132 lines (131 loc) 6.24 kB
// Copyright 2019 Google LLC // // 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 // // https://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. // This file defines the 'login' command for the clasp CLI. /** * Clasp command method bodies. */ import { Command, InvalidOptionArgumentError } from 'commander'; import { authorize, getUnauthorizedOuth2Client, getUserInfo } from '../auth/auth.js'; import { intl } from '../intl.js'; import { validateOptionInt } from './validate.js'; const DEFAULT_SCOPES = [ // Default to clasp scopes 'https://www.googleapis.com/auth/script.deployments', // Apps Script deployments 'https://www.googleapis.com/auth/script.projects', // Apps Script management 'https://www.googleapis.com/auth/script.webapp.deploy', // Apps Script Web Apps 'https://www.googleapis.com/auth/drive.metadata.readonly', // Drive metadata 'https://www.googleapis.com/auth/drive.file', // Create Drive files 'https://www.googleapis.com/auth/service.management', // Cloud Project Service Management API 'https://www.googleapis.com/auth/logging.read', // StackDriver logs 'https://www.googleapis.com/auth/userinfo.email', // User email address 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/cloud-platform', ]; export const mergeScopes = (defaultScopes, projectScopes) => { const scopes = [...defaultScopes]; if (projectScopes) { scopes.push(...projectScopes); } return [...new Set(scopes)]; }; export const parseExtraScopes = (value) => { const scopes = value.split(',').map(scope => scope.trim()); if (scopes.length === 0 || scopes.some(scope => !scope)) { throw new InvalidOptionArgumentError('must be a comma-separated list of non-empty scopes.'); } return scopes; }; export const buildScopes = ({ defaultScopes, manifestScopes, useProjectScopes, includeClaspScopes, extraScopes, }) => { let scopes = [...defaultScopes]; if (useProjectScopes) { scopes = manifestScopes ? [...manifestScopes] : scopes; if (includeClaspScopes) { scopes = mergeScopes(defaultScopes, scopes); } } if (extraScopes) { scopes = mergeScopes(scopes, extraScopes); } return scopes; }; export const command = new Command('login') .description('Log in to script.google.com') .option('--no-localhost', 'Do not run a local server, manually enter code instead') .option('--creds <file>', 'Relative path to OAuth client secret file (from GCP).') .option('--use-project-scopes', 'Use the scopes from the current project manifest. Used only when authorizing access for the run command.') .option('--include-clasp-scopes', 'Include default clasp scopes in addition to project scopes. Can only be used with --use-project-scopes.') .option('--extra-scopes <scopes>', 'Include additional OAuth scopes as a comma-separated list.', parseExtraScopes) .option('--redirect-port <port>', 'Specify a custom port for the redirect URL.', val => validateOptionInt(val, 0, 65535)) .action(async function () { const options = this.optsWithGlobals(); const auth = options.authInfo; const clasp = options.clasp; if (!auth.credentialStore) { const msg = intl.formatMessage({ id: "u1wQGD", defaultMessage: [{ type: 0, value: "No credential store found, unable to login." }] }); this.error(msg); } if (auth.credentials) { if (!options.json) { const msg = intl.formatMessage({ id: "FTrSWo", defaultMessage: [{ type: 0, value: "Warning: You seem to already be logged in." }] }); console.error(msg); } } const useLocalhost = Boolean(options.localhost); const redirectPort = options.redirectPort; const oauth2Client = getUnauthorizedOuth2Client(options.creds); if (options.includeClaspScopes && !options.useProjectScopes) { const msg = intl.formatMessage({ id: "VqKr/v", defaultMessage: [{ type: 0, value: "--include-clasp-scopes can only be used with --use-project-scopes." }] }); this.error(msg); } let manifestScopes; if (options.useProjectScopes) { const manifest = await clasp.project.readManifest(); manifestScopes = manifest.oauthScopes; } const scopes = buildScopes({ defaultScopes: DEFAULT_SCOPES, manifestScopes, useProjectScopes: options.useProjectScopes, includeClaspScopes: options.includeClaspScopes, extraScopes: options.extraScopes, }); if ((options.useProjectScopes || options.extraScopes) && !options.json) { const scopesLabel = intl.formatMessage({ id: "S0Kswv", defaultMessage: [{ type: 0, value: "Authorizing with the following scopes:" }] }); console.log(''); console.log(scopesLabel); for (const scope of scopes) { console.log(scope); } } const credentials = await authorize({ store: auth.credentialStore, userKey: auth.user, oauth2Client, scopes, noLocalServer: !useLocalhost, redirectPort, }); const user = await getUserInfo(credentials); if (options.json) { const output = { email: user === null || user === void 0 ? void 0 : user.email, }; console.log(JSON.stringify(output, null, 2)); return; } const msg = intl.formatMessage({ id: "sZ9k34", defaultMessage: [{ type: 5, value: "email", options: { undefined: { value: [{ type: 0, value: "You are logged in as an unknown user." }] }, other: { value: [{ type: 0, value: "You are logged in as " }, { type: 1, value: "email" }, { type: 0, value: "." }] } } }] }, { email: user === null || user === void 0 ? void 0 : user.email, }); console.log(msg); });