]> git.proxmox.com Git - proxmox-backup.git/blob - pbs-config/src/domains.rs
update to first proxmox crate split
[proxmox-backup.git] / pbs-config / src / domains.rs
1 use std::collections::HashMap;
2
3 use anyhow::{Error};
4 use lazy_static::lazy_static;
5 use serde::{Serialize, Deserialize};
6
7 use proxmox_schema::{api, ApiType, Updater, Schema};
8 use proxmox_section_config::{SectionConfig, SectionConfigData, SectionConfigPlugin};
9
10 use pbs_api_types::{REALM_ID_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA};
11 use crate::{open_backup_lockfile, replace_backup_config, BackupLockGuard};
12
13 lazy_static! {
14 pub static ref CONFIG: SectionConfig = init();
15 }
16
17 #[api()]
18 #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
19 #[serde(rename_all = "lowercase")]
20 /// Use the value of this attribute/claim as unique user name. It is
21 /// up to the identity provider to guarantee the uniqueness. The
22 /// OpenID specification only guarantees that Subject ('sub') is unique. Also
23 /// make sure that the user is not allowed to change that attribute by
24 /// himself!
25 pub enum OpenIdUserAttribute {
26 /// Subject (OpenId 'sub' claim)
27 Subject,
28 /// Username (OpenId 'preferred_username' claim)
29 Username,
30 /// Email (OpenId 'email' claim)
31 Email,
32 }
33
34 #[api(
35 properties: {
36 realm: {
37 schema: REALM_ID_SCHEMA,
38 },
39 "client-key": {
40 optional: true,
41 },
42 comment: {
43 optional: true,
44 schema: SINGLE_LINE_COMMENT_SCHEMA,
45 },
46 autocreate: {
47 optional: true,
48 default: false,
49 },
50 "username-claim": {
51 type: OpenIdUserAttribute,
52 optional: true,
53 },
54 },
55 )]
56 #[derive(Serialize, Deserialize, Updater)]
57 #[serde(rename_all="kebab-case")]
58 /// OpenID configuration properties.
59 pub struct OpenIdRealmConfig {
60 #[updater(skip)]
61 pub realm: String,
62 /// OpenID Issuer Url
63 pub issuer_url: String,
64 /// OpenID Client ID
65 pub client_id: String,
66 /// OpenID Client Key
67 #[serde(skip_serializing_if="Option::is_none")]
68 pub client_key: Option<String>,
69 #[serde(skip_serializing_if="Option::is_none")]
70 pub comment: Option<String>,
71 /// Automatically create users if they do not exist.
72 #[serde(skip_serializing_if="Option::is_none")]
73 pub autocreate: Option<bool>,
74 #[updater(skip)]
75 #[serde(skip_serializing_if="Option::is_none")]
76 pub username_claim: Option<OpenIdUserAttribute>,
77 }
78
79 fn init() -> SectionConfig {
80 let obj_schema = match OpenIdRealmConfig::API_SCHEMA {
81 Schema::Object(ref obj_schema) => obj_schema,
82 _ => unreachable!(),
83 };
84
85 let plugin = SectionConfigPlugin::new("openid".to_string(), Some(String::from("realm")), obj_schema);
86 let mut config = SectionConfig::new(&REALM_ID_SCHEMA);
87 config.register_plugin(plugin);
88
89 config
90 }
91
92 pub const DOMAINS_CFG_FILENAME: &str = "/etc/proxmox-backup/domains.cfg";
93 pub const DOMAINS_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.domains.lck";
94
95 /// Get exclusive lock
96 pub fn lock_config() -> Result<BackupLockGuard, Error> {
97 open_backup_lockfile(DOMAINS_CFG_LOCKFILE, None, true)
98 }
99
100 pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
101
102 let content = proxmox::tools::fs::file_read_optional_string(DOMAINS_CFG_FILENAME)?
103 .unwrap_or_else(|| "".to_string());
104
105 let digest = openssl::sha::sha256(content.as_bytes());
106 let data = CONFIG.parse(DOMAINS_CFG_FILENAME, &content)?;
107 Ok((data, digest))
108 }
109
110 pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
111 let raw = CONFIG.write(DOMAINS_CFG_FILENAME, &config)?;
112 replace_backup_config(DOMAINS_CFG_FILENAME, raw.as_bytes())
113 }
114
115 // shell completion helper
116 pub fn complete_realm_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
117 match config() {
118 Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
119 Err(_) => return vec![],
120 }
121 }
122
123 pub fn complete_openid_realm_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
124 match config() {
125 Ok((data, _digest)) => data.sections.iter()
126 .filter_map(|(id, (t, _))| if t == "openid" { Some(id.to_string()) } else { None })
127 .collect(),
128 Err(_) => return vec![],
129 }
130 }