googauth_lib/
config_file.rs

1use dirs::home_dir;
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::fs::{create_dir_all, set_permissions, File, Permissions};
5use std::io::{BufReader, BufWriter};
6use std::os::unix::fs::PermissionsExt;
7use std::path::PathBuf;
8use crate::errors::LibError;
9
10/// A configuration file that saves the information necessary
11/// to fetch tokens and to be able to refresh said tokens when
12/// needed.
13#[derive(Serialize, Deserialize)]
14pub struct ConfigFile {
15    pub version: u32,
16    pub name: String,
17    pub client_id: String,
18    pub client_secret: String,
19    pub scopes: Vec<String>,
20    pub redirect_url: String,
21    pub refresh_token: Option<String>,
22    pub id_token: Option<Token>,
23    pub access_token: Option<Token>,
24}
25
26impl ConfigFile {
27    pub fn new(
28        name: &str,
29        client_id: &str,
30        client_secret: &str,
31        scopes: &[String],
32        redirect_url: &str,
33    ) -> ConfigFile {
34        ConfigFile {
35            version: 1,
36            name: name.to_string(),
37            client_id: client_id.to_string(),
38            client_secret: client_secret.to_string(),
39            scopes: scopes.iter().map(|s| s.to_string()).collect(),
40            redirect_url: redirect_url.to_string(),
41            refresh_token: None,
42            id_token: None,
43            access_token: None,
44        }
45    }
46}
47
48pub struct ConfigBasePath {
49    path: PathBuf,
50}
51
52impl ConfigBasePath {
53    pub fn default() -> Result<ConfigBasePath, LibError> {
54        let mut config_dir = match home_dir() {
55            None => {
56                return Err(LibError::HomeDirectoryNotFound);
57            }
58            Some(dir) => dir,
59        };
60        config_dir.push(".googauth");
61        Ok(ConfigBasePath { path: config_dir } )
62    }
63
64    pub fn from(path: PathBuf) -> ConfigBasePath {
65        ConfigBasePath { path }
66    }
67}
68
69#[derive(Clone, Serialize, Deserialize)]
70pub struct Token {
71    pub secret: String,
72    pub exp: u64,
73}
74
75impl Token {
76    pub fn new(secret: String, exp: u64) -> Token {
77        Token { secret, exp }
78    }
79}
80
81impl ConfigFile {
82    pub fn config_file(name: &str, config_base_path: &ConfigBasePath) -> Result<PathBuf, LibError> {
83        let mut config_dir = config_base_path.path.clone();
84        config_dir.push(name);
85        Ok(config_dir)
86    }
87
88    pub fn list_configs(config_base_path: &ConfigBasePath) -> Result<Vec<ConfigFile>, LibError> {
89        let config_dir = config_base_path.path.clone();
90
91        if config_dir.is_dir() {
92            let mut result = Vec::new();
93            let dirs = fs::read_dir(config_dir)?;
94            for entry in dirs {
95                let entry = entry?;
96                let path = entry.path();
97                if path.is_file() {
98                    let file_name = path.file_name().ok_or(LibError::FilenameError)?.to_str().ok_or(LibError::FilenameError)?;
99                    if let Ok(config_file) = ConfigFile::read_config(file_name, config_base_path) {
100                        result.push(config_file);
101                    }
102                }
103            }
104            return Ok(result);
105        }
106        Err(LibError::ConfigsDirectoryNotADirectory(config_dir))
107    }
108
109    pub fn read_config(name: &str, config_base_path: &ConfigBasePath) -> Result<ConfigFile, LibError> {
110        let config_dir = ConfigFile::config_file(name, config_base_path)?;
111
112        let config_file = File::open(config_dir.as_path())?;
113        let config_file_reader = BufReader::new(config_file);
114        let config = serde_json::from_reader(config_file_reader)?;
115        Ok(config)
116    }
117
118    pub fn save_config(&self, config_base_path: &ConfigBasePath) -> Result<(), LibError> {
119        let mut config_dir = config_base_path.path.clone();
120
121        create_dir_all(config_dir.as_path())?;
122        if cfg!(unix) {
123            set_permissions(config_dir.as_path(), Permissions::from_mode(0o700))?
124        }
125
126        config_dir.push(self.name.to_string());
127
128        let config_file = File::create(config_dir.as_path())?;
129
130        if cfg!(unix) {
131            set_permissions(config_dir.as_path(), Permissions::from_mode(0o600))?;
132        }
133
134        let config_file_writer = BufWriter::new(config_file);
135
136        serde_json::to_writer(config_file_writer, self)?;
137
138        Ok(())
139    }
140}