1use bitwarden_cli::Color;
2use clap::{Parser, Subcommand};
3
4use crate::{
5 admin_console::{ConfirmCommand, MoveArgs},
6 auth::LoginArgs,
7 key_management::UnlockArgs,
8 platform::{ConfigCommand, SyncArgs},
9 render::Output,
10 tools::{ExportArgs, GenerateArgs, ImportArgs, ReceiveArgs, SendArgs},
11 vault::{RestoreArgs, TemplateCommands},
12};
13
14pub const SESSION_ENV: &str = "BW_SESSION";
15
16#[derive(Parser, Clone)]
17#[command(name = "Bitwarden CLI", version, about = "Bitwarden CLI", long_about = None, disable_version_flag = true)]
18pub struct Cli {
19 #[command(subcommand)]
21 pub command: Option<Commands>,
22
23 #[arg(short = 'o', long, global = true, value_enum, default_value_t = Output::JSON)]
24 pub output: Output,
25
26 #[arg(short = 'c', long, global = true, value_enum, default_value_t = Color::Auto)]
27 pub color: Color,
28
29 #[arg(
31 long,
32 global = true,
33 env = SESSION_ENV,
34 help = "The session key used to decrypt your vault data. Can be obtained with `bw login` or `bw unlock`."
35 )]
36 pub session: Option<String>,
37
38 #[arg(
39 long,
40 global = true,
41 help = "Exit with a success exit code (0) unless an error is thrown."
42 )]
43 pub cleanexit: bool,
44
45 #[arg(
46 short = 'q',
47 long,
48 global = true,
49 help = "Don't return anything to stdout."
50 )]
51 pub quiet: bool,
52
53 #[arg(
54 long,
55 global = true,
56 help = "Do not prompt for interactive user input."
57 )]
58 pub nointeraction: bool,
59
60 #[arg(short = 'v', long, action = clap::builder::ArgAction::Version)]
64 pub version: (),
65}
66
67#[derive(Subcommand, Clone)]
68pub enum Commands {
69 #[command(long_about = "Log into a user account.")]
71 Login(LoginArgs),
72
73 #[command(long_about = "Log out of the current user account.")]
74 Logout,
75
76 #[command(long_about = "Lock the vault and destroy active session keys.")]
77 Lock,
78
79 #[command(long_about = "Unlock the vault and return a session key.")]
81 Unlock(UnlockArgs),
82
83 #[command(long_about = "Pull the latest vault data from server.")]
85 Sync(SyncArgs),
86
87 #[command(long_about = "Base 64 encode stdin.")]
88 Encode,
89
90 #[command(long_about = "Configure CLI settings.")]
91 Config {
92 #[command(subcommand)]
93 command: ConfigCommand,
94 },
95
96 #[command(long_about = "Check for updates.")]
97 Update {
98 #[arg(long, help = "Return only the download URL for the update.")]
99 raw: bool,
100 },
101
102 #[command(long_about = "Generate shell completions.")]
103 Completion {
104 #[arg(long, help = "The shell to generate completions for.")]
105 shell: Option<clap_complete::Shell>,
106 },
107
108 #[command(
109 long_about = "Show server, last sync, user information, and vault status.",
110 after_help = r#"Example return value:
111 {
112 "serverUrl": "https://bitwarden.example.com",
113 "lastSync": "2020-06-16T06:33:51.419Z",
114 "userEmail": "[email protected]",
115 "userId": "00000000-0000-0000-0000-000000000000",
116 "status": "locked"
117 }
118
119Notes:
120 `status` is one of:
121 - `unauthenticated` when you are not logged in
122 - `locked` when you are logged in and the vault is locked
123 - `unlocked` when you are logged in and the vault is unlocked
124"#
125 )]
126 Status,
127
128 #[command(long_about = "List an array of objects from the vault.")]
131 List(ListArgs),
132 #[command(long_about = "Get an object from the vault.")]
133 Get {
134 #[command(subcommand)]
135 command: GetCommands,
136 },
137 #[command(long_about = "Create an object in the vault.")]
138 Create {
139 #[command(subcommand)]
140 command: CreateCommands,
141 },
142 #[command(long_about = "Edit an object from the vault.")]
143 Edit(EditArgs),
144 #[command(long_about = "Delete an object from the vault.")]
145 Delete {
146 #[command(subcommand)]
147 command: DeleteCommands,
148 },
149 #[command(long_about = "Restores an object from the trash.")]
150 Restore(RestoreArgs),
151 #[command(long_about = "Move an item to an organization.")]
152 Move(MoveArgs),
153
154 #[command(long_about = "Confirm an object to the organization.")]
156 Confirm {
157 #[command(subcommand)]
158 command: ConfirmCommand,
159 },
160
161 #[command(long_about = "Generate a password/passphrase.")]
163 #[command(after_help = r#"Notes:
164 Default options are `-uln --length 14`.
165 Minimum `length` is 5.
166 Minimum `words` is 3.
167
168Examples:
169 bw generate
170 bw generate -u -l --length 18
171 bw generate -ulns --length 25
172 bw generate -ul
173 bw generate -p --separator _
174 bw generate -p --words 5 --separator space
175 bw generate -p --words 5 --separator empty
176 "#)]
177 Generate(GenerateArgs),
178 #[command(long_about = "Import vault data from a file.")]
179 Import(ImportArgs),
180 #[command(long_about = "Export vault data to a CSV, JSON or ZIP file.")]
181 Export(ExportArgs),
182 #[command(
183 long_about = "Work with Bitwarden sends. A Send can be quickly created using this command or subcommands can be used to fine-tune the Send."
184 )]
185 Send(SendArgs),
186 #[command(long_about = "Access a Bitwarden Send from a url.")]
187 Receive(ReceiveArgs),
188
189 #[command(
191 long_about = "Manage device approval requests sent to organizations that use SSO with trusted devices."
192 )]
193 DeviceApproval,
194
195 #[command(long_about = "Start a RESTful API webserver.")]
197 Serve(ServeArgs),
198}
199
200#[derive(clap::Args, Clone)]
201pub struct ServeArgs {
202 #[arg(long, help = "Port number to listen on.", default_value = "8087")]
203 pub port: u16,
204
205 #[arg(long, help = "Hostname to bind to.", default_value = "localhost")]
206 pub hostname: String,
207
208 #[arg(
209 long,
210 help = "Disable origin protection (not recommended for production use)."
211 )]
212 pub disable_origin_protection: bool,
213}
214
215#[derive(clap::Args, Clone)]
216pub struct ListArgs {
217 pub object: ListObject,
219
220 #[arg(long, help = "Filter items by URL")]
221 pub url: Option<String>,
222
223 #[arg(long, help = "Filter items by folder ID")]
224 pub folderid: Option<String>,
225
226 #[arg(long, help = "Filter items by collection ID")]
227 pub collectionid: Option<String>,
228
229 #[arg(long, help = "Filter items by organization ID")]
230 pub organizationid: Option<String>,
231
232 #[arg(long, help = "Filter items in trash")]
233 pub trash: bool,
234
235 #[arg(long, help = "Search term")]
236 pub search: Option<String>,
237}
238
239#[derive(clap::Args, Clone)]
240pub struct EditArgs {
241 pub object: EditObject,
243 pub id: String,
245 pub encoded_json: Option<String>,
247
248 #[arg(long, help = "Organization ID for an organization object")]
249 pub organizationid: Option<String>,
250}
251
252#[derive(clap::ValueEnum, Clone, Debug)]
253#[value(rename_all = "kebab-case")]
254pub enum ListObject {
255 Items,
256 Folders,
257 Collections,
258 Organizations,
259 OrgCollections,
260 OrgMembers,
261}
262
263#[derive(clap::ValueEnum, Clone, Debug)]
264#[value(rename_all = "kebab-case")]
265pub enum EditObject {
266 Item,
267 ItemCollections,
268 Folder,
269 OrgCollection,
270}
271
272#[derive(Subcommand, Clone, Debug)]
273pub enum GetCommands {
274 #[command(long_about = "Get an item from the vault.")]
275 Item { id: String },
276
277 #[command(long_about = "Get the username for an item.")]
278 Username { id: String },
279
280 #[command(long_about = "Get the password for an item.")]
281 Password { id: String },
282
283 #[command(long_about = "Get the URI for an item.")]
284 Uri { id: String },
285
286 #[command(long_about = "Get the TOTP code for an item.")]
287 Totp { id: String },
288
289 #[command(long_about = "Check if an item password has been exposed in a data breach.")]
290 Exposed { id: String },
291
292 #[command(long_about = "Get the notes for an item.")]
293 Notes { id: String },
294
295 #[command(long_about = "Get a folder from the vault.")]
296 Folder { id: String },
297
298 #[command(long_about = "Get a collection from the vault.")]
299 Collection { id: String },
300
301 #[command(long_about = "Get an organization.")]
302 Organization { id: String },
303
304 #[command(long_about = "Get an organization collection.")]
305 #[command(name = "org-collection")]
306 OrgCollection { id: String },
307
308 #[command(long_about = "Get an attachment from an item.")]
309 Attachment {
310 filename: String,
311 #[arg(long, help = "Item ID that the attachment belongs to.")]
312 itemid: String,
313 #[arg(long, help = "Output file path. If not specified, outputs to stdout.")]
314 output: Option<String>,
315 },
316
317 #[command(long_about = "Get the fingerprint for the current user or a specified user.")]
318 Fingerprint {
319 #[arg(default_value = "me", help = "User ID or 'me' for current user")]
320 user: String,
321 },
322
323 #[command(long_about = "Get a JSON template for creating objects.")]
324 Template {
325 #[command(subcommand)]
326 command: TemplateCommands,
327 },
328
329 #[command(long_about = "Get a Bitwarden Send.")]
330 Send { id: String },
331}
332
333#[derive(Subcommand, Clone, Debug)]
334pub enum CreateCommands {
335 #[command(long_about = "Create an item in the vault.")]
336 Item {
337 #[arg(help = "Base64-encoded JSON item object")]
338 encoded_json: String,
339 },
340
341 #[command(long_about = "Create an attachment for an item.")]
342 Attachment {
343 #[arg(long, help = "Path to the file to attach")]
344 file: String,
345 #[arg(long, help = "Item ID to attach the file to")]
346 itemid: String,
347 },
348
349 #[command(long_about = "Create a folder.")]
350 Folder {
351 #[arg(help = "Base64-encoded JSON folder object")]
352 encoded_json: String,
353 },
354
355 #[command(long_about = "Create an organization collection.")]
356 #[command(name = "org-collection")]
357 OrgCollection {
358 #[arg(help = "Base64-encoded JSON collection object")]
359 encoded_json: String,
360
361 #[arg(long, help = "Organization ID")]
362 organizationid: Option<String>,
363 },
364}
365
366#[derive(Subcommand, Clone, Debug)]
367pub enum DeleteCommands {
368 #[command(long_about = "Delete an item from the vault.")]
369 Item {
370 id: String,
371 #[arg(short = 'p', long, help = "Permanently delete the item (skip trash)")]
372 permanent: bool,
373 },
374
375 #[command(long_about = "Delete an attachment from an item.")]
376 Attachment {
377 id: String,
378 #[arg(long, help = "Item ID that the attachment belongs to")]
379 itemid: String,
380 },
381
382 #[command(long_about = "Delete a folder.")]
383 Folder {
384 id: String,
385 #[arg(short = 'p', long, help = "Permanently delete the folder (skip trash)")]
386 permanent: bool,
387 },
388
389 #[command(long_about = "Delete an organization collection.")]
390 #[command(name = "org-collection")]
391 OrgCollection {
392 id: String,
393 #[arg(long, help = "Organization ID")]
394 organizationid: String,
395 },
396}