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 #[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 #[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 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 pub format: Option<String>,
94 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 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 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}