Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 1 | package security |
| 2 | |
| 3 | import ( |
| 4 | "io/ioutil" |
| 5 | "os" |
| 6 | "runtime" |
| 7 | "testing" |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 8 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 9 | "veyron.io/veyron/veyron2/naming" |
| 10 | "veyron.io/veyron/veyron2/security" |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 11 | ) |
| 12 | |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 13 | // context implements security.Context. |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 14 | type context struct { |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 15 | localPrincipal security.Principal |
| 16 | localBlessings, remoteBlessings security.Blessings |
| 17 | method string |
| 18 | label security.Label |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 19 | } |
| 20 | |
Ankur | f044a8d | 2014-09-05 17:05:24 -0700 | [diff] [blame] | 21 | func (c *context) Method() string { return c.method } |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 22 | func (c *context) Name() string { return "" } |
| 23 | func (c *context) Suffix() string { return "" } |
Ankur | f044a8d | 2014-09-05 17:05:24 -0700 | [diff] [blame] | 24 | func (c *context) Label() security.Label { return c.label } |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 25 | func (c *context) Discharges() map[string]security.Discharge { return nil } |
| 26 | func (c *context) LocalID() security.PublicID { return nil } |
| 27 | func (c *context) RemoteID() security.PublicID { return nil } |
| 28 | func (c *context) LocalPrincipal() security.Principal { return c.localPrincipal } |
| 29 | func (c *context) LocalBlessings() security.Blessings { return c.localBlessings } |
| 30 | func (c *context) RemoteBlessings() security.Blessings { return c.remoteBlessings } |
Ankur | f044a8d | 2014-09-05 17:05:24 -0700 | [diff] [blame] | 31 | func (c *context) LocalEndpoint() naming.Endpoint { return nil } |
| 32 | func (c *context) RemoteEndpoint() naming.Endpoint { return nil } |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 33 | |
| 34 | func saveACLToTempFile(acl security.ACL) string { |
| 35 | f, err := ioutil.TempFile("", "saved_acl") |
| 36 | if err != nil { |
| 37 | panic(err) |
| 38 | } |
| 39 | defer f.Close() |
Tilak Sharma | d6ade0e | 2014-08-20 16:28:32 -0700 | [diff] [blame] | 40 | if err := SaveACL(f, acl); err != nil { |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 41 | defer os.Remove(f.Name()) |
| 42 | panic(err) |
| 43 | } |
| 44 | return f.Name() |
| 45 | } |
| 46 | |
| 47 | func updateACLInFile(fileName string, acl security.ACL) { |
| 48 | f, err := os.OpenFile(fileName, os.O_WRONLY, 0600) |
| 49 | if err != nil { |
| 50 | panic(err) |
| 51 | } |
| 52 | defer f.Close() |
Tilak Sharma | d6ade0e | 2014-08-20 16:28:32 -0700 | [diff] [blame] | 53 | if err := SaveACL(f, acl); err != nil { |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 54 | panic(err) |
| 55 | } |
| 56 | } |
| 57 | |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 58 | func testSelfRPCs(t *testing.T, authorizer security.Authorizer) { |
| 59 | _, file, line, _ := runtime.Caller(1) |
| 60 | var ( |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 61 | pserver, server = newPrincipal("server") |
| 62 | _, imposter = newPrincipal("server") |
| 63 | palice, alice = newPrincipal("alice") |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 64 | |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 65 | serverAlice = bless(pserver, palice, server, "alice") |
| 66 | aliceServer = bless(palice, pserver, alice, "server") |
| 67 | |
| 68 | ctx = &context{ |
| 69 | localPrincipal: pserver, |
| 70 | localBlessings: server, |
| 71 | } |
| 72 | |
| 73 | tests = []struct { |
| 74 | remote security.Blessings |
| 75 | isAuthorized bool |
| 76 | }{ |
| 77 | {server, true}, |
| 78 | {imposter, false}, |
| 79 | {serverAlice, false}, |
| 80 | // A principal talking to itself (even if with a different blessing) is authorized. |
| 81 | // TODO(ashankar,ataly): Is this a desired property? |
| 82 | {aliceServer, true}, |
| 83 | } |
| 84 | ) |
| 85 | for _, test := range tests { |
| 86 | ctx.remoteBlessings = test.remote |
| 87 | if got, want := authorizer.Authorize(ctx), test.isAuthorized; (got == nil) != want { |
| 88 | t.Errorf("%s:%d: %+v.Authorize(&context{local: %v, remote: %v}) returned error: %v, want error: %v", file, line, authorizer, ctx.localBlessings, ctx.remoteBlessings, got, !want) |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 89 | } |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | func testNothingPermitted(t *testing.T, authorizer security.Authorizer) { |
| 94 | _, file, line, _ := runtime.Caller(1) |
| 95 | var ( |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 96 | pserver, server = newPrincipal("server") |
| 97 | palice, alice = newPrincipal("alice") |
| 98 | pbob, bob = newPrincipal("random") |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 99 | |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 100 | serverAlice = bless(pserver, palice, server, "alice") |
| 101 | serverAliceFriend = bless(palice, pbob, serverAlice, "friend") |
| 102 | serverBob = bless(pserver, pbob, server, "bob") |
| 103 | |
| 104 | users = []security.Blessings{ |
| 105 | // blessings not recognized by "server" (since they are rooted at public |
| 106 | // keys not recognized as roots by pserver) |
| 107 | alice, |
| 108 | bob, |
| 109 | // blessings recognized by "server" (since they are its delegates) |
| 110 | serverAlice, |
| 111 | serverAliceFriend, |
| 112 | serverBob, |
| 113 | } |
| 114 | |
| 115 | invalidLabel = security.Label(3) |
| 116 | ) |
| 117 | // No principal (other than the server itself - self-RPCs are allowed) should have access to any |
| 118 | // valid or invalid label. |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 119 | for _, u := range users { |
| 120 | for _, l := range security.ValidLabels { |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 121 | ctx := &context{localPrincipal: pserver, localBlessings: server, remoteBlessings: u, label: l} |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 122 | if got := authorizer.Authorize(ctx); got == nil { |
| 123 | t.Errorf("%s:%d: %+v.Authorize(%v) returns nil, want error", file, line, authorizer, ctx) |
| 124 | } |
| 125 | } |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 126 | |
| 127 | ctx := &context{localPrincipal: pserver, localBlessings: server, remoteBlessings: u, label: invalidLabel} |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 128 | if got := authorizer.Authorize(ctx); got == nil { |
| 129 | t.Errorf("%s:%d: %+v.Authorize(%v) returns nil, want error", file, line, authorizer, ctx) |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | func TestACLAuthorizer(t *testing.T) { |
| 135 | const ( |
| 136 | // Shorthands |
| 137 | R = security.ReadLabel |
| 138 | W = security.WriteLabel |
| 139 | A = security.AdminLabel |
| 140 | D = security.DebugLabel |
| 141 | M = security.MonitoringLabel |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 142 | X = security.ResolveLabel |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 143 | ) |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 144 | type Expectations map[security.Blessings]security.LabelSet |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 145 | // Principals to test |
| 146 | var ( |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 147 | // Principals |
| 148 | pserver, server = newPrincipal("server") |
| 149 | palice, alice = newPrincipal("alice") |
| 150 | pbob, bob = newPrincipal("bob") |
| 151 | pche, che = newPrincipal("che") |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 152 | |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 153 | // Blessings from the server |
| 154 | serverAlice = bless(pserver, palice, server, "alice") |
| 155 | serverBob = bless(pserver, pbob, server, "bob") |
| 156 | serverChe = bless(pserver, pche, server, "che") |
| 157 | serverAliceFriend = bless(palice, pbob, serverAlice, "friend") |
| 158 | serverCheFriend = bless(pche, pbob, serverChe, "friend") |
| 159 | |
| 160 | authorizer security.Authorizer // the authorizer to test. |
| 161 | |
| 162 | runTests = func(expectations Expectations) { |
| 163 | _, file, line, _ := runtime.Caller(1) |
| 164 | for user, labels := range expectations { |
| 165 | for _, l := range security.ValidLabels { |
| 166 | ctx := &context{remoteBlessings: user, localBlessings: server, localPrincipal: pserver, label: l} |
| 167 | if got, want := authorizer.Authorize(ctx), labels.HasLabel(l); (got == nil) != want { |
| 168 | t.Errorf("%s:%d: %+v.Authorize(&context{remoteBlessings: %v, label: %v}) returned error: %v, want error: %v", file, line, authorizer, user, l, got, !want) |
| 169 | } |
| 170 | } |
| 171 | } |
| 172 | } |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 173 | ) |
| 174 | // Convenience function for combining Labels into a LabelSet. |
| 175 | LS := func(labels ...security.Label) security.LabelSet { |
| 176 | var ret security.LabelSet |
| 177 | for _, l := range labels { |
| 178 | ret = ret | security.LabelSet(l) |
| 179 | } |
| 180 | return ret |
| 181 | } |
| 182 | |
| 183 | // ACL for testing |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 184 | acl := security.ACL{ |
| 185 | In: map[security.BlessingPattern]security.LabelSet{ |
| 186 | "...": LS(R), |
| 187 | "server/alice/...": LS(W, R), |
| 188 | "server/alice": LS(A, D, M), |
| 189 | "server/bob": LS(D, M), |
| 190 | "server/che/...": LS(W, R), |
| 191 | "server/che": LS(W, R), |
| 192 | }, |
| 193 | NotIn: map[string]security.LabelSet{ |
| 194 | "server/che/friend": LS(W), |
| 195 | }, |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | // Authorizations for the above ACL. |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 199 | expectations := Expectations{ |
| 200 | alice: LS(R), // "..." ACL entry. |
| 201 | bob: LS(R), // "..." ACL entry. |
| 202 | che: LS(R), // "..." ACL entry. |
| 203 | server: security.AllLabels, // self RPC |
| 204 | serverAlice: LS(R, W, A, D, M), // "server/alice/..." ACL entry |
| 205 | serverBob: LS(R, D, M), // "..." and "server/bob" ACL entries |
| 206 | serverAliceFriend: LS(W, R), // "server/alice/..." ACL entry. |
| 207 | serverChe: LS(W, R), // "server/che" ACL entry. |
| 208 | serverCheFriend: LS(R), // "server/che/..." ACL entry, with the "server/che/friend" NotIn exception. |
| 209 | nil: LS(R), // No blessings presented, same authorizations as "..." ACL entry. |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 210 | } |
| 211 | // Create an aclAuthorizer based on the ACL and verify the authorizations. |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 212 | authorizer = NewACLAuthorizer(acl) |
| 213 | runTests(expectations) |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 214 | testSelfRPCs(t, authorizer) |
| 215 | |
| 216 | // Create a fileACLAuthorizer by saving the ACL in a file, and verify the |
| 217 | // authorizations. |
| 218 | fileName := saveACLToTempFile(acl) |
| 219 | defer os.Remove(fileName) |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 220 | authorizer = NewFileACLAuthorizer(fileName) |
| 221 | runTests(expectations) |
| 222 | testSelfRPCs(t, authorizer) |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 223 | |
| 224 | // Modify the ACL stored in the file and verify that the authorizations appropriately |
| 225 | // change for the fileACLAuthorizer. |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 226 | acl.In["server/bob"] = LS(R, W, A, D, M) |
| 227 | expectations[serverBob] = LS(R, W, A, D, M) |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 228 | updateACLInFile(fileName, acl) |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 229 | runTests(expectations) |
| 230 | testSelfRPCs(t, authorizer) |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 231 | |
| 232 | // Update the ACL file with invalid contents and verify that no requests are |
| 233 | // authorized. |
| 234 | f, err := os.OpenFile(fileName, os.O_WRONLY, 0600) |
| 235 | if err != nil { |
| 236 | panic(err) |
| 237 | } |
| 238 | f.Write([]byte("invalid ACL")) |
| 239 | f.Close() |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 240 | testNothingPermitted(t, authorizer) |
| 241 | } |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 242 | |
Asim Shankar | bf6263f | 2014-10-01 12:32:30 -0700 | [diff] [blame] | 243 | func TestFileACLAuthorizerOnNonExistentFile(t *testing.T) { |
| 244 | testNothingPermitted(t, NewFileACLAuthorizer("fileDoesNotExist")) |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 245 | } |
| 246 | |
| 247 | func TestNilACLAuthorizer(t *testing.T) { |
Tilak Sharma | b88a111 | 2014-08-15 17:17:12 -0700 | [diff] [blame] | 248 | authorizer := NewACLAuthorizer(nullACL) |
Tilak Sharma | 3ed3024 | 2014-08-11 11:45:55 -0700 | [diff] [blame] | 249 | testNothingPermitted(t, authorizer) |
| 250 | testSelfRPCs(t, authorizer) |
| 251 | } |