2020-12-18 12:26:08 +01:00
use std ::collections ::HashSet ;
2020-04-17 14:11:25 +02:00
use anyhow ::{ bail , format_err , Error } ;
2019-11-22 06:43:13 +01:00
2022-04-10 17:49:26 +02:00
use proxmox_router ::{ ApiMethod , Permission , Router , SubRoute , SubdirMap } ;
2021-10-08 11:19:37 +02:00
use proxmox_schema ::* ;
2019-11-22 06:43:13 +01:00
use proxmox_backup ::api2 ;
// 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 10:27:59 +01:00
if ! map . is_empty ( ) {
2019-11-22 06:43:13 +01:00
for i in 1 .. map . len ( ) {
2022-04-10 17:49:26 +02:00
if map [ i ] . 0 < = map [ i - 1 ] . 0 {
2019-11-22 06:43:13 +01:00
for ( name , _ , _ ) in map . iter ( ) {
eprintln! ( " {} " , name ) ;
}
2022-04-10 17:49:26 +02:00
bail! (
" found unsorted property map ({} <= {}) " ,
map [ i ] . 0 ,
map [ i - 1 ] . 0
) ;
2019-11-22 06:43:13 +01:00
}
}
}
for ( _name , _ , sub_schema ) in map . iter ( ) {
verify_schema ( sub_schema ) ? ;
}
Ok ( ( ) )
}
2020-12-18 12:26:08 +01: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 13:44:17 +01:00
Schema ::AllOf ( allof ) = > verify_all_of_schema ( allof ) ? ,
2024-02-02 15:07:38 +01:00
Schema ::OneOf ( oneof ) = > verify_one_of_schema ( oneof ) ? ,
2020-12-18 12:26:08 +01: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 12:21:33 +01:00
if ! keys . insert ( property . 0 ) {
if ! dupes . is_empty ( ) {
2020-12-18 12:26:08 +01:00
dupes . push_str ( " , " ) ;
}
dupes . push_str ( property . 0 ) ;
}
}
if ! dupes . is_empty ( ) {
bail! ( " Duplicate keys found in AllOf schema: {} " , dupes ) ;
}
Ok ( ( ) )
}
2024-02-02 15:07:38 +01:00
fn verify_one_of_schema ( schema : & OneOfSchema ) -> Result < ( ) , Error > {
for ( _name , entry ) in schema . list {
match entry {
Schema ::Object ( obj ) = > verify_object_schema ( obj ) ? ,
Schema ::AllOf ( allof ) = > verify_all_of_schema ( allof ) ? ,
Schema ::OneOf ( oneof ) = > verify_one_of_schema ( oneof ) ? ,
_ = > bail! ( " OneOf schema with a non-object schema entry! " ) ,
}
}
Ok ( ( ) )
}
2019-11-22 06:43:13 +01:00
fn verify_schema ( schema : & Schema ) -> Result < ( ) , Error > {
match schema {
Schema ::Object ( obj_schema ) = > {
verify_object_schema ( obj_schema ) ? ;
}
2020-12-18 12:26:08 +01:00
Schema ::AllOf ( all_of_schema ) = > {
verify_all_of_schema ( all_of_schema ) ? ;
}
2024-02-02 15:07:38 +01:00
Schema ::OneOf ( one_of_schema ) = > {
verify_one_of_schema ( one_of_schema ) ? ;
}
2019-11-22 06:43:13 +01:00
Schema ::Array ( arr_schema ) = > {
verify_schema ( arr_schema . items ) ? ;
}
_ = > { }
}
Ok ( ( ) )
}
2020-04-30 09:30:00 +02:00
fn verify_access_permissions ( permission : & Permission ) -> Result < ( ) , Error > {
match permission {
Permission ::Or ( list ) = > {
2022-04-10 17:49:26 +02:00
for perm in list . iter ( ) {
verify_access_permissions ( perm ) ? ;
}
2020-04-30 09:30:00 +02:00
}
Permission ::And ( list ) = > {
2022-04-10 17:49:26 +02:00
for perm in list . iter ( ) {
verify_access_permissions ( perm ) ? ;
}
2020-04-30 09:30:00 +02:00
}
2022-04-10 17:49:26 +02:00
Permission ::Privilege ( path_comp , .. ) = > {
2020-04-30 09:30:00 +02:00
let path = format! ( " / {} " , path_comp . join ( " / " ) ) ;
2021-09-09 10:32:44 +02:00
pbs_config ::acl ::check_acl_path ( & path ) ? ;
2020-04-30 09:30:00 +02:00
}
_ = > { }
}
Ok ( ( ) )
}
2022-04-10 17:49:26 +02:00
fn verify_api_method ( method : & str , path : & str , info : & ApiMethod ) -> Result < ( ) , Error > {
2020-12-18 12:26:08 +01: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 ) ) ? ;
}
2024-02-02 15:07:38 +01:00
ParameterSchema ::OneOf ( one_of ) = > {
verify_one_of_schema ( one_of )
. map_err ( | err | format_err! ( " {} {} parameters: {} " , method , path , err ) ) ? ;
}
2020-12-18 12:26:08 +01:00
}
2019-11-22 06:43:13 +01:00
2020-12-18 12:26:08 +01:00
verify_schema ( info . returns . schema )
2019-11-22 06:43:13 +01:00
. map_err ( | err | format_err! ( " {} {} returns: {} " , method , path , err ) ) ? ;
2020-04-30 09:30:00 +02:00
verify_access_permissions ( info . access . permission )
. map_err ( | err | format_err! ( " {} {} access: {} " , method , path , err ) ) ? ;
2019-11-22 06:43:13 +01:00
Ok ( ( ) )
}
2022-04-10 17:49:26 +02:00
fn verify_dirmap ( path : & str , dirmap : SubdirMap ) -> Result < ( ) , Error > {
2021-01-19 10:27:59 +01:00
if ! dirmap . is_empty ( ) {
2019-11-22 06:43:13 +01:00
for i in 1 .. dirmap . len ( ) {
2022-04-10 17:49:26 +02:00
if dirmap [ i ] . 0 < = dirmap [ i - 1 ] . 0 {
2019-11-22 06:43:13 +01:00
for ( name , _ ) in dirmap . iter ( ) {
eprintln! ( " {} / {} " , path , name ) ;
}
2022-04-10 17:49:26 +02:00
bail! (
" found unsorted dirmap at {:?} ({} <= {}) " ,
path ,
dirmap [ i ] . 0 ,
dirmap [ i - 1 ] . 0
) ;
2019-11-22 06:43:13 +01:00
}
}
}
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 ( ( ) )
}