1 use std
::collections
::HashMap
;
3 use anyhow
::{bail, format_err, Error}
;
4 use serde
::{Serialize, Deserialize}
;
5 use serde_json
::{from_value, Value}
;
7 use proxmox
::tools
::fs
::CreateOptions
;
9 use pbs_api_types
::Authid
;
11 use crate::{open_backup_lockfile, BackupLockGuard}
;
13 const LOCK_FILE
: &str = pbs_buildcfg
::configdir
!("/token.shadow.lock");
14 const CONF_FILE
: &str = pbs_buildcfg
::configdir
!("/token.shadow");
16 #[derive(Serialize, Deserialize)]
17 #[serde(rename_all="kebab-case")]
18 /// ApiToken id / secret pair
19 pub struct ApiTokenSecret
{
25 fn lock_config() -> Result
<BackupLockGuard
, Error
> {
26 open_backup_lockfile(LOCK_FILE
, None
, true)
29 fn read_file() -> Result
<HashMap
<Authid
, String
>, Error
> {
30 let json
= proxmox
::tools
::fs
::file_get_json(CONF_FILE
, Some(Value
::Null
))?
;
32 if json
== Value
::Null
{
35 // swallow serde error which might contain sensitive data
36 from_value(json
).map_err(|_err
| format_err
!("unable to parse '{}'", CONF_FILE
))
40 fn write_file(data
: HashMap
<Authid
, String
>) -> Result
<(), Error
> {
41 let backup_user
= crate::backup_user()?
;
42 let options
= CreateOptions
::new()
43 .perm(nix
::sys
::stat
::Mode
::from_bits_truncate(0o0640))
44 .owner(backup_user
.uid
)
45 .group(backup_user
.gid
);
47 let json
= serde_json
::to_vec(&data
)?
;
48 proxmox
::tools
::fs
::replace_file(CONF_FILE
, &json
, options
, true)
52 /// Verifies that an entry for given tokenid / API token secret exists
53 pub fn verify_secret(tokenid
: &Authid
, secret
: &str) -> Result
<(), Error
> {
54 if !tokenid
.is_token() {
55 bail
!("not an API token ID");
58 let data
= read_file()?
;
59 match data
.get(tokenid
) {
60 Some(hashed_secret
) => {
61 proxmox_sys
::crypt
::verify_crypt_pw(secret
, &hashed_secret
)
63 None
=> bail
!("invalid API token"),
67 /// Adds a new entry for the given tokenid / API token secret. The secret is stored as salted hash.
68 pub fn set_secret(tokenid
: &Authid
, secret
: &str) -> Result
<(), Error
> {
69 if !tokenid
.is_token() {
70 bail
!("not an API token ID");
73 let _guard
= lock_config()?
;
75 let mut data
= read_file()?
;
76 let hashed_secret
= proxmox_sys
::crypt
::encrypt_pw(secret
)?
;
77 data
.insert(tokenid
.clone(), hashed_secret
);
83 /// Deletes the entry for the given tokenid.
84 pub fn delete_secret(tokenid
: &Authid
) -> Result
<(), Error
> {
85 if !tokenid
.is_token() {
86 bail
!("not an API token ID");
89 let _guard
= lock_config()?
;
91 let mut data
= read_file()?
;