2021-12-20 18:04:00 +01:00
import * as chalk from "chalk" ;
2024-01-31 23:17:04 +01:00
import { program , Command , OptionValues } from "commander" ;
2024-05-27 03:03:23 +02:00
import { firstValueFrom } from "rxjs" ;
2018-05-14 17:15:54 +02:00
2023-02-06 22:53:37 +01:00
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status" ;
2018-05-15 05:16:59 +02:00
2023-02-06 22:53:37 +01:00
import { LockCommand } from "./auth/commands/lock.command" ;
import { LoginCommand } from "./auth/commands/login.command" ;
import { LogoutCommand } from "./auth/commands/logout.command" ;
import { UnlockCommand } from "./auth/commands/unlock.command" ;
2024-05-27 03:03:23 +02:00
import { BaseProgram } from "./base-program" ;
2022-03-03 18:24:41 +01:00
import { CompletionCommand } from "./commands/completion.command" ;
2021-12-20 18:04:00 +01:00
import { EncodeCommand } from "./commands/encode.command" ;
import { StatusCommand } from "./commands/status.command" ;
2022-11-18 13:20:19 +01:00
import { UpdateCommand } from "./commands/update.command" ;
import { Response } from "./models/response" ;
import { MessageResponse } from "./models/response/message.response" ;
2024-06-17 17:00:19 +02:00
import { ConfigCommand } from "./platform/commands/config.command" ;
2023-03-10 21:39:46 +01:00
import { GenerateCommand } from "./tools/generate.command" ;
2021-12-20 18:04:00 +01:00
import { CliUtils } from "./utils" ;
2023-01-31 22:08:37 +01:00
import { SyncCommand } from "./vault/sync.command" ;
2018-05-14 20:54:19 +02:00
2018-08-06 15:38:17 +02:00
const writeLn = CliUtils . writeLn ;
2018-05-16 20:47:03 +02:00
2024-05-27 03:03:23 +02:00
export class Program extends BaseProgram {
2021-12-20 18:04:00 +01:00
async register() {
program
. option ( "--pretty" , "Format output. JSON is tabbed with two spaces." )
. option ( "--raw" , "Return raw output instead of a descriptive message." )
. option ( "--response" , "Return a JSON formatted version of response output." )
. option ( "--cleanexit" , "Exit with a success exit code (0) unless an error is thrown." )
. option ( "--quiet" , "Don't return anything to stdout." )
. option ( "--nointeraction" , "Do not prompt for interactive user input." )
. option ( "--session <session>" , "Pass session key instead of reading from env." )
2024-05-15 16:09:24 +02:00
. version (
await this . serviceContainer . platformUtilsService . getApplicationVersion ( ) ,
"-v, --version" ,
) ;
2021-12-20 18:04:00 +01:00
program . on ( "option:pretty" , ( ) = > {
process . env . BW_PRETTY = "true" ;
} ) ;
program . on ( "option:raw" , ( ) = > {
process . env . BW_RAW = "true" ;
} ) ;
program . on ( "option:quiet" , ( ) = > {
process . env . BW_QUIET = "true" ;
} ) ;
program . on ( "option:response" , ( ) = > {
process . env . BW_RESPONSE = "true" ;
} ) ;
program . on ( "option:cleanexit" , ( ) = > {
process . env . BW_CLEANEXIT = "true" ;
} ) ;
program . on ( "option:nointeraction" , ( ) = > {
process . env . BW_NOINTERACTION = "true" ;
} ) ;
2024-05-08 20:48:49 +02:00
program . on ( "option:session" , async ( key ) = > {
2021-12-20 18:04:00 +01:00
process . env . BW_SESSION = key ;
2024-05-08 20:48:49 +02:00
// once we have the session key, we can set the user key in memory
2024-05-15 16:09:24 +02:00
const activeAccount = await firstValueFrom (
this . serviceContainer . accountService . activeAccount $ ,
) ;
2024-05-08 20:48:49 +02:00
if ( activeAccount ) {
2024-05-15 16:09:24 +02:00
await this . serviceContainer . userAutoUnlockKeyService . setUserKeyInMemoryIfAutoUserKeySet (
2024-05-08 20:48:49 +02:00
activeAccount . id ,
) ;
}
2021-12-20 18:04:00 +01:00
} ) ;
program . on ( "command:*" , ( ) = > {
writeLn ( chalk . redBright ( "Invalid command: " + program . args . join ( " " ) ) , false , true ) ;
writeLn ( "See --help for a list of available commands." , true , true ) ;
process . exitCode = 1 ;
} ) ;
program . on ( "--help" , ( ) = > {
2024-06-06 09:40:51 +02:00
writeLn (
chalk . yellowBright (
"\n Tip: Managing and retrieving secrets for dev environments is easier with Bitwarden Secrets Manager. Learn more under https://bitwarden.com/products/secrets-manager/" ,
) ,
) ;
2021-12-20 18:04:00 +01:00
writeLn ( "\n Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw login" ) ;
writeLn ( " bw lock" ) ;
writeLn ( " bw unlock myPassword321" ) ;
writeLn ( " bw list --help" ) ;
writeLn ( " bw list items --search google" ) ;
writeLn ( " bw get item 99ee88d2-6046-4ea7-92c2-acac464b1412" ) ;
writeLn ( " bw get password google.com" ) ;
writeLn ( ' echo \'{"name":"My Folder"}\' | bw encode' ) ;
writeLn ( " bw create folder eyJuYW1lIjoiTXkgRm9sZGVyIn0K" ) ;
writeLn (
2023-11-29 22:15:20 +01:00
" bw edit folder c7c7b60b-9c61-40f2-8ccd-36c49595ed72 eyJuYW1lIjoiTXkgRm9sZGVyMiJ9Cg==" ,
2021-12-20 18:04:00 +01:00
) ;
writeLn ( " bw delete item 99ee88d2-6046-4ea7-92c2-acac464b1412" ) ;
writeLn ( " bw generate -lusn --length 18" ) ;
writeLn ( " bw config server https://bitwarden.example.com" ) ;
writeLn ( " bw send -f ./file.ext" ) ;
writeLn ( ' bw send "text to send"' ) ;
writeLn ( ' echo "text to send" | bw send' ) ;
writeLn (
2023-11-29 22:15:20 +01:00
" bw receive https://vault.bitwarden.com/#/send/rg3iuoS_Akm2gqy6ADRHmg/Ht7dYjsqjmgqUM3rjzZDSQ" ,
2021-12-20 18:04:00 +01:00
) ;
writeLn ( "" , true ) ;
} ) ;
program
. command ( "login [email] [password]" )
. description ( "Log into a user account." )
. option ( "--method <method>" , "Two-step login method." )
. option ( "--code <code>" , "Two-step login code." )
. option ( "--sso" , "Log in with Single-Sign On." )
. option ( "--apikey" , "Log in with an Api Key." )
. option ( "--passwordenv <passwordenv>" , "Environment variable storing your password" )
. option (
"--passwordfile <passwordfile>" ,
2023-11-29 22:15:20 +01:00
"Path to a file containing your password as its first line" ,
2021-12-20 18:04:00 +01:00
)
. option ( "--check" , "Check login status." , async ( ) = > {
2024-05-15 16:09:24 +02:00
const authed = await this . serviceContainer . stateService . getIsAuthenticated ( ) ;
2021-12-20 18:04:00 +01:00
if ( authed ) {
const res = new MessageResponse ( "You are logged in!" , null ) ;
this . processResponse ( Response . success ( res ) , true ) ;
}
this . processResponse ( Response . error ( "You are not logged in." ) , true ) ;
} )
. on ( "--help" , ( ) = > {
writeLn ( "\n Notes:" ) ;
writeLn ( "" ) ;
writeLn ( " See docs for valid `method` enum values." ) ;
writeLn ( "" ) ;
writeLn ( " Pass `--raw` option to only return the session key." ) ;
writeLn ( "" ) ;
writeLn ( " Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw login" ) ;
writeLn ( " bw login john@example.com myPassword321 --raw" ) ;
writeLn ( " bw login john@example.com myPassword321 --method 1 --code 249213" ) ;
writeLn ( " bw login --sso" ) ;
writeLn ( "" , true ) ;
} )
2024-01-31 23:17:04 +01:00
. action ( async ( email : string , password : string , options : OptionValues ) = > {
2021-12-20 18:04:00 +01:00
if ( ! options . check ) {
await this . exitIfAuthed ( ) ;
const command = new LoginCommand (
2024-05-15 16:09:24 +02:00
this . serviceContainer . loginStrategyService ,
this . serviceContainer . authService ,
this . serviceContainer . apiService ,
this . serviceContainer . cryptoFunctionService ,
this . serviceContainer . environmentService ,
this . serviceContainer . passwordGenerationService ,
this . serviceContainer . passwordStrengthService ,
this . serviceContainer . platformUtilsService ,
2024-05-17 00:21:00 +02:00
this . serviceContainer . accountService ,
2024-05-15 16:09:24 +02:00
this . serviceContainer . cryptoService ,
this . serviceContainer . policyService ,
this . serviceContainer . twoFactorService ,
this . serviceContainer . syncService ,
this . serviceContainer . keyConnectorService ,
this . serviceContainer . policyApiService ,
this . serviceContainer . organizationService ,
async ( ) = > await this . serviceContainer . logout ( ) ,
this . serviceContainer . kdfConfigService ,
2021-12-20 18:04:00 +01:00
) ;
const response = await command . run ( email , password , options ) ;
2023-05-03 20:15:32 +02:00
this . processResponse ( response , true ) ;
2021-12-20 18:04:00 +01:00
}
} ) ;
program
. command ( "logout" )
. description ( "Log out of the current user account." )
. on ( "--help" , ( ) = > {
writeLn ( "\n Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw logout" ) ;
writeLn ( "" , true ) ;
} )
. action ( async ( cmd ) = > {
await this . exitIfNotAuthed ( ) ;
const command = new LogoutCommand (
2024-05-15 16:09:24 +02:00
this . serviceContainer . authService ,
this . serviceContainer . i18nService ,
async ( ) = > await this . serviceContainer . logout ( ) ,
2021-12-20 18:04:00 +01:00
) ;
const response = await command . run ( ) ;
this . processResponse ( response ) ;
} ) ;
program
. command ( "lock" )
. description ( "Lock the vault and destroy active session keys." )
. on ( "--help" , ( ) = > {
writeLn ( "\n Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw lock" ) ;
writeLn ( "" , true ) ;
} )
. action ( async ( cmd ) = > {
await this . exitIfNotAuthed ( ) ;
2018-05-14 20:54:19 +02:00
2024-05-15 16:09:24 +02:00
if ( await this . serviceContainer . keyConnectorService . getUsesKeyConnector ( ) ) {
2021-12-20 18:04:00 +01:00
const logoutCommand = new LogoutCommand (
2024-05-15 16:09:24 +02:00
this . serviceContainer . authService ,
this . serviceContainer . i18nService ,
async ( ) = > await this . serviceContainer . logout ( ) ,
2021-12-20 18:04:00 +01:00
) ;
await logoutCommand . run ( ) ;
this . processResponse (
Response . error (
"You cannot lock your vault because you are using Key Connector. " +
2023-11-29 22:15:20 +01:00
"To protect your vault, you have been logged out." ,
2021-12-20 18:04:00 +01:00
) ,
2023-11-29 22:15:20 +01:00
true ,
2021-12-20 18:04:00 +01:00
) ;
return ;
}
2018-05-16 04:10:24 +02:00
2024-05-15 16:09:24 +02:00
const command = new LockCommand ( this . serviceContainer . vaultTimeoutService ) ;
2022-01-19 16:45:14 +01:00
const response = await command . run ( ) ;
2021-12-20 18:04:00 +01:00
this . processResponse ( response ) ;
} ) ;
program
. command ( "unlock [password]" )
. description ( "Unlock the vault and return a new session key." )
. on ( "--help" , ( ) = > {
writeLn ( "\n Notes:" ) ;
writeLn ( "" ) ;
writeLn ( " After unlocking, any previous session keys will no longer be valid." ) ;
writeLn ( "" ) ;
writeLn ( " Pass `--raw` option to only return the session key." ) ;
writeLn ( "" ) ;
writeLn ( " Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw unlock" ) ;
writeLn ( " bw unlock myPassword321" ) ;
writeLn ( " bw unlock myPassword321 --raw" ) ;
writeLn ( "" , true ) ;
} )
. option ( "--check" , "Check lock status." , async ( ) = > {
2022-05-16 03:58:36 +02:00
await this . exitIfNotAuthed ( ) ;
2024-05-15 16:09:24 +02:00
const authStatus = await this . serviceContainer . authService . getAuthStatus ( ) ;
2022-05-16 03:58:36 +02:00
if ( authStatus === AuthenticationStatus . Unlocked ) {
2021-12-20 18:04:00 +01:00
const res = new MessageResponse ( "Vault is unlocked!" , null ) ;
this . processResponse ( Response . success ( res ) , true ) ;
2022-05-16 03:58:36 +02:00
} else {
this . processResponse ( Response . error ( "Vault is locked." ) , true ) ;
2018-05-16 04:10:24 +02:00
}
2021-12-20 18:04:00 +01:00
} )
. option ( "--passwordenv <passwordenv>" , "Environment variable storing your password" )
. option (
"--passwordfile <passwordfile>" ,
2023-11-29 22:15:20 +01:00
"Path to a file containing your password as its first line" ,
2021-12-20 18:04:00 +01:00
)
. action ( async ( password , cmd ) = > {
if ( ! cmd . check ) {
await this . exitIfNotAuthed ( ) ;
const command = new UnlockCommand (
2024-05-15 16:09:24 +02:00
this . serviceContainer . accountService ,
this . serviceContainer . masterPasswordService ,
this . serviceContainer . cryptoService ,
2024-06-14 22:06:55 +02:00
this . serviceContainer . userVerificationService ,
2024-05-15 16:09:24 +02:00
this . serviceContainer . cryptoFunctionService ,
this . serviceContainer . logService ,
this . serviceContainer . keyConnectorService ,
this . serviceContainer . environmentService ,
this . serviceContainer . syncService ,
this . serviceContainer . organizationApiService ,
async ( ) = > await this . serviceContainer . logout ( ) ,
2021-12-20 18:04:00 +01:00
) ;
const response = await command . run ( password , cmd ) ;
this . processResponse ( response ) ;
}
} ) ;
program
. command ( "sync" )
. description ( "Pull the latest vault data from server." )
. option ( "-f, --force" , "Force a full sync." )
. option ( "--last" , "Get the last sync date." )
. on ( "--help" , ( ) = > {
writeLn ( "\n Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw sync" ) ;
writeLn ( " bw sync -f" ) ;
writeLn ( " bw sync --last" ) ;
writeLn ( "" , true ) ;
} )
. action ( async ( cmd ) = > {
2022-08-16 23:27:05 +02:00
await this . exitIfNotAuthed ( ) ;
2024-05-15 16:09:24 +02:00
const command = new SyncCommand ( this . serviceContainer . syncService ) ;
2021-12-20 18:04:00 +01:00
const response = await command . run ( cmd ) ;
this . processResponse ( response ) ;
} ) ;
program
. command ( "generate" )
. description ( "Generate a password/passphrase." )
. option ( "-u, --uppercase" , "Include uppercase characters." )
. option ( "-l, --lowercase" , "Include lowercase characters." )
. option ( "-n, --number" , "Include numeric characters." )
. option ( "-s, --special" , "Include special characters." )
. option ( "-p, --passphrase" , "Generate a passphrase." )
. option ( "--length <length>" , "Length of the password." )
. option ( "--words <words>" , "Number of words." )
2023-09-04 22:01:16 +02:00
. option ( "--minNumber <count>" , "Minimum number of numeric characters." )
. option ( "--minSpecial <count>" , "Minimum number of special characters." )
2021-12-20 18:04:00 +01:00
. option ( "--separator <separator>" , "Word separator." )
. option ( "-c, --capitalize" , "Title case passphrase." )
. option ( "--includeNumber" , "Passphrase includes number." )
2023-09-04 22:01:16 +02:00
. option ( "--ambiguous" , "Avoid ambiguous characters." )
2021-12-20 18:04:00 +01:00
. on ( "--help" , ( ) = > {
writeLn ( "\n Notes:" ) ;
writeLn ( "" ) ;
writeLn ( " Default options are `-uln --length 14`." ) ;
writeLn ( "" ) ;
writeLn ( " Minimum `length` is 5." ) ;
writeLn ( "" ) ;
writeLn ( " Minimum `words` is 3." ) ;
writeLn ( "" ) ;
writeLn ( " Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw generate" ) ;
writeLn ( " bw generate -u -l --length 18" ) ;
writeLn ( " bw generate -ulns --length 25" ) ;
writeLn ( " bw generate -ul" ) ;
writeLn ( " bw generate -p --separator _" ) ;
writeLn ( " bw generate -p --words 5 --separator space" ) ;
2024-02-09 17:07:53 +01:00
writeLn ( " bw generate -p --words 5 --separator empty" ) ;
2021-12-20 18:04:00 +01:00
writeLn ( "" , true ) ;
} )
. action ( async ( options ) = > {
2022-02-14 15:29:44 +01:00
const command = new GenerateCommand (
2024-05-15 16:09:24 +02:00
this . serviceContainer . passwordGenerationService ,
this . serviceContainer . stateService ,
2022-02-14 15:29:44 +01:00
) ;
2021-12-20 18:04:00 +01:00
const response = await command . run ( options ) ;
this . processResponse ( response ) ;
} ) ;
program
. command ( "encode" )
. description ( "Base 64 encode stdin." )
. on ( "--help" , ( ) = > {
writeLn ( "\n Notes:" ) ;
writeLn ( "" ) ;
writeLn ( " Use to create `encodedJson` for `create` and `edit` commands." ) ;
writeLn ( "" ) ;
writeLn ( " Examples:" ) ;
writeLn ( "" ) ;
writeLn ( ' echo \'{"name":"My Folder"}\' | bw encode' ) ;
writeLn ( "" , true ) ;
} )
. action ( async ( ) = > {
const command = new EncodeCommand ( ) ;
const response = await command . run ( ) ;
this . processResponse ( response ) ;
} ) ;
program
. command ( "config <setting> [value]" )
. description ( "Configure CLI settings." )
. option (
"--web-vault <url>" ,
2023-11-29 22:15:20 +01:00
"Provides a custom web vault URL that differs from the base URL." ,
2021-12-20 18:04:00 +01:00
)
. option ( "--api <url>" , "Provides a custom API URL that differs from the base URL." )
. option ( "--identity <url>" , "Provides a custom identity URL that differs from the base URL." )
. option (
"--icons <url>" ,
2023-11-29 22:15:20 +01:00
"Provides a custom icons service URL that differs from the base URL." ,
2021-12-20 18:04:00 +01:00
)
. option (
"--notifications <url>" ,
2023-11-29 22:15:20 +01:00
"Provides a custom notifications URL that differs from the base URL." ,
2021-12-20 18:04:00 +01:00
)
. option ( "--events <url>" , "Provides a custom events URL that differs from the base URL." )
. option ( "--key-connector <url>" , "Provides the URL for your Key Connector server." )
. on ( "--help" , ( ) = > {
writeLn ( "\n Settings:" ) ;
writeLn ( "" ) ;
writeLn ( " server - On-premises hosted installation URL." ) ;
writeLn ( "" ) ;
writeLn ( " Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw config server" ) ;
writeLn ( " bw config server https://bw.company.com" ) ;
writeLn ( " bw config server bitwarden.com" ) ;
writeLn (
2023-11-29 22:15:20 +01:00
" bw config server --api http://localhost:4000 --identity http://localhost:33656" ,
2021-12-20 18:04:00 +01:00
) ;
writeLn ( "" , true ) ;
} )
. action ( async ( setting , value , options ) = > {
2024-06-17 17:00:19 +02:00
const command = new ConfigCommand (
this . serviceContainer . environmentService ,
this . serviceContainer . accountService ,
) ;
2021-12-20 18:04:00 +01:00
const response = await command . run ( setting , value , options ) ;
this . processResponse ( response ) ;
} ) ;
program
. command ( "update" )
. description ( "Check for updates." )
. on ( "--help" , ( ) = > {
writeLn ( "\n Notes:" ) ;
writeLn ( "" ) ;
writeLn ( " Returns the URL to download the newest version of this CLI tool." ) ;
writeLn ( "" ) ;
writeLn ( " Use the `--raw` option to return only the download URL for the update." ) ;
writeLn ( "" ) ;
writeLn ( " Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw update" ) ;
writeLn ( " bw update --raw" ) ;
writeLn ( "" , true ) ;
} )
. action ( async ( ) = > {
2024-05-15 16:09:24 +02:00
const command = new UpdateCommand ( this . serviceContainer . platformUtilsService ) ;
2021-12-20 18:04:00 +01:00
const response = await command . run ( ) ;
this . processResponse ( response ) ;
} ) ;
program
. command ( "completion" )
. description ( "Generate shell completions." )
. option ( "--shell <shell>" , "Shell to generate completions for." )
. on ( "--help" , ( ) = > {
writeLn ( "\n Notes:" ) ;
writeLn ( "" ) ;
writeLn ( " Valid shells are `zsh`." ) ;
writeLn ( "" ) ;
writeLn ( " Examples:" ) ;
writeLn ( "" ) ;
writeLn ( " bw completion --shell zsh" ) ;
writeLn ( "" , true ) ;
} )
2024-01-31 23:17:04 +01:00
. action ( async ( options : OptionValues , cmd : Command ) = > {
2021-12-20 18:04:00 +01:00
const command = new CompletionCommand ( ) ;
const response = await command . run ( options ) ;
this . processResponse ( response ) ;
} ) ;
program
. command ( "status" )
. description ( "Show server, last sync, user information, and vault status." )
. on ( "--help" , ( ) = > {
writeLn ( "" ) ;
writeLn ( "" ) ;
writeLn ( " Example return value:" ) ;
writeLn ( "" ) ;
writeLn ( " {" ) ;
writeLn ( ' "serverUrl": "https://bitwarden.example.com",' ) ;
writeLn ( ' "lastSync": "2020-06-16T06:33:51.419Z",' ) ;
writeLn ( ' "userEmail": "user@example.com,' ) ;
writeLn ( ' "userId": "00000000-0000-0000-0000-000000000000",' ) ;
writeLn ( ' "status": "locked"' ) ;
writeLn ( " }" ) ;
writeLn ( "" ) ;
writeLn ( " Notes:" ) ;
writeLn ( "" ) ;
writeLn ( " `status` is one of:" ) ;
writeLn ( " - `unauthenticated` when you are not logged in" ) ;
writeLn ( " - `locked` when you are logged in and the vault is locked" ) ;
writeLn ( " - `unlocked` when you are logged in and the vault is unlocked" ) ;
writeLn ( "" , true ) ;
} )
. action ( async ( ) = > {
const command = new StatusCommand (
2024-05-15 16:09:24 +02:00
this . serviceContainer . environmentService ,
this . serviceContainer . syncService ,
2024-05-17 00:21:00 +02:00
this . serviceContainer . accountService ,
2024-05-15 16:09:24 +02:00
this . serviceContainer . authService ,
2021-12-20 18:04:00 +01:00
) ;
const response = await command . run ( ) ;
this . processResponse ( response ) ;
} ) ;
}
2018-05-14 17:15:54 +02:00
}