/** * @license * Copyright 2020 Balena Ltd. * * 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 Command from '@oclif/command'; import { InsufficientPrivilegesError } from './errors'; export default abstract class BalenaCommand extends Command { /** * When set to true, command will be listed in `help`, * otherwise listed in `help --verbose` with secondary commands. */ public static primary = false; /** * Require elevated privileges to run. * When set to true, command will exit with an error * if executed without root on Mac/Linux * or if executed by non-Administrator on Windows. */ public static root = false; /** * Require authentication to run. * When set to true, command will exit with an error * if user is not already logged in. */ public static authenticated = false; /** * Accept piped input. * When set to true, command will read from stdin during init * and make contents available on member `stdin`. */ public static readStdin = false; public stdin: string; /** * Throw InsufficientPrivilegesError if not root on Mac/Linux * or non-Administrator on Windows. * * Called automatically if `root=true`. * Can be called explicitly by command implementation, if e.g.: * - check should only be done conditionally * - other code needs to execute before check */ protected static async checkElevatedPrivileges() { const isElevated = await (await import('is-elevated'))(); if (!isElevated) { throw new InsufficientPrivilegesError( 'You need root/admin privileges to run this command', ); } } /** * Throw NotLoggedInError if not logged in. * * Called automatically if `authenticated=true`. * Can be called explicitly by command implementation, if e.g.: * - check should only be done conditionally * - other code needs to execute before check * * Note, currently public to allow use outside of derived commands * (as some command implementations require this. Can be made protected * if this changes). */ public static async checkLoggedIn() { await (await import('./utils/patterns')).checkLoggedIn(); } /** * Read stdin contents and make available to command. * * This approach could be improved in the future to automatically set argument * values from stdin based in configuration, minimising command implementation. */ protected async getStdin() { this.stdin = await (await import('get-stdin'))(); } protected async init() { const ctr = this.constructor as typeof BalenaCommand; if (ctr.root) { await BalenaCommand.checkElevatedPrivileges(); } if (ctr.authenticated) { await BalenaCommand.checkLoggedIn(); } if (ctr.readStdin) { await this.getStdin(); } } }