2 use ::serde
::{Deserialize, Serialize}
;
4 use proxmox
::api
::{api, const_regex, schema::*}
;
5 use proxmox
::tools
::*; // required to use IPRE!() macro ???
7 // File names: may not contain slashes, may not start with "."
8 pub const FILENAME_FORMAT
: ApiStringFormat
= ApiStringFormat
::VerifyFn(|name
| {
9 if name
.starts_with('
.'
) {
10 bail
!("file names may not start with '.'");
12 if name
.contains('
/'
) {
13 bail
!("file names may not contain slashes");
18 macro_rules
! DNS_LABEL { () => (r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)") }
19 macro_rules
! DNS_NAME { () => (concat!(r"(?:", DNS_LABEL!() , r"\.)*", DNS_LABEL!())) }
21 // we only allow a limited set of characters
22 // colon is not allowed, because we store usernames in
23 // colon separated lists)!
24 // slash is not allowed because it is used as pve API delimiter
25 // also see "man useradd"
26 macro_rules
! USER_NAME_REGEX_STR { () => (r"(?:[^\s:/[:cntrl:]]+)") }
28 macro_rules
! PROXMOX_SAFE_ID_REGEX_STR { () => (r"(?:[A-Za-z0-9_][A-Za-z0-9._\-]*)") }
31 pub IP_FORMAT_REGEX
= IPRE
!();
32 pub SHA256_HEX_REGEX
= r
"^[a-f0-9]{64}$"; // fixme: define in common_regex ?
33 pub SYSTEMD_DATETIME_REGEX
= r
"^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?$"; // fixme: define in common_regex ?
35 pub PASSWORD_REGEX
= r
"^[[:^cntrl:]]*$"; // everything but control characters
37 /// Regex for safe identifiers.
40 /// [article](https://dwheeler.com/essays/fixing-unix-linux-filenames.html)
41 /// contains further information why it is reasonable to restict
42 /// names this way. This is not only useful for filenames, but for
43 /// any identifier command line tools work with.
44 pub PROXMOX_SAFE_ID_REGEX
= concat
!(r
"^", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"$");
46 pub SINGLE_LINE_COMMENT_REGEX
= r
"^[[:^cntrl:]]*$";
48 pub HOSTNAME_REGEX
= r
"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?)$";
50 pub DNS_NAME_REGEX
= concat
!(r
"^", DNS_NAME
!(), r
"$");
52 pub DNS_NAME_OR_IP_REGEX
= concat
!(r
"^", DNS_NAME
!(), "|", IPRE
!(), r
"$");
54 pub PROXMOX_USER_ID_REGEX
= concat
!(r
"^", USER_NAME_REGEX_STR
!(), r
"@", PROXMOX_SAFE_ID_REGEX_STR
!(), r
"$");
57 pub const SYSTEMD_DATETIME_FORMAT
: ApiStringFormat
=
58 ApiStringFormat
::Pattern(&SYSTEMD_DATETIME_REGEX
);
60 pub const IP_FORMAT
: ApiStringFormat
=
61 ApiStringFormat
::Pattern(&IP_FORMAT_REGEX
);
63 pub const PVE_CONFIG_DIGEST_FORMAT
: ApiStringFormat
=
64 ApiStringFormat
::Pattern(&SHA256_HEX_REGEX
);
66 pub const PROXMOX_SAFE_ID_FORMAT
: ApiStringFormat
=
67 ApiStringFormat
::Pattern(&PROXMOX_SAFE_ID_REGEX
);
69 pub const SINGLE_LINE_COMMENT_FORMAT
: ApiStringFormat
=
70 ApiStringFormat
::Pattern(&SINGLE_LINE_COMMENT_REGEX
);
72 pub const HOSTNAME_FORMAT
: ApiStringFormat
=
73 ApiStringFormat
::Pattern(&HOSTNAME_REGEX
);
75 pub const DNS_NAME_FORMAT
: ApiStringFormat
=
76 ApiStringFormat
::Pattern(&DNS_NAME_REGEX
);
78 pub const DNS_NAME_OR_IP_FORMAT
: ApiStringFormat
=
79 ApiStringFormat
::Pattern(&DNS_NAME_OR_IP_REGEX
);
81 pub const PROXMOX_USER_ID_FORMAT
: ApiStringFormat
=
82 ApiStringFormat
::Pattern(&PROXMOX_USER_ID_REGEX
);
84 pub const PASSWORD_FORMAT
: ApiStringFormat
=
85 ApiStringFormat
::Pattern(&PASSWORD_REGEX
);
88 pub const PVE_CONFIG_DIGEST_SCHEMA
: Schema
= StringSchema
::new(r
#"\
89 Prevent changes if current configuration file has different SHA256 digest.
90 This can be used to prevent concurrent modifications.
93 .format(&PVE_CONFIG_DIGEST_FORMAT
)
97 pub const CHUNK_DIGEST_FORMAT
: ApiStringFormat
=
98 ApiStringFormat
::Pattern(&SHA256_HEX_REGEX
);
100 pub const CHUNK_DIGEST_SCHEMA
: Schema
= StringSchema
::new("Chunk digest (SHA256).")
101 .format(&CHUNK_DIGEST_FORMAT
)
104 pub const NODE_SCHEMA
: Schema
= StringSchema
::new("Node name (or 'localhost')")
105 .format(&ApiStringFormat
::VerifyFn(|node
| {
106 if node
== "localhost" || node
== proxmox
::tools
::nodename() {
109 bail
!("no such node '{}'", node
);
114 pub const SEARCH_DOMAIN_SCHEMA
: Schema
=
115 StringSchema
::new("Search domain for host-name lookup.").schema();
117 pub const FIRST_DNS_SERVER_SCHEMA
: Schema
=
118 StringSchema
::new("First name server IP address.")
122 pub const SECOND_DNS_SERVER_SCHEMA
: Schema
=
123 StringSchema
::new("Second name server IP address.")
127 pub const THIRD_DNS_SERVER_SCHEMA
: Schema
=
128 StringSchema
::new("Third name server IP address.")
132 pub const BACKUP_ARCHIVE_NAME_SCHEMA
: Schema
=
133 StringSchema
::new("Backup archive name.")
134 .format(&PROXMOX_SAFE_ID_FORMAT
)
137 pub const BACKUP_TYPE_SCHEMA
: Schema
=
138 StringSchema
::new("Backup type.")
139 .format(&ApiStringFormat
::Enum(&["vm", "ct", "host"]))
142 pub const BACKUP_ID_SCHEMA
: Schema
=
143 StringSchema
::new("Backup ID.")
144 .format(&PROXMOX_SAFE_ID_FORMAT
)
147 pub const BACKUP_TIME_SCHEMA
: Schema
=
148 IntegerSchema
::new("Backup time (Unix epoch.)")
149 .minimum(1_547_797_308)
152 pub const UPID_SCHEMA
: Schema
= StringSchema
::new("Unique Process/Task ID.")
156 pub const DATASTORE_SCHEMA
: Schema
= StringSchema
::new("Datastore name.")
157 .format(&PROXMOX_SAFE_ID_FORMAT
)
162 pub const REMOTE_ID_SCHEMA
: Schema
= StringSchema
::new("Remote ID.")
163 .format(&PROXMOX_SAFE_ID_FORMAT
)
168 pub const SINGLE_LINE_COMMENT_SCHEMA
: Schema
= StringSchema
::new("Comment (single line).")
169 .format(&SINGLE_LINE_COMMENT_FORMAT
)
172 pub const HOSTNAME_SCHEMA
: Schema
= StringSchema
::new("Hostname (as defined in RFC1123).")
173 .format(&HOSTNAME_FORMAT
)
176 pub const DNS_NAME_OR_IP_SCHEMA
: Schema
= StringSchema
::new("DNS name or IP address.")
177 .format(&DNS_NAME_OR_IP_FORMAT
)
180 pub const PROXMOX_AUTH_REALM_SCHEMA
: Schema
= StringSchema
::new("Authentication domain ID")
181 .format(&PROXMOX_SAFE_ID_FORMAT
)
186 pub const PROXMOX_USER_ID_SCHEMA
: Schema
= StringSchema
::new("User ID")
187 .format(&PROXMOX_USER_ID_FORMAT
)
193 // Complex type definitions
198 schema
: BACKUP_TYPE_SCHEMA
,
201 schema
: BACKUP_ID_SCHEMA
,
204 schema
: BACKUP_TIME_SCHEMA
,
208 schema
: BACKUP_ARCHIVE_NAME_SCHEMA
213 #[derive(Serialize, Deserialize)]
214 #[serde(rename_all="kebab-case")]
215 /// Basic information about backup snapshot.
216 pub struct SnapshotListItem
{
217 pub backup_type
: String
, // enum
218 pub backup_id
: String
,
219 pub backup_time
: i64,
220 /// List of contained archive files.
221 pub files
: Vec
<String
>,
222 /// Overall snapshot size (sum of all archive sizes).
223 #[serde(skip_serializing_if="Option::is_none")]
224 pub size
: Option
<u64>,
231 fn test_proxmox_user_id_schema() -> Result
<(), Error
> {
233 let schema
= PROXMOX_USER_ID_SCHEMA
;
235 let invalid_user_ids
= [
240 "xx x@test", // contains space
241 "xx\nx@test", // contains control character
242 "x:xx@test", // contains collon
243 "xx/x@test", // contains slash
244 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@test", // too long
247 for name
in invalid_user_ids
.iter() {
248 if let Ok(_
) = parse_simple_value(name
, &schema
) {
249 bail
!("test userid '{}' failed - got Ok() while expection an error.", name
);
253 let valid_user_ids
= [
257 "xxx@_T_E_S_T-it.com",
258 "x_x-x.x@test-it.com",
261 for name
in valid_user_ids
.iter() {
262 let v
= match parse_simple_value(name
, &schema
) {
265 bail
!("unable to parse userid '{}' - {}", name
, err
);
269 if v
!= serde_json
::json
!(name
) {
270 bail
!("unable to parse userid '{}' - got wrong value {:?}", name
, v
);