Skip to content

Commit 51d1da8

Browse files
committed
update-or-create with random option
1 parent dcd884b commit 51d1da8

File tree

3 files changed

+123
-52
lines changed

3 files changed

+123
-52
lines changed

README.md

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ creates (for example) this string: `🏴󠁧󠁢󠁷󠁬󠁳󠁿🦑👹🦑🙉
147147

148148
### `update-or-create`
149149

150-
`psonoci env-vars update-or-create` updates or creates environment variable value by name with the supplied value. If there is no environment variable with that name a new one is created. If there are more than one with the same name, only the first will be updated.
150+
`psonoci env-vars update-or-create` updates or creates environment variable value by name with the supplied value and then returns this value. If there is no environment variable with that name a new one is created. If there are more than one with the same name, only the first will be updated. If no new value is provided a random one will be created. The new value can be adjusted with `--password-length` and `-danger-password-allowed-chars`. Please see above.
151151

152152
## Config
153153

@@ -280,26 +280,6 @@ I am not sure if `aarch64-apple-darwin` is also supported (I think it should wor
280280

281281
Download `psonoci` binary, make executable (`chmod +x psonoci`), and place into a directory which is part of your `$PATH`.
282282

283-
## Key Setup
284-
285-
TODO
286-
287-
<!-- ### Create API Key
288-
289-
1. Go to `Other -> API Keys` and click `Create new API Key`.
290-
2. Name your API key and make sure neither `Secret Restriction?` nor `Allow insecure usage?` are activated. (see Image )
291-
3. Click Create
292-
4. In the API key overview click on the edit Icon for the newly created key
293-
5. In this view you will see all secrets you need for the `psoco` config (see image 2)
294-
295-
#### Create API Key
296-
297-
![Create API Key](./images/create_api_key.png "Create API Key")
298-
299-
#### View API Key
300-
301-
![View API Key](./images/view_api_key_secrets.png "View API Key") -->
302-
303283
## License
304284

305285
[The MIT License](https://opensource.org/licenses/MIT)

src/env_vars.rs

Lines changed: 106 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,32 @@ use crate::{
99
};
1010

1111
pub fn run_env_vars_command(env_command: EnvVarsCommand, config: Config) -> Result<()> {
12-
match env_command {
12+
let secret_env_value = match env_command {
1313
EnvVarsCommand::GetOrCreate {
1414
secret_id,
1515
env_var_name,
1616
password_creation_settings,
17-
} => {
18-
let secret_env_value = get_or_create_env_value_by_name(
19-
secret_id,
20-
env_var_name,
21-
config,
22-
password_creation_settings,
23-
)?;
24-
25-
print!("{}", secret_env_value);
26-
}
17+
} => get_or_create_env_value_by_name(
18+
secret_id,
19+
env_var_name,
20+
config,
21+
password_creation_settings,
22+
)?,
2723
EnvVarsCommand::UpdateOrCreate {
2824
secret_id,
2925
env_var_name,
3026
env_var_value,
31-
} => update_or_create_env_value_by_name(secret_id, env_var_name, env_var_value, config)?,
32-
}
27+
password_creation_settings,
28+
} => update_or_create_env_value_by_name(
29+
secret_id,
30+
env_var_name,
31+
env_var_value,
32+
config,
33+
password_creation_settings,
34+
)?,
35+
};
36+
37+
print!("{}", secret_env_value);
3338

3439
Ok(())
3540
}
@@ -96,47 +101,61 @@ fn get_or_create(
96101
pub fn update_or_create_env_value_by_name(
97102
secret_id: Uuid,
98103
env_var_name: String,
99-
env_var_value: String,
104+
env_var_value: Option<String>,
100105
config: Config,
101-
) -> Result<()> {
106+
password_creation_settings: PasswordCreationSettings,
107+
) -> Result<String> {
102108
let (secret, secret_key_hex) = get_env_var_secret(&secret_id, &config)
103109
.context("update_or_create_env_value_by_name loading secret from store failed")?;
104110

105-
let secret_updated = update_or_create(secret, env_var_name, env_var_value);
111+
let (secret_updated, new_value) = update_or_create(
112+
secret,
113+
env_var_name,
114+
env_var_value,
115+
password_creation_settings,
116+
);
106117

107118
if let Some(updated) = secret_updated {
108119
set_secret(&secret_id, &config, &updated, &secret_key_hex)
109120
.context("Updating secret failed")?;
110121
}
111122

112-
Ok(())
123+
Ok(new_value)
113124
}
114125

115126
fn update_or_create(
116127
mut secret: Secret,
117128
env_var_name: String,
118-
env_var_value: String,
119-
) -> Option<Secret> {
129+
env_var_value: Option<String>,
130+
password_creation_settings: PasswordCreationSettings,
131+
) -> (Option<Secret>, String) {
120132
let mut env_vars = secret.env_vars.unwrap_or_default();
121133

134+
let new_value = match env_var_value {
135+
Some(v) => v,
136+
None => create_random_password(
137+
password_creation_settings.password_length,
138+
password_creation_settings.danger_password_allowed_chars,
139+
),
140+
};
122141
let needle = env_vars.iter_mut().find(|ev| ev.key == env_var_name);
123142

124143
if let Some(ev) = needle {
125-
if ev.value == env_var_value {
126-
return None;
144+
if ev.value == new_value {
145+
return (None, new_value);
127146
}
128147

129-
ev.value = env_var_value;
148+
ev.value = new_value.clone();
130149
} else {
131150
env_vars.push(EnvironmentVariable {
132151
key: env_var_name,
133-
value: env_var_value,
152+
value: new_value.clone(),
134153
});
135154
}
136155

137156
secret.env_vars = Some(env_vars);
138157

139-
Some(secret)
158+
(Some(secret), new_value)
140159
}
141160

142161
#[cfg(test)]
@@ -273,7 +292,12 @@ mod tests {
273292
env_vars: Some(env_vars),
274293
};
275294

276-
let secret_updated = update_or_create(secret, "name".to_owned(), "after".to_owned());
295+
let (secret_updated, new_secret) = update_or_create(
296+
secret,
297+
"name".to_owned(),
298+
Some("after".to_owned()),
299+
PasswordCreationSettings::default(),
300+
);
277301

278302
let env_vars_expected = vec![
279303
EnvironmentVariable {
@@ -304,6 +328,7 @@ mod tests {
304328
env_vars: Some(env_vars_expected),
305329
};
306330
assert_eq!(secret_updated, Some(secret_expected));
331+
assert_eq!(new_secret, "after");
307332
}
308333

309334
#[test]
@@ -335,7 +360,14 @@ mod tests {
335360
env_vars: Some(env_vars),
336361
};
337362

338-
let secret_updated = update_or_create(secret, "new".to_owned(), "new".to_owned());
363+
let (secret_updated, new_secret) = update_or_create(
364+
secret,
365+
"new".to_owned(),
366+
Some("new".to_owned()),
367+
PasswordCreationSettings::default(),
368+
);
369+
370+
assert_eq!(new_secret, "new");
339371

340372
let env_vars_expected = vec![
341373
EnvironmentVariable {
@@ -368,6 +400,47 @@ mod tests {
368400
assert_eq!(secret_updated, Some(secret_expected));
369401
}
370402

403+
#[test]
404+
#[allow(non_snake_case)]
405+
fn update_or_create__add_new_env_var_with_random_value() {
406+
let env_vars = vec![
407+
EnvironmentVariable {
408+
key: "name".to_owned(),
409+
value: "before".to_owned(),
410+
},
411+
EnvironmentVariable {
412+
key: "unchanged".to_owned(),
413+
value: "unchanged".to_owned(),
414+
},
415+
];
416+
417+
let secret = Secret {
418+
title: Some("test".to_owned()),
419+
url_filter: None,
420+
notes: None,
421+
password: None,
422+
username: None,
423+
url: None,
424+
secret_type: SecretType::EnvVars,
425+
gpg_key_private: None,
426+
gpg_key_public: None,
427+
gpg_key_name: None,
428+
gpg_key_email: None,
429+
env_vars: Some(env_vars),
430+
};
431+
432+
let (secret_updated, new_secret) = update_or_create(
433+
secret,
434+
"new".to_owned(),
435+
None,
436+
PasswordCreationSettings::default(),
437+
);
438+
439+
let new_env_var = &secret_updated.unwrap().env_vars.unwrap()[2];
440+
assert_eq!(new_env_var.key, "new");
441+
assert_eq!(new_env_var.value, new_secret);
442+
}
443+
371444
#[test]
372445
#[allow(non_snake_case)]
373446
fn update_or_create__env_var_not_changed() {
@@ -397,9 +470,14 @@ mod tests {
397470
env_vars: Some(env_vars),
398471
};
399472

400-
let secret_updated =
401-
update_or_create(secret, "unchanged".to_owned(), "unchanged".to_owned());
473+
let (secret_updated, new_secret) = update_or_create(
474+
secret,
475+
"unchanged".to_owned(),
476+
Some("unchanged".to_owned()),
477+
PasswordCreationSettings::default(),
478+
);
402479

480+
assert_eq!(new_secret, "unchanged");
403481
assert_eq!(secret_updated, None);
404482
}
405483
}

src/opt.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ pub struct PasswordCreationSettings {
245245
pub danger_password_allowed_chars: Option<String>,
246246
}
247247

248+
impl Default for PasswordCreationSettings {
249+
fn default() -> Self {
250+
Self {
251+
password_length: 21,
252+
danger_password_allowed_chars: None,
253+
}
254+
}
255+
}
256+
248257
#[derive(StructOpt, Debug)]
249258
pub enum EnvVarsCommand {
250259
#[structopt(
@@ -262,7 +271,7 @@ pub enum EnvVarsCommand {
262271
password_creation_settings: PasswordCreationSettings,
263272
},
264273
#[structopt(
265-
about = "Update or create env var for a specific secret. Will always update the first secret with the specified name in the env var list"
274+
about = "Update or create env var for a specific secret and then returns the value. Will always update the first secret with the specified name in the env var list. If no new value is supplied, a random value will be created"
266275
)]
267276
UpdateOrCreate {
268277
#[structopt(
@@ -272,7 +281,11 @@ pub enum EnvVarsCommand {
272281
secret_id: Uuid,
273282
#[structopt(required = true, help = "The name of the env var")]
274283
env_var_name: String,
275-
#[structopt(required = true, help = "The value of the env var")]
276-
env_var_value: String,
284+
#[structopt(
285+
help = "The value of the env var. If no value is provided, a random one will be created"
286+
)]
287+
env_var_value: Option<String>,
288+
#[structopt(flatten)]
289+
password_creation_settings: PasswordCreationSettings,
277290
},
278291
}

0 commit comments

Comments
 (0)