@@ -2726,3 +2726,228 @@ mod schema_based_parsing_tests {
2726
2726
) ;
2727
2727
}
2728
2728
}
2729
+
2730
+ mod prop_test_policy_set_operations {
2731
+ use super :: * ;
2732
+ use proptest:: prelude:: * ;
2733
+ use std:: collections:: hash_map:: Entry ;
2734
+
2735
+ /// Production PolicySet along with simplified model of policy set
2736
+ /// for Proptesting
2737
+ struct PolicySetModel {
2738
+ //The production PolicySet implementation
2739
+ policy_set : PolicySet ,
2740
+
2741
+ //Used to generate unique names
2742
+ max_name_int : i32 ,
2743
+
2744
+ //We model the PolicySet state machine in terms of the below:
2745
+ //Every name is unique
2746
+ static_policy_names : Vec < String > ,
2747
+ template_names : Vec < String > ,
2748
+ link_names : Vec < String > ,
2749
+
2750
+ //Every existent template has a (possibly empty) vector of the links to that template
2751
+ template_to_link_map : HashMap < String , Vec < String > > ,
2752
+
2753
+ //Every link points to its template
2754
+ link_to_template_map : HashMap < String , String > ,
2755
+ }
2756
+
2757
+ /// Model of a PolicySet where ops that shouldn't be allowed have no effect
2758
+ /// e.g., remove_static with no static policies does nothing
2759
+ impl PolicySetModel {
2760
+ fn new ( ) -> Self {
2761
+ Self {
2762
+ policy_set : PolicySet :: new ( ) ,
2763
+ static_policy_names : Vec :: new ( ) ,
2764
+ template_names : Vec :: new ( ) ,
2765
+ link_names : Vec :: new ( ) ,
2766
+ template_to_link_map : HashMap :: new ( ) ,
2767
+ link_to_template_map : HashMap :: new ( ) ,
2768
+ max_name_int : 0 ,
2769
+ }
2770
+ }
2771
+
2772
+ fn get_next_name ( & mut self ) -> String {
2773
+ let i = self . max_name_int ;
2774
+ self . max_name_int += 1 ;
2775
+ format ! ( "policy{i}" )
2776
+ }
2777
+
2778
+ //add_static never fails and bumps `max_name_int`
2779
+ fn add_static ( & mut self ) {
2780
+ let policy_name = self . get_next_name ( ) ;
2781
+ self . static_policy_names . push ( policy_name. clone ( ) ) ;
2782
+ let policy_str = "permit(principal, action, resource);" ;
2783
+ let p = Policy :: parse ( Some ( policy_name) , policy_str) . unwrap ( ) ;
2784
+ self . policy_set
2785
+ . add ( p)
2786
+ . expect ( "Should be able to add policy with unique name" ) ;
2787
+ }
2788
+
2789
+ //add_static never fails and bumps `max_name_int`
2790
+ fn add_template ( & mut self ) {
2791
+ let template_name = self . get_next_name ( ) ;
2792
+ self . template_names . push ( template_name. clone ( ) ) ;
2793
+ let template_str = "permit(principal == ?principal, action, resource);" ;
2794
+ let template = Template :: parse ( Some ( template_name. clone ( ) ) , template_str) . unwrap ( ) ;
2795
+ self . policy_set
2796
+ . add_template ( template)
2797
+ . expect ( "Should be able to add template with unique name" ) ;
2798
+ self . template_to_link_map . insert ( template_name, Vec :: new ( ) ) ;
2799
+ }
2800
+
2801
+ fn link ( & mut self ) {
2802
+ if self . template_names . len ( ) > 0 {
2803
+ let policy_name = self . get_next_name ( ) ;
2804
+ self . link_names . push ( policy_name. clone ( ) ) ;
2805
+ let euid = EntityUid :: from_strs ( "User" , "alice" ) ;
2806
+ let template_name = self . template_names [ 0 ] . clone ( ) ; //TODO: randomize
2807
+ let vals = HashMap :: from ( [ ( SlotId :: principal ( ) , euid) ] ) ;
2808
+ self . policy_set
2809
+ . link (
2810
+ PolicyId :: from_str ( & template_name) . unwrap ( ) ,
2811
+ PolicyId :: from_str ( & policy_name) . unwrap ( ) ,
2812
+ vals,
2813
+ )
2814
+ . expect ( "linking should succeed with existent template and unique link name" ) ;
2815
+ match self . template_to_link_map . entry ( template_name. clone ( ) ) {
2816
+ Entry :: Occupied ( v) => v. into_mut ( ) . push ( policy_name. clone ( ) ) ,
2817
+ Entry :: Vacant ( _) => {
2818
+ panic ! ( "template to link map should have Vec for existing template" )
2819
+ }
2820
+ } ;
2821
+ assert ! ( self . link_to_template_map. get( & policy_name) . is_none( ) ) ;
2822
+ self . link_to_template_map . insert ( policy_name, template_name) ;
2823
+ }
2824
+ }
2825
+
2826
+ fn remove_static ( & mut self ) {
2827
+ //TODO: randomize
2828
+ if let Some ( policy_id) = self . static_policy_names . last ( ) {
2829
+ //Remove from PolicySet and `static_policy_names`
2830
+ self . policy_set
2831
+ . remove_static ( PolicyId :: from_str ( & policy_id) . unwrap ( ) )
2832
+ . expect ( "Should be able to remove static policy that exists" ) ;
2833
+ self . static_policy_names . pop ( ) ;
2834
+ }
2835
+ }
2836
+
2837
+ fn get_name_template_with_no_links ( & mut self ) -> Option < String > {
2838
+ //TODO: randomize
2839
+ for ( template_name, links) in self . template_to_link_map . iter ( ) {
2840
+ if links. len ( ) == 0 {
2841
+ return Some ( template_name. clone ( ) ) ;
2842
+ }
2843
+ }
2844
+ None
2845
+ }
2846
+
2847
+ fn remove_template ( & mut self ) {
2848
+ if let Some ( template_name) = self . get_name_template_with_no_links ( ) {
2849
+ //Assert no link exists
2850
+ assert ! ( !self
2851
+ . link_to_template_map
2852
+ . iter( )
2853
+ . any( |( _, v) | v == & template_name) ) ;
2854
+
2855
+ //Remove from `template_to_link_map`, `template_names` and the PolicySet
2856
+ self . template_to_link_map
2857
+ . remove ( & template_name)
2858
+ . expect ( "Template should exist" ) ;
2859
+ self . policy_set
2860
+ . remove_template ( PolicyId :: from_str ( & template_name) . unwrap ( ) )
2861
+ . expect ( "Template can be removed" ) ;
2862
+ let idx = self
2863
+ . template_names
2864
+ . iter ( )
2865
+ . position ( |r| r == & template_name)
2866
+ . expect ( "Should find template_name" ) ;
2867
+ self . template_names . remove ( idx) ;
2868
+ }
2869
+ }
2870
+
2871
+ fn unlink ( & mut self ) {
2872
+ //TODO: randomize
2873
+ if let Some ( policy_id) = self . link_names . last ( ) {
2874
+ //Remove from PolicySet, `link_to_template_map` and `template_to_link_map`
2875
+ self . policy_set
2876
+ . unlink ( PolicyId :: from_str ( & policy_id) . unwrap ( ) )
2877
+ . expect ( "Should be able to remove link that exists" ) ;
2878
+ let template_name = self
2879
+ . link_to_template_map
2880
+ . get ( policy_id)
2881
+ . expect ( "Template should exist" )
2882
+ . clone ( ) ;
2883
+ self . link_to_template_map
2884
+ . remove ( policy_id)
2885
+ . expect ( "Template should exist" ) ;
2886
+ match self . template_to_link_map . entry ( template_name. clone ( ) ) {
2887
+ Entry :: Occupied ( e) => {
2888
+ let v = e. into_mut ( ) ;
2889
+ let idx = v
2890
+ . iter ( )
2891
+ . position ( |r| r == policy_id)
2892
+ . expect ( "Should find index for link" ) ;
2893
+ v. remove ( idx) ;
2894
+ }
2895
+ Entry :: Vacant ( _) => {
2896
+ panic ! ( "template to link map should have Vec for existing template" )
2897
+ }
2898
+ } ;
2899
+ //Remove from `link_names`
2900
+ self . link_names . pop ( ) ;
2901
+ }
2902
+ }
2903
+ }
2904
+
2905
+ fn string_to_policy_set_ops ( s : & str ) {
2906
+ let mut my_policy_set = PolicySetModel :: new ( ) ;
2907
+ enum PolicySetOp {
2908
+ Add ,
2909
+ RemoveStatic ,
2910
+ AddTemplate ,
2911
+ RemoveTemplate ,
2912
+ Link ,
2913
+ Unlink ,
2914
+ }
2915
+ use PolicySetOp :: * ;
2916
+ let n_to_op_map: HashMap < u32 , PolicySetOp > = HashMap :: from ( [
2917
+ ( 0 , Add ) ,
2918
+ ( 1 , RemoveStatic ) ,
2919
+ ( 2 , AddTemplate ) ,
2920
+ ( 3 , RemoveTemplate ) ,
2921
+ ( 4 , Link ) ,
2922
+ ( 5 , Unlink ) ,
2923
+ ] ) ;
2924
+
2925
+ for c in s. chars ( ) {
2926
+ let n = c. to_digit ( 10 ) ;
2927
+ match n {
2928
+ Some ( n) => {
2929
+ if n > 5 {
2930
+ panic ! ( "Testing harness sending numbers greater than 5" ) ;
2931
+ }
2932
+ let op = n_to_op_map. get ( & n) . unwrap ( ) ;
2933
+ match op {
2934
+ Add => my_policy_set. add_static ( ) ,
2935
+ RemoveStatic => my_policy_set. remove_static ( ) ,
2936
+ AddTemplate => my_policy_set. add_template ( ) ,
2937
+ RemoveTemplate => my_policy_set. remove_template ( ) ,
2938
+ Link => my_policy_set. link ( ) ,
2939
+ Unlink => my_policy_set. unlink ( ) ,
2940
+ } ;
2941
+ }
2942
+ None => continue ,
2943
+ }
2944
+ }
2945
+ }
2946
+
2947
+ proptest ! {
2948
+ #[ test]
2949
+ fn doesnt_crash( s in "[0-5]{32}" ) {
2950
+ string_to_policy_set_ops( & s) ;
2951
+ }
2952
+ }
2953
+ }
0 commit comments