bw/tools/
mod.rs

1use bitwarden_generators::{PassphraseGeneratorRequest, PasswordGeneratorRequest};
2use bitwarden_pm::PasswordManagerClient;
3use clap::{Args, Subcommand};
4
5use crate::render::CommandResult;
6
7#[derive(Args, Clone)]
8pub struct GenerateArgs {
9    // Password arguments
10    #[arg(short = 'u', long, action, help = "Include uppercase characters (A-Z)")]
11    pub uppercase: bool,
12
13    #[arg(short = 'l', long, action, help = "Include lowercase characters (a-z)")]
14    pub lowercase: bool,
15
16    #[arg(short = 'n', long, action, help = "Include numbers (0-9)")]
17    pub number: bool,
18
19    #[arg(
20        short = 's',
21        long,
22        action,
23        help = "Include special characters (!@#$%^&*)"
24    )]
25    pub special: bool,
26
27    #[arg(long, default_value = "16", help = "Length of generated password")]
28    pub length: u8,
29
30    #[arg(long, help = "Minimum number of numeric characters")]
31    pub min_numbers: Option<u8>,
32
33    #[arg(long, help = "Minimum number of special characters")]
34    pub min_special: Option<u8>,
35
36    #[arg(long, action, help = "Avoid ambiguous characters")]
37    pub ambiguous: bool,
38
39    // Passphrase arguments
40    #[arg(short = 'p', long, action, help = "Generate a passphrase")]
41    pub passphrase: bool,
42
43    #[arg(long, default_value = "5", help = "Number of words in the passphrase")]
44    pub words: u8,
45
46    #[arg(long, default_value = "-", help = "Separator between words")]
47    pub separator: char,
48
49    #[arg(long, action, help = "Capitalize the first letter of each word")]
50    pub capitalize: bool,
51
52    #[arg(long, action, help = "Include a number in one of the words")]
53    pub include_number: bool,
54}
55
56impl GenerateArgs {
57    pub fn run(mut self, client: &PasswordManagerClient) -> CommandResult {
58        let result = if self.passphrase {
59            client.generator().passphrase(PassphraseGeneratorRequest {
60                num_words: self.words,
61                word_separator: self.separator.to_string(),
62                capitalize: self.capitalize,
63                include_number: self.include_number,
64            })?
65        } else {
66            // Default options if none are specified
67            if !self.lowercase && !self.uppercase && !self.number && !self.special {
68                self.lowercase = true;
69                self.uppercase = true;
70                self.number = true;
71            }
72
73            client.generator().password(PasswordGeneratorRequest {
74                lowercase: self.lowercase,
75                uppercase: self.uppercase,
76                numbers: self.number,
77                special: self.special,
78                length: self.length,
79                min_number: self.min_numbers,
80                min_special: self.min_special,
81                avoid_ambiguous: self.ambiguous,
82                ..Default::default()
83            })?
84        };
85
86        Ok(result.into())
87    }
88}
89
90#[derive(Args, Clone)]
91pub struct ImportArgs {
92    /// Format to import from
93    pub format: Option<String>,
94    /// Filepath to data to import
95    pub input: Option<String>,
96
97    #[arg(long, help = "List formats")]
98    pub formats: bool,
99
100    #[arg(long, help = "ID of the organization to import to.")]
101    pub organizationid: Option<String>,
102}
103
104#[derive(Args, Clone)]
105pub struct ExportArgs {
106    #[arg(long, help = "Output directory or filename.")]
107    pub output: Option<String>,
108
109    #[arg(long, help = "Export file format.")]
110    pub format: Option<String>,
111
112    #[arg(
113        long,
114        help = "Use password to encrypt instead of your Bitwarden account encryption key."
115    )]
116    pub password: Option<String>,
117
118    #[arg(long, help = "Organization id for an organization.")]
119    pub organizationid: Option<String>,
120}
121
122#[derive(Args, Clone)]
123pub struct SendArgs {
124    /// The data to Send
125    pub data: Option<String>,
126
127    #[arg(short = 'f', long, help = "Specifies that <data> is a filepath.")]
128    pub file: bool,
129
130    #[arg(
131        short = 'd',
132        long = "deleteInDays",
133        help = "The number of days in the future to set deletion date.",
134        default_value = "7"
135    )]
136    pub delete_in_days: String,
137
138    #[arg(long, help = "Optional password to access this Send.")]
139    pub password: Option<String>,
140
141    #[arg(
142        short = 'a',
143        long = "maxAccessCount",
144        help = "The amount of max possible accesses."
145    )]
146    pub max_access_count: Option<u32>,
147
148    #[arg(long, help = "Hide <data> in web by default.")]
149    pub hidden: bool,
150
151    #[arg(short = 'n', long, help = "The name of the Send.")]
152    pub name: Option<String>,
153
154    #[arg(long, help = "Notes to add to the Send.")]
155    pub notes: Option<String>,
156
157    #[arg(
158        long = "fullObject",
159        help = "Specifies that the full Send object should be returned."
160    )]
161    pub full_object: bool,
162
163    #[command(subcommand)]
164    pub command: Option<SendCommands>,
165}
166
167#[derive(Subcommand, Clone, Debug)]
168pub enum SendCommands {
169    #[command(long_about = "List all the Sends owned by you.")]
170    List,
171
172    #[command(long_about = "Get json templates for send objects.")]
173    Template { object: String },
174
175    #[command(long_about = "Get Sends owned by you.")]
176    Get {
177        id: String,
178
179        #[arg(long, help = "Specify a file path to save a File-type Send to.")]
180        output: Option<String>,
181
182        #[arg(long, help = "Only return the access url.")]
183        text: bool,
184    },
185
186    #[command(long_about = "Access a Bitwarden Send from a url.")]
187    Receive {
188        url: String,
189
190        #[arg(long, help = "Optional password for the Send.")]
191        password: Option<String>,
192
193        #[arg(long, help = "Specify a file path to save a File-type Send to.")]
194        obj: Option<String>,
195    },
196
197    #[command(long_about = "Create a Send.")]
198    Create {
199        encoded_json: Option<String>,
200
201        #[arg(short = 'f', long, help = "Path to the file to Send.")]
202        file: Option<String>,
203
204        #[arg(long, help = "Text to Send.")]
205        text: Option<String>,
206
207        #[arg(
208            short = 'd',
209            long = "deleteInDays",
210            help = "The number of days in the future to set deletion date.",
211            default_value = "7"
212        )]
213        delete_in_days: String,
214
215        #[arg(
216            long = "maxAccessCount",
217            help = "The maximum number of times this Send can be accessed."
218        )]
219        max_access_count: Option<u32>,
220
221        #[arg(long, help = "Hide text.")]
222        hidden: bool,
223
224        #[arg(short = 'n', long, help = "The name of the Send.")]
225        name: Option<String>,
226
227        #[arg(long, help = "Notes to add to the Send.")]
228        notes: Option<String>,
229
230        #[arg(long, help = "Optional password to access this Send.")]
231        password: Option<String>,
232
233        #[arg(
234            long = "fullObject",
235            help = "Return full Send object instead of access url."
236        )]
237        full_object: bool,
238    },
239
240    #[command(long_about = "Edit a Send.")]
241    Edit {
242        encoded_json: Option<String>,
243
244        #[arg(long, help = "Overrides the itemId provided in encodedJson.")]
245        itemid: Option<String>,
246
247        #[arg(
248            short = 'd',
249            long = "deleteInDays",
250            help = "The number of days in the future to set deletion date."
251        )]
252        delete_in_days: Option<String>,
253
254        #[arg(
255            long = "maxAccessCount",
256            help = "The maximum number of times this Send can be accessed."
257        )]
258        max_access_count: Option<u32>,
259
260        #[arg(long, help = "Hide text.")]
261        hidden: bool,
262    },
263
264    #[command(long_about = "Removes the saved password from a Send.")]
265    RemovePassword { id: String },
266
267    #[command(long_about = "Delete a Send.")]
268    Delete { id: String },
269}
270
271#[derive(Args, Clone)]
272pub struct ReceiveArgs {
273    /// URL to access Send from
274    pub url: String,
275
276    #[arg(long, help = "Optional password for the Send.")]
277    pub password: Option<String>,
278
279    #[arg(long, help = "Specify a file path to save a File-type Send to.")]
280    pub obj: Option<String>,
281}