1#[cfg(not(test))]
51const ROOTASROLE: &str = "/etc/security/rootasrole.json";
52#[cfg(test)]
53const ROOTASROLE: &str = "target/rootasrole.json";
54
55use std::{cell::RefCell, error::Error, ffi::OsStr, path::PathBuf, rc::Rc};
56
57use bon::Builder;
58use log::debug;
59use serde::{Deserialize, Serialize};
60
61pub mod api;
62pub mod database;
63pub mod plugin;
64pub mod util;
65pub mod version;
66
67use util::{
68 dac_override_effective, open_with_privileges, read_effective, toggle_lock_config,
69 write_json_config, ImmutableLock,
70};
71
72use database::{
73 migration::Migration,
74 structs::SConfig,
75 versionning::{Versioning, JSON_MIGRATIONS, SETTINGS_MIGRATIONS},
76};
77
78#[derive(Serialize, Deserialize, Debug, Clone)]
79#[serde(rename_all = "lowercase")]
80pub enum StorageMethod {
81 JSON,
82 #[serde(other)]
87 Unknown,
88}
89
90pub enum Storage {
91 JSON(Rc<RefCell<SConfig>>),
92}
93
94#[derive(Serialize, Deserialize, Debug, Clone, Builder)]
95pub struct SettingsFile {
96 pub storage: Settings,
97 #[serde(flatten)]
98 pub config: Rc<RefCell<SConfig>>,
99}
100
101#[derive(Serialize, Deserialize, Debug, Clone, Builder)]
102pub struct Settings {
103 #[builder(default = StorageMethod::JSON)]
104 pub method: StorageMethod,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub settings: Option<RemoteStorageSettings>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub ldap: Option<LdapSettings>,
109}
110
111#[derive(Serialize, Deserialize, Debug, Clone, Builder, Default)]
112pub struct RemoteStorageSettings {
113 #[serde(skip_serializing_if = "Option::is_none")]
114 #[builder(name = not_immutable,with = || false)]
115 pub immutable: Option<bool>,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 #[builder(into)]
118 pub path: Option<PathBuf>,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub host: Option<String>,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub port: Option<u16>,
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub auth: Option<ConnectionAuth>,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub database: Option<String>,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub schema: Option<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub table_prefix: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub properties: Option<Properties>,
133}
134
135#[derive(Serialize, Deserialize, Debug, Clone)]
136pub struct ConnectionAuth {
137 pub user: String,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub password: Option<String>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub client_ssl: Option<ClientSsl>,
142}
143
144#[derive(Serialize, Deserialize, Debug, Clone)]
145pub struct ClientSsl {
146 pub enabled: bool,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub ca_cert: Option<String>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub client_cert: Option<String>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub client_key: Option<String>,
153}
154
155#[derive(Serialize, Deserialize, Debug, Clone)]
156pub struct Properties {
157 pub use_unicode: bool,
158 pub character_encoding: String,
159}
160
161#[derive(Serialize, Deserialize, Debug, Clone)]
162pub struct LdapSettings {
163 pub enabled: bool,
164 pub host: String,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub port: Option<u16>,
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub auth: Option<ConnectionAuth>,
169 pub base_dn: String,
170 pub user_dn: String,
171 pub group_dn: String,
172 pub user_filter: String,
173 pub group_filter: String,
174}
175
176impl Default for SettingsFile {
177 fn default() -> Self {
178 Self {
179 storage: Settings::default(),
180 config: Rc::new(RefCell::new(SConfig::default())),
181 }
182 }
183}
184
185impl Default for Settings {
187 fn default() -> Self {
188 Self {
189 method: StorageMethod::JSON,
190 settings: None,
191 ldap: None,
192 }
193 }
194}
195
196pub fn save_settings(settings: Rc<RefCell<SettingsFile>>) -> Result<(), Box<dyn Error>> {
197 let default_remote: RemoteStorageSettings = RemoteStorageSettings::default();
198 let into = ROOTASROLE.into();
200 let binding = settings.as_ref().borrow();
201 let path = binding
202 .storage
203 .settings
204 .as_ref()
205 .unwrap_or(&default_remote)
206 .path
207 .as_ref()
208 .unwrap_or(&into);
209 if let Some(settings) = &settings.as_ref().borrow().storage.settings {
210 if settings.immutable.unwrap_or(true) {
211 debug!("Toggling immutable on for config file");
212 toggle_lock_config(path, ImmutableLock::Unset)?;
213 }
214 }
215 debug!("Writing config file");
216 let versionned: Versioning<Rc<RefCell<SettingsFile>>> = Versioning::new(settings.clone());
217 write_json_config(&versionned, ROOTASROLE)?;
218 if let Some(settings) = &settings.as_ref().borrow().storage.settings {
219 if settings.immutable.unwrap_or(true) {
220 debug!("Toggling immutable off for config file");
221 toggle_lock_config(path, ImmutableLock::Set)?;
222 }
223 }
224 debug!("Resetting dac privilege");
225 dac_override_effective(false)?;
226 Ok(())
227}
228
229pub fn get_settings<S>(path: &S) -> Result<Rc<RefCell<SettingsFile>>, Box<dyn Error>>
230where
231 S: AsRef<OsStr> + ?Sized,
232{
233 if !std::path::Path::new(path.as_ref()).exists() {
235 return Ok(rc_refcell!(SettingsFile::default()));
236 }
237 let file = open_with_privileges(path.as_ref())?;
239 let value: Versioning<SettingsFile> = serde_json::from_reader(file)
240 .inspect_err(|e| {
241 debug!("Error reading file: {}", e);
242 })
243 .unwrap_or_default();
244 read_effective(false).or(dac_override_effective(false))?;
245 debug!("{}", serde_json::to_string_pretty(&value)?);
246 let settingsfile = rc_refcell!(value.data);
247 if let Ok(true) = Migration::migrate(
248 &value.version,
249 &mut *settingsfile.as_ref().borrow_mut(),
250 SETTINGS_MIGRATIONS,
251 ) {
252 if let Ok(true) = Migration::migrate(
253 &value.version,
254 &mut *settingsfile
255 .as_ref()
256 .borrow_mut()
257 .config
258 .as_ref()
259 .borrow_mut(),
260 JSON_MIGRATIONS,
261 ) {
262 save_settings(settingsfile.clone())?;
263 } else {
264 debug!("No config migrations needed");
265 }
266 } else {
267 debug!("No settings migrations needed");
268 }
269 Ok(settingsfile)
270}