github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/remote/access/access_test.go (about) 1 package access 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "testing" 7 8 "github.com/qri-io/qri/dsref" 9 "github.com/qri-io/qri/profile" 10 ) 11 12 func ExamplePolicy() { 13 14 const examplePolicy = ` 15 [ 16 { 17 "title": "pull any dataset", 18 "effect": "allow", 19 "subject": "*", 20 "resources": [ 21 "dataset:*" 22 ], 23 "actions": [ 24 "remote:pull" 25 ] 26 }, 27 { 28 "title": "push and delete user-owned datasets", 29 "effect": "allow", 30 "subject": "*", 31 "resources": [ 32 "dataset:_subject:*" 33 ], 34 "actions": [ 35 "remote:push", 36 "remote:remove" 37 ] 38 } 39 ] 40 ` 41 42 p := &Policy{} 43 if err := json.Unmarshal([]byte(examplePolicy), p); err != nil { 44 panic(err) 45 } 46 47 bob := &profile.Profile{ 48 ID: profile.IDB58DecodeOrEmpty("QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt"), 49 Peername: "bob", 50 } 51 52 if err := p.Enforce(bob, "dataset:someone_else:world_bank_population", "remote:pull"); err == nil { 53 fmt.Println("bob can pull someone_else/world_bank_population") 54 } 55 if err := p.Enforce(bob, "dataset:bob:bobs_dataset", "remote:remove"); err == nil { 56 fmt.Println("bob can remote-delete his own dataset") 57 } 58 if err := p.Enforce(bob, "dataset:someone_else:dataset", "remote:remove"); err == ErrAccessDenied { 59 fmt.Println("bob can't remote-delete someone else's dataset") 60 } 61 62 // Output: 63 // bob can pull someone_else/world_bank_population 64 // bob can remote-delete his own dataset 65 // bob can't remote-delete someone else's dataset 66 } 67 68 func TestEnforce(t *testing.T) { 69 pro := &profile.Profile{ 70 ID: profile.IDB58DecodeOrEmpty("QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt"), 71 Peername: "bob", 72 } 73 74 p := Policy{ 75 { 76 Subject: "QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt", 77 Resources: Resources{MustParseResource("dataset:foo:bar")}, 78 Actions: Actions{MustParseAction("*")}, 79 Effect: EffectAllow, 80 }, 81 } 82 83 if err := p.Enforce(pro, "dataset:foo:bar", "read"); err != nil { 84 t.Error(err) 85 } 86 } 87 88 func TestPolicyJSON(t *testing.T) { 89 bad := [][2]string{ 90 {"rule.Subject is required", `[{}]`}, 91 {"rule.Resources field is required", `[{ 92 "subject": "*", 93 "resources": [], 94 "actions": ["*"], 95 "effect": "deny" 96 }]`}, 97 {`rule.Effect must be one of ("allow"|"deny")`, `[{ 98 "subject": "*", 99 "resources": ["*"], 100 "actions": ["*"], 101 "effect": "evaporate" 102 }]`}, 103 {`rule.Actions field is required`, `[{ 104 "subject": "*", 105 "resources": ["*"], 106 "action": ["*"], 107 "effect": "allow" 108 }]`}, 109 } 110 111 for _, c := range bad { 112 t.Run(c[0], func(t *testing.T) { 113 pol := &Policy{} 114 115 err := json.Unmarshal([]byte(c[1]), pol) 116 if err == nil { 117 t.Fatal("expected bad policy to fail. received no error") 118 } 119 120 if err.Error() != c[0] { 121 t.Errorf("error message mismatch. want: %q\ngot: %q", c[1], err.Error()) 122 } 123 }) 124 } 125 } 126 127 func TestParseResource(t *testing.T) { 128 bad := []string{ 129 "", 130 "*:foo", 131 } 132 133 for _, str := range bad { 134 if _, err := ParseResource(str); err == nil { 135 t.Errorf("expected error parsing bad resource. got nil.") 136 } 137 } 138 } 139 140 func TestResourceContains(t *testing.T) { 141 cases := []struct { 142 a, b string 143 expect bool 144 }{ 145 {"*", "apples", true}, 146 {"candy:*", "apples", false}, 147 {"candy:apples", "candy:apples", true}, 148 {"candy:apples", "candy:applez", false}, 149 {"dataset:foo:bar", "dataset", false}, 150 {"dataset:foo:bar", "dataset:foo:bar:baz", false}, 151 {"dataset:foo:*", "dataset:foo:bar:baz", true}, 152 {"dataset:foo:bar:*", "dataset:foo:bar:baz", true}, 153 154 {"dataset:*", "dataset:someone_else:world_bank_population", true}, 155 156 {"dataset:_subject:bar:*", "dataset:user:bar:baz", true}, 157 {"dataset:_subject:bar:*", "dataset:other_user:bar:baz", false}, 158 } 159 160 for _, c := range cases { 161 t.Run(fmt.Sprintf("%s_contains_%s_is_%t", c.a, c.b, c.expect), func(t *testing.T) { 162 rscA, err := ParseResource(c.a) 163 if err != nil { 164 t.Fatal(err) 165 } 166 167 rscB, err := ParseResource(c.b) 168 if err != nil { 169 t.Fatal(err) 170 } 171 172 got := rscA.Contains(rscB, "user") 173 if c.expect != got { 174 t.Errorf("result mismatch expected %q.Contains(%q) == %t", c.a, c.b, c.expect) 175 } 176 177 }) 178 } 179 } 180 181 func TestActionContains(t *testing.T) { 182 cases := []struct { 183 a, b string 184 expect bool 185 }{ 186 {"*", "apples", true}, 187 {"candy:*", "apples", false}, 188 {"candy:apples", "candy:apples", true}, 189 {"candy:apples", "candy:applez", false}, 190 {"dataset:foo:bar", "dataset", false}, 191 {"dataset:foo:bar", "dataset:foo:bar:baz", false}, 192 {"dataset:foo:*", "dataset:foo:bar:baz", true}, 193 {"dataset:foo:bar:*", "dataset:foo:bar:baz", true}, 194 195 {"remote:pull", "remote:pull", true}, 196 } 197 198 for _, c := range cases { 199 t.Run(fmt.Sprintf("%s_contains_%s_is_%t", c.a, c.b, c.expect), func(t *testing.T) { 200 actA := MustParseAction(c.a) 201 actB := MustParseAction(c.b) 202 203 got := actA.Contains(actB) 204 if c.expect != got { 205 t.Errorf("result mismatch expected %q.Contains(%q) == %t", c.a, c.b, c.expect) 206 } 207 }) 208 } 209 } 210 211 func TestResourceStrFromRef(t *testing.T) { 212 cases := []struct { 213 ref dsref.Ref 214 expect string 215 }{ 216 {dsref.Ref{Username: "foo", Name: "bar"}, "dataset:foo:bar"}, 217 } 218 219 for _, c := range cases { 220 t.Run(fmt.Sprintf("ref_%s_becomes_resource_%s", c.ref, c.expect), func(t *testing.T) { 221 222 got := ResourceStrFromRef(c.ref) 223 if c.expect != got { 224 t.Errorf("result mismatch expected ResourceStrFromRef(%s) == %s", c.ref, c.expect) 225 } 226 }) 227 } 228 }