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