Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 1 | package acl |
| 2 | |
| 3 | import ( |
| 4 | "io/ioutil" |
| 5 | "reflect" |
| 6 | "testing" |
Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 7 | |
| 8 | vsecurity "veyron.io/veyron/veyron/security" |
| 9 | "veyron.io/veyron/veyron/security/acl/test" |
Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 10 | "veyron.io/veyron/veyron2/security" |
| 11 | ) |
| 12 | |
| 13 | // TestTaggedACLAuthorizer is both a test and a demonstration of the use of the |
| 14 | // TaggedACLAuthorizer and interaction with interface specification in VDL. |
| 15 | func TestTaggedACLAuthorizer(t *testing.T) { |
| 16 | type P []security.BlessingPattern |
| 17 | type S []string |
| 18 | // TaggedACLMap to test against. |
| 19 | acl := TaggedACLMap{ |
| 20 | "R": { |
| 21 | In: P{"..."}, |
| 22 | }, |
| 23 | "W": { |
| 24 | In: P{"ali/family/...", "bob/...", "che"}, |
| 25 | NotIn: S{"bob/acquaintances"}, |
| 26 | }, |
| 27 | "X": { |
| 28 | In: P{"ali/family/boss", "superman"}, |
| 29 | }, |
| 30 | } |
| 31 | type testcase struct { |
| 32 | Method string |
| 33 | Client security.Blessings |
| 34 | } |
| 35 | var ( |
| 36 | authorizer, _ = TaggedACLAuthorizer(acl, reflect.TypeOf(test.Read)) |
| 37 | // Two principals: The "server" and the "client" |
| 38 | pserver, _ = vsecurity.NewPrincipal() |
| 39 | pclient, _ = vsecurity.NewPrincipal() |
| 40 | server, _ = pserver.BlessSelf("server") |
| 41 | |
| 42 | // B generates the provided blessings for the client and ensures |
| 43 | // that the server will recognize them. |
| 44 | B = func(names ...string) security.Blessings { |
| 45 | var ret security.Blessings |
| 46 | for _, name := range names { |
| 47 | b, err := pclient.BlessSelf(name) |
| 48 | if err != nil { |
| 49 | t.Fatalf("%q: %v", name, err) |
| 50 | } |
| 51 | if err := pserver.AddToRoots(b); err != nil { |
| 52 | t.Fatalf("%q: %v", name, err) |
| 53 | } |
| 54 | if ret, err = security.UnionOfBlessings(ret, b); err != nil { |
| 55 | t.Fatal(err) |
| 56 | } |
| 57 | } |
| 58 | return ret |
| 59 | } |
| 60 | |
| 61 | run = func(test testcase) error { |
Asim Shankar | 252fe5f | 2014-11-06 00:59:29 -0800 | [diff] [blame] | 62 | ctx := security.NewContext(&security.ContextParams{ |
| 63 | LocalPrincipal: pserver, |
| 64 | LocalBlessings: server, |
| 65 | RemoteBlessings: test.Client, |
| 66 | Method: test.Method, |
| 67 | MethodTags: methodTags(test.Method), |
| 68 | }) |
Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 69 | return authorizer.Authorize(ctx) |
| 70 | } |
| 71 | ) |
| 72 | |
| 73 | // Test cases where access should be granted to methods with tags on |
| 74 | // them. |
| 75 | for _, test := range []testcase{ |
| 76 | {"Get", nil}, |
| 77 | {"Get", B("ali")}, |
| 78 | {"Get", B("bob/friend", "che/enemy")}, |
| 79 | |
| 80 | {"Put", B("ali")}, |
| 81 | {"Put", B("ali/family/mom")}, |
| 82 | {"Put", B("bob/friends")}, |
| 83 | {"Put", B("bob/acquantainces/carol", "che")}, // Access granted because of "che" |
| 84 | |
| 85 | {"Resolve", B("ali")}, |
| 86 | {"Resolve", B("ali/family/boss")}, |
| 87 | |
| 88 | {"AllTags", B("ali/family/boss")}, |
| 89 | } { |
| 90 | if err := run(test); err != nil { |
| 91 | t.Errorf("Access denied to method %q to %v: %v", test.Method, test.Client, err) |
| 92 | } |
| 93 | } |
| 94 | // Test cases where access should be denied. |
| 95 | for _, test := range []testcase{ |
| 96 | // Nobody is denied access to "Get" |
| 97 | {"Put", B("bob/acquaintances/dave", "che/friend", "dave")}, |
| 98 | {"Resolve", B("ali/family/friend")}, |
| 99 | // Since there are no tags on the NoTags method, it has an |
| 100 | // empty ACL. No client will have access. |
| 101 | {"NoTags", B("ali", "ali/family/boss", "bob")}, |
| 102 | // On a method with multiple tags on it, all must be satisfied. |
| 103 | {"AllTags", B("superman")}, // Only in the X ACL, not in R or W |
| 104 | {"AllTags", B("superman", "clark")}, // In X and in R, but not W |
| 105 | } { |
| 106 | if err := run(test); err == nil { |
| 107 | t.Errorf("Access to %q granted to %v", test.Method, test.Client) |
| 108 | } |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | func TestTaggedACLAuthorizerSelfRPCs(t *testing.T) { |
| 113 | var ( |
| 114 | // Client and server are the same principal, though have |
| 115 | // different blessings. |
| 116 | p, _ = vsecurity.NewPrincipal() |
| 117 | client, _ = p.BlessSelf("client") |
| 118 | server, _ = p.BlessSelf("server") |
| 119 | // Authorizer with a TaggedACLMap that grants read access to |
| 120 | // anyone, write/execute access to noone. |
| 121 | typ test.MyTag |
| 122 | authorizer, _ = TaggedACLAuthorizer(TaggedACLMap{"R": {In: []security.BlessingPattern{"nobody"}}}, reflect.TypeOf(typ)) |
| 123 | ) |
| 124 | for _, test := range []string{"Put", "Get", "Resolve", "NoTags", "AllTags"} { |
Asim Shankar | 252fe5f | 2014-11-06 00:59:29 -0800 | [diff] [blame] | 125 | ctx := security.NewContext(&security.ContextParams{ |
| 126 | LocalPrincipal: p, |
| 127 | LocalBlessings: server, |
| 128 | RemoteBlessings: client, |
| 129 | Method: test, |
| 130 | MethodTags: methodTags(test), |
| 131 | }) |
| 132 | if err := authorizer.Authorize(ctx); err != nil { |
Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 133 | t.Errorf("Got error %v for method %q", err, test) |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | func TestTaggedACLAuthorizerWithNilACL(t *testing.T) { |
| 139 | var ( |
| 140 | authorizer, _ = TaggedACLAuthorizer(nil, reflect.TypeOf(test.Read)) |
| 141 | pserver, _ = vsecurity.NewPrincipal() |
| 142 | pclient, _ = vsecurity.NewPrincipal() |
| 143 | server, _ = pserver.BlessSelf("server") |
| 144 | client, _ = pclient.BlessSelf("client") |
| 145 | ) |
| 146 | for _, test := range []string{"Put", "Get", "Resolve", "NoTags", "AllTags"} { |
Asim Shankar | 252fe5f | 2014-11-06 00:59:29 -0800 | [diff] [blame] | 147 | ctx := security.NewContext(&security.ContextParams{ |
| 148 | LocalPrincipal: pserver, |
| 149 | LocalBlessings: server, |
| 150 | RemoteBlessings: client, |
| 151 | Method: test, |
| 152 | MethodTags: methodTags(test), |
| 153 | }) |
| 154 | if err := authorizer.Authorize(ctx); err == nil { |
Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 155 | t.Errorf("nil TaggedACLMap authorized method %q", test) |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | func TestTaggedACLAuthorizerFromFile(t *testing.T) { |
| 161 | file, err := ioutil.TempFile("", "TestTaggedACLAuthorizerFromFile") |
| 162 | if err != nil { |
| 163 | t.Fatal(err) |
| 164 | } |
| 165 | filename := file.Name() |
| 166 | file.Close() |
| 167 | |
| 168 | var ( |
| 169 | authorizer, _ = TaggedACLAuthorizerFromFile(filename, reflect.TypeOf(test.Read)) |
| 170 | pserver, _ = vsecurity.NewPrincipal() |
| 171 | pclient, _ = vsecurity.NewPrincipal() |
| 172 | server, _ = pserver.BlessSelf("alice") |
| 173 | alicefriend, _ = pserver.Bless(pclient.PublicKey(), server, "friend/bob", security.UnconstrainedUse()) |
Asim Shankar | 252fe5f | 2014-11-06 00:59:29 -0800 | [diff] [blame] | 174 | ctx = security.NewContext(&security.ContextParams{ |
| 175 | LocalPrincipal: pserver, |
| 176 | LocalBlessings: server, |
| 177 | RemoteBlessings: alicefriend, |
| 178 | Method: "Get", |
| 179 | MethodTags: methodTags("Get"), |
| 180 | }) |
Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 181 | ) |
| 182 | // Make pserver recognize itself as an authority on "alice/..." blessings. |
| 183 | if err := pserver.AddToRoots(server); err != nil { |
| 184 | t.Fatal(err) |
| 185 | } |
| 186 | // "alice/friend/bob" should not have access to test.Read methods like Get. |
| 187 | if err := authorizer.Authorize(ctx); err == nil { |
Asim Shankar | 252fe5f | 2014-11-06 00:59:29 -0800 | [diff] [blame] | 188 | t.Fatalf("Expected authorization error as %v is not on the ACL for Read operations", ctx.RemoteBlessings()) |
Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 189 | } |
| 190 | // Rewrite the file giving access |
| 191 | if err := ioutil.WriteFile(filename, []byte(`{"R": { "In":["alice/friend/..."] }}`), 0600); err != nil { |
| 192 | t.Fatal(err) |
| 193 | } |
| 194 | // Now should have access |
| 195 | if err := authorizer.Authorize(ctx); err != nil { |
| 196 | t.Fatal(err) |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | func TestTagTypeMustBeString(t *testing.T) { |
| 201 | type I int |
| 202 | if auth, err := TaggedACLAuthorizer(TaggedACLMap{}, reflect.TypeOf(I(0))); err == nil || auth != nil { |
| 203 | t.Errorf("Got (%v, %v), wanted error since tag type is not a string", auth, err) |
| 204 | } |
| 205 | if auth, err := TaggedACLAuthorizerFromFile("does_not_matter", reflect.TypeOf(I(0))); err == nil || auth != nil { |
| 206 | t.Errorf("Got (%v, %v), wanted error since tag type is not a string", auth, err) |
| 207 | } |
| 208 | } |
| 209 | |
Asim Shankar | 252fe5f | 2014-11-06 00:59:29 -0800 | [diff] [blame] | 210 | func methodTags(method string) []interface{} { |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 211 | server := test.MyObjectServer(nil) |
Asim Shankar | 252fe5f | 2014-11-06 00:59:29 -0800 | [diff] [blame] | 212 | tags, _ := server.GetMethodTags(nil, method) |
Asim Shankar | da68285 | 2014-11-06 00:38:43 -0800 | [diff] [blame] | 213 | return tags |
| 214 | } |