]>
Commit | Line | Data |
---|---|---|
eb5e3420 WB |
1 | use std::collections::HashSet; |
2 | ||
f7d4e4b5 | 3 | use anyhow::{bail, format_err, Error}; |
5e6b9e44 DM |
4 | |
5 | use proxmox_backup::api2; | |
6 | use proxmox::api::*; | |
7 | use proxmox::api::schema::*; | |
5e6b9e44 DM |
8 | |
9 | // Simply test if api lookup tables inside Routers and Schemas are | |
10 | // correctly sorted. | |
11 | ||
12 | fn verify_object_schema(schema: &ObjectSchema) -> Result<(), Error> { | |
13 | ||
14 | let map = schema.properties; | |
15 | ||
3984a5fd | 16 | if !map.is_empty() { |
5e6b9e44 DM |
17 | |
18 | for i in 1..map.len() { | |
19 | ||
20 | if map[i].0 <= map[i-1].0 { | |
21 | for (name, _, _) in map.iter() { | |
22 | eprintln!("{}", name); | |
23 | } | |
24 | bail!("found unsorted property map ({} <= {})", map[i].0, map[i-1].0); | |
25 | } | |
26 | } | |
27 | } | |
28 | ||
29 | for (_name, _, sub_schema) in map.iter() { | |
30 | verify_schema(sub_schema)?; | |
31 | } | |
32 | ||
33 | Ok(()) | |
34 | } | |
35 | ||
eb5e3420 WB |
36 | // verify entries in an AllOf schema are actually object schemas and that they don't contain |
37 | // duplicate keys | |
38 | fn verify_all_of_schema(schema: &AllOfSchema) -> Result<(), Error> { | |
39 | for entry in schema.list { | |
40 | match entry { | |
41 | Schema::Object(obj) => verify_object_schema(obj)?, | |
42 | _ => bail!("AllOf schema with a non-object schema entry!"), | |
43 | } | |
44 | } | |
45 | ||
46 | let mut keys = HashSet::<&'static str>::new(); | |
47 | let mut dupes = String::new(); | |
48 | for property in schema.properties() { | |
2fb1bdda FG |
49 | if !keys.insert(property.0) { |
50 | if !dupes.is_empty() { | |
eb5e3420 WB |
51 | dupes.push_str(", "); |
52 | } | |
53 | dupes.push_str(property.0); | |
54 | } | |
55 | } | |
56 | if !dupes.is_empty() { | |
57 | bail!("Duplicate keys found in AllOf schema: {}", dupes); | |
58 | } | |
59 | ||
60 | Ok(()) | |
61 | } | |
62 | ||
5e6b9e44 DM |
63 | fn verify_schema(schema: &Schema) -> Result<(), Error> { |
64 | match schema { | |
65 | Schema::Object(obj_schema) => { | |
66 | verify_object_schema(obj_schema)?; | |
67 | } | |
eb5e3420 WB |
68 | Schema::AllOf(all_of_schema) => { |
69 | verify_all_of_schema(all_of_schema)?; | |
70 | } | |
5e6b9e44 DM |
71 | Schema::Array(arr_schema) => { |
72 | verify_schema(arr_schema.items)?; | |
73 | } | |
74 | _ => {} | |
75 | } | |
76 | Ok(()) | |
77 | } | |
74c08a57 DM |
78 | |
79 | fn verify_access_permissions(permission: &Permission) -> Result<(), Error> { | |
80 | ||
81 | match permission { | |
82 | Permission::Or(list) => { | |
83 | for perm in list.iter() { verify_access_permissions(perm)?; } | |
84 | } | |
85 | Permission::And(list) => { | |
86 | for perm in list.iter() { verify_access_permissions(perm)?; } | |
87 | } | |
88 | Permission::Privilege(path_comp, ..)=> { | |
89 | let path = format!("/{}", path_comp.join("/")); | |
90 | proxmox_backup::config::acl::check_acl_path(&path)?; | |
91 | } | |
92 | _ => {} | |
93 | } | |
94 | Ok(()) | |
95 | } | |
96 | ||
5e6b9e44 DM |
97 | fn verify_api_method( |
98 | method: &str, | |
99 | path: &str, | |
100 | info: &ApiMethod | |
101 | ) -> Result<(), Error> | |
102 | { | |
eb5e3420 WB |
103 | match &info.parameters { |
104 | ParameterSchema::Object(obj) => { | |
105 | verify_object_schema(obj) | |
106 | .map_err(|err| format_err!("{} {} parameters: {}", method, path, err))?; | |
107 | } | |
108 | ParameterSchema::AllOf(all_of) => { | |
109 | verify_all_of_schema(all_of) | |
110 | .map_err(|err| format_err!("{} {} parameters: {}", method, path, err))?; | |
111 | } | |
112 | } | |
5e6b9e44 | 113 | |
eb5e3420 | 114 | verify_schema(info.returns.schema) |
5e6b9e44 DM |
115 | .map_err(|err| format_err!("{} {} returns: {}", method, path, err))?; |
116 | ||
74c08a57 DM |
117 | verify_access_permissions(info.access.permission) |
118 | .map_err(|err| format_err!("{} {} access: {}", method, path, err))?; | |
119 | ||
5e6b9e44 DM |
120 | Ok(()) |
121 | } | |
122 | ||
123 | fn verify_dirmap( | |
124 | path: &str, | |
125 | dirmap: SubdirMap, | |
126 | ) -> Result<(), Error> { | |
127 | ||
3984a5fd | 128 | if !dirmap.is_empty() { |
5e6b9e44 DM |
129 | |
130 | for i in 1..dirmap.len() { | |
131 | ||
132 | if dirmap[i].0 <= dirmap[i-1].0 { | |
133 | for (name, _) in dirmap.iter() { | |
134 | eprintln!("{}/{}", path, name); | |
135 | } | |
136 | bail!("found unsorted dirmap at {:?} ({} <= {})", path, dirmap[i].0, dirmap[i-1].0); | |
137 | } | |
138 | ||
139 | } | |
140 | } | |
141 | ||
142 | for (name, router) in dirmap.iter() { | |
143 | let sub_path = format!("{}/{}", path, name); | |
144 | verify_router(&sub_path, router)?; | |
145 | } | |
146 | ||
147 | Ok(()) | |
148 | } | |
149 | ||
150 | fn verify_router(path: &str, router: &Router) -> Result<(), Error> { | |
151 | ||
152 | println!("Verify {}", path); | |
153 | ||
154 | if let Some(api_method) = router.get { | |
155 | verify_api_method("GET", path, api_method)?; | |
156 | } | |
157 | if let Some(api_method) = router.put { | |
158 | verify_api_method("PUT", path, api_method)?; | |
159 | } | |
160 | if let Some(api_method) = router.post { | |
161 | verify_api_method("POST", path, api_method)?; | |
162 | } | |
163 | if let Some(api_method) = router.delete { | |
164 | verify_api_method("DELETE", path, api_method)?; | |
165 | } | |
166 | ||
167 | match router.subroute { | |
168 | Some(SubRoute::Map(dirmap)) => { | |
169 | verify_dirmap(path, dirmap)?; | |
170 | } | |
171 | Some(SubRoute::MatchAll { router, param_name }) => { | |
172 | let path = format!("{}/{{{}}}", path, param_name); | |
173 | verify_router(&path, router)?; | |
174 | } | |
175 | None => {} | |
176 | } | |
177 | ||
178 | Ok(()) | |
179 | } | |
180 | ||
181 | #[test] | |
182 | fn verify_backup_api() -> Result<(), Error> { | |
183 | ||
184 | let api = &api2::backup::BACKUP_API_ROUTER; | |
185 | verify_router("backup-api", api)?; | |
186 | ||
187 | Ok(()) | |
188 | } | |
189 | ||
190 | #[test] | |
191 | fn verify_reader_api() -> Result<(), Error> { | |
192 | ||
193 | let api = &api2::reader::READER_API_ROUTER; | |
194 | verify_router("reader-api", api)?; | |
195 | ||
196 | Ok(()) | |
197 | } | |
198 | ||
199 | #[test] | |
200 | fn verify_root_api() -> Result<(), Error> { | |
201 | ||
202 | let api = &api2::ROUTER; | |
203 | verify_router("root", api)?; | |
204 | ||
205 | Ok(()) | |
206 | } |