2020-12-18 11:26:08 +00:00
|
|
|
use std::collections::HashSet;
|
|
|
|
|
2020-04-17 12:11:25 +00:00
|
|
|
use anyhow::{bail, format_err, Error};
|
2019-11-22 05:43:13 +00:00
|
|
|
|
|
|
|
use proxmox_backup::api2;
|
|
|
|
use proxmox::api::*;
|
|
|
|
use proxmox::api::schema::*;
|
|
|
|
|
|
|
|
// Simply test if api lookup tables inside Routers and Schemas are
|
|
|
|
// correctly sorted.
|
|
|
|
|
|
|
|
fn verify_object_schema(schema: &ObjectSchema) -> Result<(), Error> {
|
|
|
|
|
|
|
|
let map = schema.properties;
|
|
|
|
|
2021-01-19 09:27:59 +00:00
|
|
|
if !map.is_empty() {
|
2019-11-22 05:43:13 +00:00
|
|
|
|
|
|
|
for i in 1..map.len() {
|
|
|
|
|
|
|
|
if map[i].0 <= map[i-1].0 {
|
|
|
|
for (name, _, _) in map.iter() {
|
|
|
|
eprintln!("{}", name);
|
|
|
|
}
|
|
|
|
bail!("found unsorted property map ({} <= {})", map[i].0, map[i-1].0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (_name, _, sub_schema) in map.iter() {
|
|
|
|
verify_schema(sub_schema)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-12-18 11:26:08 +00:00
|
|
|
// verify entries in an AllOf schema are actually object schemas and that they don't contain
|
|
|
|
// duplicate keys
|
|
|
|
fn verify_all_of_schema(schema: &AllOfSchema) -> Result<(), Error> {
|
|
|
|
for entry in schema.list {
|
|
|
|
match entry {
|
|
|
|
Schema::Object(obj) => verify_object_schema(obj)?,
|
2021-02-25 12:44:17 +00:00
|
|
|
Schema::AllOf(allof) => verify_all_of_schema(allof)?,
|
2020-12-18 11:26:08 +00:00
|
|
|
_ => bail!("AllOf schema with a non-object schema entry!"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut keys = HashSet::<&'static str>::new();
|
|
|
|
let mut dupes = String::new();
|
|
|
|
for property in schema.properties() {
|
2020-12-30 11:21:33 +00:00
|
|
|
if !keys.insert(property.0) {
|
|
|
|
if !dupes.is_empty() {
|
2020-12-18 11:26:08 +00:00
|
|
|
dupes.push_str(", ");
|
|
|
|
}
|
|
|
|
dupes.push_str(property.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !dupes.is_empty() {
|
|
|
|
bail!("Duplicate keys found in AllOf schema: {}", dupes);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-11-22 05:43:13 +00:00
|
|
|
fn verify_schema(schema: &Schema) -> Result<(), Error> {
|
|
|
|
match schema {
|
|
|
|
Schema::Object(obj_schema) => {
|
|
|
|
verify_object_schema(obj_schema)?;
|
|
|
|
}
|
2020-12-18 11:26:08 +00:00
|
|
|
Schema::AllOf(all_of_schema) => {
|
|
|
|
verify_all_of_schema(all_of_schema)?;
|
|
|
|
}
|
2019-11-22 05:43:13 +00:00
|
|
|
Schema::Array(arr_schema) => {
|
|
|
|
verify_schema(arr_schema.items)?;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-30 07:30:00 +00:00
|
|
|
|
|
|
|
fn verify_access_permissions(permission: &Permission) -> Result<(), Error> {
|
|
|
|
|
|
|
|
match permission {
|
|
|
|
Permission::Or(list) => {
|
|
|
|
for perm in list.iter() { verify_access_permissions(perm)?; }
|
|
|
|
}
|
|
|
|
Permission::And(list) => {
|
|
|
|
for perm in list.iter() { verify_access_permissions(perm)?; }
|
|
|
|
}
|
|
|
|
Permission::Privilege(path_comp, ..)=> {
|
|
|
|
let path = format!("/{}", path_comp.join("/"));
|
|
|
|
proxmox_backup::config::acl::check_acl_path(&path)?;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-11-22 05:43:13 +00:00
|
|
|
fn verify_api_method(
|
|
|
|
method: &str,
|
|
|
|
path: &str,
|
|
|
|
info: &ApiMethod
|
|
|
|
) -> Result<(), Error>
|
|
|
|
{
|
2020-12-18 11:26:08 +00:00
|
|
|
match &info.parameters {
|
|
|
|
ParameterSchema::Object(obj) => {
|
|
|
|
verify_object_schema(obj)
|
|
|
|
.map_err(|err| format_err!("{} {} parameters: {}", method, path, err))?;
|
|
|
|
}
|
|
|
|
ParameterSchema::AllOf(all_of) => {
|
|
|
|
verify_all_of_schema(all_of)
|
|
|
|
.map_err(|err| format_err!("{} {} parameters: {}", method, path, err))?;
|
|
|
|
}
|
|
|
|
}
|
2019-11-22 05:43:13 +00:00
|
|
|
|
2020-12-18 11:26:08 +00:00
|
|
|
verify_schema(info.returns.schema)
|
2019-11-22 05:43:13 +00:00
|
|
|
.map_err(|err| format_err!("{} {} returns: {}", method, path, err))?;
|
|
|
|
|
2020-04-30 07:30:00 +00:00
|
|
|
verify_access_permissions(info.access.permission)
|
|
|
|
.map_err(|err| format_err!("{} {} access: {}", method, path, err))?;
|
|
|
|
|
2019-11-22 05:43:13 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn verify_dirmap(
|
|
|
|
path: &str,
|
|
|
|
dirmap: SubdirMap,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
|
2021-01-19 09:27:59 +00:00
|
|
|
if !dirmap.is_empty() {
|
2019-11-22 05:43:13 +00:00
|
|
|
|
|
|
|
for i in 1..dirmap.len() {
|
|
|
|
|
|
|
|
if dirmap[i].0 <= dirmap[i-1].0 {
|
|
|
|
for (name, _) in dirmap.iter() {
|
|
|
|
eprintln!("{}/{}", path, name);
|
|
|
|
}
|
|
|
|
bail!("found unsorted dirmap at {:?} ({} <= {})", path, dirmap[i].0, dirmap[i-1].0);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (name, router) in dirmap.iter() {
|
|
|
|
let sub_path = format!("{}/{}", path, name);
|
|
|
|
verify_router(&sub_path, router)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn verify_router(path: &str, router: &Router) -> Result<(), Error> {
|
|
|
|
|
|
|
|
println!("Verify {}", path);
|
|
|
|
|
|
|
|
if let Some(api_method) = router.get {
|
|
|
|
verify_api_method("GET", path, api_method)?;
|
|
|
|
}
|
|
|
|
if let Some(api_method) = router.put {
|
|
|
|
verify_api_method("PUT", path, api_method)?;
|
|
|
|
}
|
|
|
|
if let Some(api_method) = router.post {
|
|
|
|
verify_api_method("POST", path, api_method)?;
|
|
|
|
}
|
|
|
|
if let Some(api_method) = router.delete {
|
|
|
|
verify_api_method("DELETE", path, api_method)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
match router.subroute {
|
|
|
|
Some(SubRoute::Map(dirmap)) => {
|
|
|
|
verify_dirmap(path, dirmap)?;
|
|
|
|
}
|
|
|
|
Some(SubRoute::MatchAll { router, param_name }) => {
|
|
|
|
let path = format!("{}/{{{}}}", path, param_name);
|
|
|
|
verify_router(&path, router)?;
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn verify_backup_api() -> Result<(), Error> {
|
|
|
|
|
|
|
|
let api = &api2::backup::BACKUP_API_ROUTER;
|
|
|
|
verify_router("backup-api", api)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn verify_reader_api() -> Result<(), Error> {
|
|
|
|
|
|
|
|
let api = &api2::reader::READER_API_ROUTER;
|
|
|
|
verify_router("reader-api", api)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn verify_root_api() -> Result<(), Error> {
|
|
|
|
|
|
|
|
let api = &api2::ROUTER;
|
|
|
|
verify_router("root", api)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|