signstar_common/
admin_credentials.rs

1//! Data and functions for using administrative credentials on a Signstar host.
2//!
3//! # Examples
4//!
5//! ```
6//! use signstar_common::admin_credentials::get_credentials_dir;
7//!
8//! // Get the directory path in which administrative credentials reside.
9//! println!("{:?}", get_credentials_dir());
10//! ```
11
12use std::{
13    fs::{Permissions, create_dir_all, set_permissions},
14    os::unix::fs::{PermissionsExt, chown},
15    path::PathBuf,
16};
17
18use crate::common::{CREDENTIALS_DIR_MODE, get_data_home};
19
20/// File name of plaintext administrative credentials.
21const PLAINTEXT_CREDENTIALS_FILE: &str = "admin-credentials.toml";
22
23/// File name of systemd-creds encrypted administrative credentials.
24const SYSTEMD_CREDS_CREDENTIALS_FILE: &str = "admin-credentials.creds";
25
26/// The directory for administrative credentials (encrypted and unencrypted).
27const CREDENTIALS_DIR: &str = "creds/";
28
29/// An error that may occur when handling credentials.
30#[derive(Debug, thiserror::Error)]
31pub enum Error {
32    /// Applying permissions to a file failed.
33    #[error("Unable to apply permissions {permissions} to {path}:\n{source}")]
34    ApplyPermissions {
35        /// The octal permissions applied to a `path`.
36        permissions: u32,
37        /// The path the `permissions` are applied to.
38        path: PathBuf,
39        /// The error source.
40        source: std::io::Error,
41    },
42
43    /// No plaintext administrative credentials file can be found
44    #[error("Unable to create directory {dir}:\n{source}")]
45    CreateDirectory {
46        /// The directory that cannot be created.
47        dir: &'static str,
48        /// The error source.
49        source: std::io::Error,
50    },
51
52    /// The ownership of a directory can not be set.
53    #[error("Ownership of directory {dir} can not be changed to user {system_user}: {source}")]
54    DirChangeOwner {
55        /// The directory for which ownership cannot be transferred to a `system_user`.
56        dir: PathBuf,
57        /// The system user that cannot be made owner of `dir`.
58        system_user: String,
59        /// The error source.
60        source: std::io::Error,
61    },
62}
63
64/// Returns the path of the directory in which administrative credentials reside.
65pub fn get_credentials_dir() -> PathBuf {
66    get_data_home().join(PathBuf::from(CREDENTIALS_DIR))
67}
68
69/// Returns the file path for plaintext administrative credentials.
70pub fn get_plaintext_credentials_file() -> PathBuf {
71    get_credentials_dir().join(PathBuf::from(PLAINTEXT_CREDENTIALS_FILE))
72}
73
74/// Returns the file path for systemd-creds encrypted administrative credentials.
75pub fn get_systemd_creds_credentials_file() -> PathBuf {
76    get_credentials_dir().join(PathBuf::from(SYSTEMD_CREDS_CREDENTIALS_FILE))
77}
78
79/// Creates the directory for administrative credentials.
80///
81/// # Errors
82///
83/// Returns an error if the directory or one of its parents can not be created.
84/// Refer to [`create_dir_all`] for further information on failure scenarios.
85pub fn create_credentials_dir() -> Result<(), Error> {
86    let credentials_dir = get_credentials_dir();
87    create_dir_all(credentials_dir.as_path()).map_err(|source| Error::CreateDirectory {
88        dir: CREDENTIALS_DIR,
89        source,
90    })?;
91
92    // Set the permissions of the credentials directory to `CREDENTIALS_DIR_MODE`.
93    set_permissions(
94        credentials_dir.as_path(),
95        Permissions::from_mode(CREDENTIALS_DIR_MODE),
96    )
97    .map_err(|source| Error::ApplyPermissions {
98        permissions: CREDENTIALS_DIR_MODE,
99        path: credentials_dir.clone(),
100        source,
101    })?;
102
103    // Recursively chown all directories to root, until `DATA_HOME` is
104    // reached.
105    let data_home = get_data_home();
106    let mut chown_dir = credentials_dir.clone();
107    while chown_dir != data_home {
108        chown(&chown_dir, Some(0), Some(0)).map_err(|source| Error::DirChangeOwner {
109            dir: chown_dir.to_path_buf(),
110            system_user: "root".to_string(),
111            source,
112        })?;
113        if let Some(parent) = &chown_dir.parent() {
114            chown_dir = parent.to_path_buf()
115        } else {
116            break;
117        }
118    }
119
120    Ok(())
121}