blob: 4f1a5581307ac8ed4987aae84b1d301e25ed86bd [file] [log] [blame]
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -08001package impl_test
2
3import (
4 "bytes"
5 "fmt"
6 "io"
7 "os"
8 "reflect"
9 "syscall"
10 "testing"
11
12 "v.io/core/veyron2"
13 "v.io/core/veyron2/context"
14 "v.io/core/veyron2/naming"
15 "v.io/core/veyron2/rt"
16 "v.io/core/veyron2/security"
17 "v.io/core/veyron2/services/mgmt/application"
18 "v.io/core/veyron2/services/security/access"
19 "v.io/core/veyron2/vdl/vdlutil"
20 "v.io/core/veyron2/verror"
21 "v.io/core/veyron2/vlog"
22
23 "v.io/core/veyron/lib/modules"
24 "v.io/core/veyron/lib/signals"
25 "v.io/core/veyron/lib/testutil"
26 tsecurity "v.io/core/veyron/lib/testutil/security"
27 "v.io/core/veyron/services/mgmt/application/impl"
28 mgmttest "v.io/core/veyron/services/mgmt/lib/testutil"
29 "v.io/core/veyron/services/mgmt/repository"
30)
31
32const (
33 repoCmd = "repository"
34)
35
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080036var globalCtx *context.T
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -080037var globalCancel context.CancelFunc
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080038
39// This is also a modules world.
40// Insert necessary code here to be a modules test.
41func init() {
42 // TODO(rjkroege): Remove when vom2 is ready.
43 vdlutil.Register(&naming.VDLMountedServer{})
44
45 modules.RegisterChild(repoCmd, "", appRepository)
46 testutil.Init()
47
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -080048 globalRT, err := rt.New()
49 if err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080050 panic(err)
51 }
52 globalCtx = globalRT.NewContext()
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -080053 globalCancel = globalRT.Cleanup
Matt Rosencrantzd599e382015-01-12 11:13:32 -080054 veyron2.GetNamespace(globalCtx).CacheCtl(naming.DisableCache(true))
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080055}
56
57// TestHelperProcess is the entrypoint for the modules commands in a
58// a test subprocess.
59func TestHelperProcess(t *testing.T) {
60 modules.DispatchInTest()
61}
62
63func appRepository(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
64 args = args[1:]
65 if len(args) < 2 {
66 vlog.Fatalf("repository expected at least name and store arguments and optionally ACL flags per TaggedACLMapFromFlag")
67 }
68 publishName := args[0]
69 storedir := args[1]
70
71 defer fmt.Fprintf(stdout, "%v terminating\n", publishName)
72 defer vlog.VI(1).Infof("%v terminating", publishName)
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -080073 defer globalCancel()
Matt Rosencrantz6edab562015-01-12 11:07:55 -080074 server, endpoint := mgmttest.NewServer(globalCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080075 defer server.Stop()
76
77 name := naming.JoinAddressName(endpoint, "")
78 vlog.VI(1).Infof("applicationd name: %v", name)
79
80 dispatcher, err := impl.NewDispatcher(storedir)
81 if err != nil {
82 vlog.Fatalf("Failed to create repository dispatcher: %v", err)
83 }
84 if err := server.ServeDispatcher(publishName, dispatcher); err != nil {
85 vlog.Fatalf("Serve(%v) failed: %v", publishName, err)
86 }
87
88 fmt.Fprintf(stdout, "ready:%d\n", os.Getpid())
89 <-signals.ShutdownOnSignals(globalCtx)
90
91 return nil
92}
93
94func TestApplicationUpdateACL(t *testing.T) {
Matt Rosencrantz6edab562015-01-12 11:07:55 -080095 sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -080096 defer deferFn()
97
98 // setup mock up directory to put state in
99 storedir, cleanup := mgmttest.SetupRootDir(t, "application")
100 defer cleanup()
101
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800102 otherCtx, otherCancel := mgmttest.NewRuntime(t, globalCtx)
103 defer otherCancel()
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800104
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800105 idp := tsecurity.NewIDProvider("root")
106
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800107 // By default, globalRT and otherRT will have blessings generated based on the
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800108 // username/machine name running this process. Since these blessings will appear
109 // in ACLs, give them recognizable names.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800110 if err := idp.Bless(veyron2.GetPrincipal(globalCtx), "self"); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800111 t.Fatal(err)
112 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800113 if err := idp.Bless(veyron2.GetPrincipal(otherCtx), "other"); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800114 t.Fatal(err)
115 }
116
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800117 crDir, crEnv := mgmttest.CredentialsForChild(globalCtx, "repo")
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800118 defer os.RemoveAll(crDir)
119
120 // Make server credentials derived from the test harness.
121 _, nms := mgmttest.RunShellCommand(t, sh, crEnv, repoCmd, "repo", storedir)
122 pid := mgmttest.ReadPID(t, nms)
123 defer syscall.Kill(pid, syscall.SIGINT)
124
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800125 v1stub := repository.ApplicationClient("repo/search/v1")
126 repostub := repository.ApplicationClient("repo")
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800127
128 // Create example envelopes.
129 envelopeV1 := application.Envelope{
130 Args: []string{"--help"},
131 Env: []string{"DEBUG=1"},
132 Binary: "/veyron/name/of/binary",
133 }
134
135 // Envelope putting as other should fail.
136 // TODO(rjkroege): Validate that it is failed with permission denied.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800137 if err := v1stub.Put(otherCtx, []string{"base"}, envelopeV1); err == nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800138 t.Fatalf("Put() wrongly didn't fail")
139 }
140
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800141 // Envelope putting as global should succeed.
142 if err := v1stub.Put(globalCtx, []string{"base"}, envelopeV1); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800143 t.Fatalf("Put() failed: %v", err)
144 }
145
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800146 acl, etag, err := repostub.GetACL(globalCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800147 if !verror.Is(err, impl.ErrNotFound.ID) {
148 t.Fatalf("GetACL should have failed with ErrNotFound but was: %v", err)
149 }
150 if etag != "default" {
151 t.Fatalf("getACL expected:default, got:%v(%v)", etag, acl)
152 }
153 if acl != nil {
154 t.Fatalf("GetACL got %v, expected %v", acl, nil)
155 }
156
157 vlog.VI(2).Infof("self attempting to give other permission to update application")
158 newACL := make(access.TaggedACLMap)
159 for _, tag := range access.AllTypicalTags() {
160 newACL.Add("root/self", string(tag))
161 newACL.Add("root/other", string(tag))
162 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800163 if err := repostub.SetACL(globalCtx, newACL, ""); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800164 t.Fatalf("SetACL failed: %v", err)
165 }
166
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800167 acl, etag, err = repostub.GetACL(globalCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800168 if err != nil {
169 t.Fatalf("GetACL should not have failed: %v", err)
170 }
171 expected := newACL
172 if got := acl; !reflect.DeepEqual(expected.Normalize(), got.Normalize()) {
173 t.Errorf("got %#v, exected %#v ", got, expected)
174 }
175
176 // Envelope putting as other should now succeed.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800177 if err := v1stub.Put(otherCtx, []string{"base"}, envelopeV1); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800178 t.Fatalf("Put() wrongly failed: %v", err)
179 }
180
181 // Other takes control.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800182 acl, etag, err = repostub.GetACL(otherCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800183 if err != nil {
184 t.Fatalf("GetACL 2 should not have failed: %v", err)
185 }
186 acl["Admin"] = access.ACL{
187 In: []security.BlessingPattern{"root/other"},
188 NotIn: []string{}}
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800189 if err = repostub.SetACL(otherCtx, acl, etag); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800190 t.Fatalf("SetACL failed: %v", err)
191 }
192
193 // Self is now locked out but other isn't.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800194 if _, _, err = repostub.GetACL(globalCtx); err == nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800195 t.Fatalf("GetACL should not have succeeded")
196 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800197 acl, _, err = repostub.GetACL(otherCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800198 if err != nil {
199 t.Fatalf("GetACL should not have failed: %v", err)
200 }
201 expected = access.TaggedACLMap{
202 "Admin": access.ACL{
203 In: []security.BlessingPattern{"root/other"},
204 NotIn: []string{}},
205 "Read": access.ACL{In: []security.BlessingPattern{"root/other",
206 "root/self"},
207 NotIn: []string{}},
208 "Write": access.ACL{In: []security.BlessingPattern{"root/other",
209 "root/self"},
210 NotIn: []string{}},
211 "Debug": access.ACL{In: []security.BlessingPattern{"root/other",
212 "root/self"},
213 NotIn: []string{}},
214 "Resolve": access.ACL{In: []security.BlessingPattern{"root/other",
215 "root/self"},
216 NotIn: []string{}}}
217
218 if got := acl; !reflect.DeepEqual(expected.Normalize(), got.Normalize()) {
219 t.Errorf("got %#v, exected %#v ", got, expected)
220 }
221}
222
223func TestPerAppACL(t *testing.T) {
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800224 sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800225 defer deferFn()
226
227 // setup mock up directory to put state in
228 storedir, cleanup := mgmttest.SetupRootDir(t, "application")
229 defer cleanup()
230
Matt Rosencrantzf1c3b442015-01-12 17:53:08 -0800231 otherCtx, otherCancel := mgmttest.NewRuntime(t, globalCtx)
232 defer otherCancel()
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800233 idp := tsecurity.NewIDProvider("root")
234
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800235 // By default, globalRT and otherRT will have blessings generated based on the
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800236 // username/machine name running this process. Since these blessings will appear
237 // in ACLs, give them recognizable names.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800238 if err := idp.Bless(veyron2.GetPrincipal(globalCtx), "self"); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800239 t.Fatal(err)
240 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800241 if err := idp.Bless(veyron2.GetPrincipal(otherCtx), "other"); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800242 t.Fatal(err)
243 }
244
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800245 crDir, crEnv := mgmttest.CredentialsForChild(globalCtx, "repo")
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800246 defer os.RemoveAll(crDir)
247
248 // Make a server with the same credential as test harness.
249 _, nms := mgmttest.RunShellCommand(t, sh, crEnv, repoCmd, "repo", storedir)
250 pid := mgmttest.ReadPID(t, nms)
251 defer syscall.Kill(pid, syscall.SIGINT)
252
253 // Create example envelope.
254 envelopeV1 := application.Envelope{
255 Args: []string{"--help"},
256 Env: []string{"DEBUG=1"},
257 Binary: "/veyron/name/of/binary",
258 }
259
260 // Upload the envelope at two different names.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800261 v1stub := repository.ApplicationClient("repo/search/v1")
262 if err := v1stub.Put(globalCtx, []string{"base"}, envelopeV1); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800263 t.Fatalf("Put() failed: %v", err)
264 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800265 v2stub := repository.ApplicationClient("repo/search/v2")
266 if err := v2stub.Put(globalCtx, []string{"base"}, envelopeV1); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800267 t.Fatalf("Put() failed: %v", err)
268 }
269
270 // Self can access ACLs but other can't.
271 for _, path := range []string{"repo/search", "repo/search/v1", "repo/search/v2"} {
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800272 stub := repository.ApplicationClient(path)
273 acl, etag, err := stub.GetACL(globalCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800274 if !verror.Is(err, impl.ErrNotFound.ID) {
275 t.Fatalf("GetACL should have failed with ErrNotFound but was: %v", err)
276 }
277 if etag != "default" {
278 t.Fatalf("GetACL expected:default, got:%v(%v)", etag, acl)
279 }
280 if acl != nil {
281 t.Fatalf("GetACL got %v, expected %v", acl, nil)
282 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800283 if _, _, err := stub.GetACL(otherCtx); err == nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800284 t.Fatalf("GetACL didn't fail for other when it should have.")
285 }
286 }
287
288 // Self gives other full access only to repo/search/v1.
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800289 newACL := make(access.TaggedACLMap)
290 for _, tag := range access.AllTypicalTags() {
291 newACL.Add("root/self", string(tag))
292 newACL.Add("root/other", string(tag))
293 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800294 if err := v1stub.SetACL(globalCtx, newACL, ""); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800295 t.Fatalf("SetACL failed: %v", err)
296 }
297
298 // Other can now access this location.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800299 acl, _, err := v1stub.GetACL(otherCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800300 if err != nil {
301 t.Fatalf("GetACL should not have failed: %v", err)
302 }
303 expected := access.TaggedACLMap{
304 "Admin": access.ACL{
305 In: []security.BlessingPattern{"root/other",
306 "root/self"},
307 NotIn: []string{}},
308 "Read": access.ACL{In: []security.BlessingPattern{"root/other",
309 "root/self"},
310 NotIn: []string{}},
311 "Write": access.ACL{In: []security.BlessingPattern{"root/other",
312 "root/self"},
313 NotIn: []string{}},
314 "Debug": access.ACL{In: []security.BlessingPattern{"root/other",
315 "root/self"},
316 NotIn: []string{}},
317 "Resolve": access.ACL{In: []security.BlessingPattern{"root/other",
318 "root/self"},
319 NotIn: []string{}}}
320 if got := acl; !reflect.DeepEqual(expected.Normalize(), got.Normalize()) {
321 t.Errorf("got %#v, exected %#v ", got, expected)
322 }
323
324 // But other locations should be unaffected and other cannot access.
325 for _, path := range []string{"repo/search", "repo/search/v2"} {
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800326 stub := repository.ApplicationClient(path)
327 if _, _, err := stub.GetACL(otherCtx); err == nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800328 t.Fatalf("GetACL didn't fail for other when it should have.")
329 }
330 }
331
332 // Self gives other write perms on base.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800333 repostub := repository.ApplicationClient("repo/")
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800334 newACL = make(access.TaggedACLMap)
335 for _, tag := range access.AllTypicalTags() {
336 newACL.Add("root/self", string(tag))
337 }
338 newACL["Write"] = access.ACL{In: []security.BlessingPattern{"root/other", "root/self"}}
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800339 if err := repostub.SetACL(globalCtx, newACL, ""); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800340 t.Fatalf("SetACL failed: %v", err)
341 }
342
343 // Other can now upload an envelope at both locations.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800344 for _, stub := range []repository.ApplicationClientStub{v1stub, v2stub} {
345 if err := stub.Put(otherCtx, []string{"base"}, envelopeV1); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800346 t.Fatalf("Put() failed: %v", err)
347 }
348 }
349
350 // But self didn't give other ACL modification permissions.
351 for _, path := range []string{"repo/search", "repo/search/v2"} {
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800352 stub := repository.ApplicationClient(path)
353 if _, _, err := stub.GetACL(otherCtx); err == nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800354 t.Fatalf("GetACL didn't fail for other when it should have.")
355 }
356 }
357}
358
359func TestInitialACLSet(t *testing.T) {
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800360 sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800361 defer deferFn()
362
363 // Setup mock up directory to put state in.
364 storedir, cleanup := mgmttest.SetupRootDir(t, "application")
365 defer cleanup()
366
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800367 idp := tsecurity.NewIDProvider("root")
368
369 // Make a recognizable principal name.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800370 if err := idp.Bless(veyron2.GetPrincipal(globalCtx), "self"); err != nil {
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800371 t.Fatal(err)
372 }
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800373 crDir, crEnv := mgmttest.CredentialsForChild(globalCtx, "repo")
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800374 defer os.RemoveAll(crDir)
375
376 // Make an TAM for use on the command line.
377 expected := access.TaggedACLMap{
378 "Admin": access.ACL{
379 In: []security.BlessingPattern{"root/rubberchicken",
380 "root/self"},
381 NotIn: []string{},
382 },
383 }
384
385 b := new(bytes.Buffer)
386 if err := expected.WriteTo(b); err != nil {
387 t.Fatal(err)
388 }
389
390 // Start a server with the same credential as test harness.
391 _, nms := mgmttest.RunShellCommand(t, sh, crEnv, repoCmd, "--veyron.acl.literal", b.String(), "repo", storedir)
392 pid := mgmttest.ReadPID(t, nms)
393 defer syscall.Kill(pid, syscall.SIGINT)
394
395 // It should have the correct starting ACLs from the command line.
Matt Rosencrantz6edab562015-01-12 11:07:55 -0800396 stub := repository.ApplicationClient("repo")
397 acl, _, err := stub.GetACL(globalCtx)
Robert Kroegerd6e1d1a2014-12-10 15:08:45 -0800398 if err != nil {
399 t.Fatalf("GetACL should not have failed: %v", err)
400 }
401 if got := acl; !reflect.DeepEqual(expected.Normalize(), got.Normalize()) {
402 t.Errorf("got %#v, exected %#v ", got, expected)
403 }
404}