Merge "core: Rename vom2 to vom."
diff --git a/lib/modules/core/wspr.go b/lib/modules/core/wspr.go
index d6d8dd1..42db213 100644
--- a/lib/modules/core/wspr.go
+++ b/lib/modules/core/wspr.go
@@ -9,7 +9,7 @@
"v.io/core/veyron/lib/modules"
"v.io/wspr/veyron/services/wsprd/wspr"
- "v.io/core/veyron2/rt"
+ "v.io/core/veyron2"
)
var (
@@ -32,13 +32,11 @@
return fmt.Errorf("failed to parse args: %s", err)
}
- r, err := rt.New()
- if err != nil {
- return fmt.Errorf("rt.New failed: %s", err)
- }
- defer r.Cleanup()
+ ctx, shutdown := veyron2.Init()
+ defer shutdown()
+
l := initListenSpec(fl)
- proxy := wspr.NewWSPR(r.NewContext(), *port, nil, &l, *identd, nil)
+ proxy := wspr.NewWSPR(ctx, *port, &l, *identd, nil)
defer proxy.Shutdown()
addr := proxy.Listen()
diff --git a/services/mgmt/binary/binaryd/main.go b/services/mgmt/binary/binaryd/main.go
index 7dc03fa..4a22834 100644
--- a/services/mgmt/binary/binaryd/main.go
+++ b/services/mgmt/binary/binaryd/main.go
@@ -13,7 +13,6 @@
"v.io/core/veyron/lib/netstate"
"v.io/core/veyron/lib/signals"
"v.io/core/veyron/profiles/roaming"
- vflag "v.io/core/veyron/security/flag"
"v.io/core/veyron/services/mgmt/binary/impl"
)
@@ -86,13 +85,18 @@
return
}
defer server.Stop()
- auth := vflag.NewAuthorizerOrDie()
endpoints, err := server.Listen(roaming.ListenSpec)
if err != nil {
vlog.Errorf("Listen(%s) failed: %v", roaming.ListenSpec, err)
return
}
- if err := server.ServeDispatcher(*name, impl.NewDispatcher(state, auth)); err != nil {
+
+ dis, err := impl.NewDispatcher(veyron2.GetPrincipal(ctx), state)
+ if err != nil {
+ vlog.Errorf("NewDispatcher() failed: %v\n", err)
+ return
+ }
+ if err := server.ServeDispatcher(*name, dis); err != nil {
vlog.Errorf("ServeDispatcher(%v) failed: %v", *name, err)
return
}
diff --git a/services/mgmt/binary/impl/acl_test.go b/services/mgmt/binary/impl/acl_test.go
new file mode 100644
index 0000000..6778da3
--- /dev/null
+++ b/services/mgmt/binary/impl/acl_test.go
@@ -0,0 +1,317 @@
+package impl_test
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "syscall"
+ "testing"
+
+ "v.io/core/veyron2"
+ "v.io/core/veyron2/naming"
+ "v.io/core/veyron2/rt"
+ "v.io/core/veyron2/security"
+ "v.io/core/veyron2/services/mgmt/repository"
+ "v.io/core/veyron2/services/security/access"
+ "v.io/core/veyron2/vdl/vdlutil"
+ "v.io/core/veyron2/verror"
+ "v.io/core/veyron2/vlog"
+
+ "v.io/core/veyron/lib/modules"
+ "v.io/core/veyron/lib/signals"
+ "v.io/core/veyron/lib/testutil"
+ tsecurity "v.io/core/veyron/lib/testutil/security"
+ vsecurity "v.io/core/veyron/security"
+ "v.io/core/veyron/services/mgmt/binary/impl"
+ mgmttest "v.io/core/veyron/services/mgmt/lib/testutil"
+)
+
+const (
+ binaryCmd = "binaryd"
+)
+
+func init() {
+ // TODO(rjkroege): Remove when vom2 is ready.
+ vdlutil.Register(&naming.VDLMountedServer{})
+
+ modules.RegisterChild(binaryCmd, "", binaryd)
+ testutil.Init()
+
+ globalRT, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ gctx = globalRT.NewContext()
+ globalCancel = globalRT.Cleanup
+ veyron2.GetNamespace(gctx).CacheCtl(naming.DisableCache(true))
+}
+
+// TestHelperProcess is the entrypoint for the modules commands in
+// a test subprocess.
+func TestHelperProcess(t *testing.T) {
+ modules.DispatchInTest()
+}
+
+func binaryd(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ args = args[1:]
+ if len(args) < 2 {
+ vlog.Fatalf("binaryd expected at least name and store arguments and optionally ACL flags per TaggedACLMapFromFlag")
+ }
+ publishName := args[0]
+ storedir := args[1]
+
+ defer fmt.Fprintf(stdout, "%v terminating\n", publishName)
+ defer vlog.VI(1).Infof("%v terminating", publishName)
+ defer globalCancel()
+ server, endpoint := mgmttest.NewServer(gctx)
+ defer server.Stop()
+
+ name := naming.JoinAddressName(endpoint, "")
+ vlog.VI(1).Infof("binaryd name: %v", name)
+
+ depth := 2
+ state, err := impl.NewState(storedir, "", depth)
+ if err != nil {
+ vlog.Fatalf("NewState(%v, %v, %v) failed: %v", storedir, "", depth, err)
+ }
+ dispatcher, err := impl.NewDispatcher(veyron2.GetPrincipal(gctx), state)
+ if err != nil {
+ vlog.Fatalf("Failed to create binaryd dispatcher: %v", err)
+ }
+ if err := server.ServeDispatcher(publishName, dispatcher); err != nil {
+ vlog.Fatalf("Serve(%v) failed: %v", publishName, err)
+ }
+
+ fmt.Fprintf(stdout, "ready:%d\n", os.Getpid())
+ <-signals.ShutdownOnSignals(gctx)
+
+ return nil
+}
+
+func TestBinaryRootACL(t *testing.T) {
+ selfPrincipal := tsecurity.NewPrincipal("self")
+ selfCtx, err := veyron2.SetPrincipal(gctx, selfPrincipal)
+ if err != nil {
+ t.Fatalf("SetPrincipal failed: %v", err)
+ }
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, selfCtx, veyron2.GetPrincipal(selfCtx))
+ defer deferFn()
+
+ // setup mock up directory to put state in
+ storedir, cleanup := mgmttest.SetupRootDir(t, "bindir")
+ defer cleanup()
+ prepDirectory(t, storedir)
+
+ otherPrincipal, err := vsecurity.NewPrincipal()
+ if err != nil {
+ t.Fatalf("NewPrincipal() failed: %v", err)
+ }
+ otherCtx, err := veyron2.SetPrincipal(selfCtx, otherPrincipal)
+ if err != nil {
+ t.Fatalf("SetPrincipal() failed: %v", err)
+ }
+
+ selfBlessing := selfPrincipal.BlessingStore().Default()
+ otherBlessing, err := selfPrincipal.Bless(otherPrincipal.PublicKey(), selfBlessing, "other", security.UnconstrainedUse())
+ if err != nil {
+ t.Fatalf("selfPrincipal.Bless() failed: %v", err)
+ }
+ if _, err := otherPrincipal.BlessingStore().Set(otherBlessing, "self/child"); err != nil {
+ t.Fatalf("otherPrincipal.BlessingStore() failed: %v", err)
+ }
+ if err := otherPrincipal.AddToRoots(otherBlessing); err != nil {
+ t.Fatalf("otherPrincipal.AddToRoots() failed: %v", err)
+ }
+
+ _, nms := mgmttest.RunShellCommand(t, sh, nil, binaryCmd, "bini", storedir)
+ pid := mgmttest.ReadPID(t, nms)
+ defer syscall.Kill(pid, syscall.SIGINT)
+
+ vlog.VI(2).Infof("Self uploads a shared and private binary.")
+ binary := repository.BinaryClient("bini/private")
+ if err := binary.Create(selfCtx, 1, repository.MediaInfo{Type: "application/octet-stream"}); err != nil {
+ t.Fatalf("Create() failed %v", err)
+ }
+ fakeDataPrivate := testData()
+ if streamErr, err := invokeUpload(t, selfCtx, binary, fakeDataPrivate, 0); streamErr != nil || err != nil {
+ t.Fatalf("invokeUpload() failed %v, %v", err, streamErr)
+ }
+
+ binary = repository.BinaryClient("bini/shared")
+ if err := binary.Create(selfCtx, 1, repository.MediaInfo{Type: "application/octet-stream"}); err != nil {
+ t.Fatalf("Create() failed %v", err)
+ }
+ fakeDataShared := testData()
+ if streamErr, err := invokeUpload(t, selfCtx, binary, fakeDataShared, 0); streamErr != nil || err != nil {
+ t.Fatalf("invokeUpload() failed %v, %v", err, streamErr)
+ }
+
+ vlog.VI(2).Infof("Verify that other can't access bini/private")
+ binary = repository.BinaryClient("bini/shared")
+ if _, _, err := binary.Stat(otherCtx); !verror.Is(err, verror.NoAccess) {
+ t.Fatalf("Stat() should have failed but didn't: %v", err)
+ }
+
+ vlog.VI(2).Infof("Validate the ACL file on bini/private.")
+ binary = repository.BinaryClient("bini/private")
+ acl, _, err := binary.GetACL(selfCtx)
+ if err != nil {
+ t.Fatalf("GetACL failed: %v", err)
+ }
+ expected := access.TaggedACLMap{"Admin": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}, "Read": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}, "Write": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}, "Debug": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}, "Resolve": access.ACL{In: []security.BlessingPattern{"self"}, NotIn: []string{}}}
+ if got, want := acl.Normalize(), expected.Normalize(); !reflect.DeepEqual(got, want) {
+ t.Errorf("got %#v, exected %#v ", got, want)
+ }
+
+ vlog.VI(2).Infof("Validate the ACL file on bini/shared.")
+ binary = repository.BinaryClient("bini/shared")
+ acl, etag, err := binary.GetACL(selfCtx)
+ if err != nil {
+ t.Fatalf("GetACL failed: %v", err)
+ }
+ if got, want := acl.Normalize(), expected.Normalize(); !reflect.DeepEqual(got, want) {
+ t.Errorf("got %#v, exected %#v ", got, want)
+ }
+
+ vlog.VI(2).Infof("Self modifies the ACL file on bini/shared.")
+ for _, tag := range access.AllTypicalTags() {
+ acl.Add("self/other", string(tag))
+ }
+ if err := binary.SetACL(selfCtx, acl, etag); err != nil {
+ t.Fatalf("SetACL failed: %v", err)
+ }
+
+ vlog.VI(2).Infof(" Verify that bini/shared's acls are updated.")
+ binary = repository.BinaryClient("bini/shared")
+ updated := access.TaggedACLMap{"Admin": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}, "Read": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}, "Write": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}, "Debug": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}, "Resolve": access.ACL{In: []security.BlessingPattern{"self", "self/other"}, NotIn: []string{}}}
+ acl, _, err = binary.GetACL(selfCtx)
+ if err != nil {
+ t.Fatalf("GetACL failed: %v", err)
+ }
+ if got, want := acl.Normalize(), updated.Normalize(); !reflect.DeepEqual(got, want) {
+ t.Errorf("got %#v, exected %#v ", got, want)
+ }
+
+ // Other still can't access bini/shared because there's no ACL file at the
+ // root level. Self has to set one explicitly to enable sharing. This way, self
+ // can't accidentally expose the server without setting a root ACL.
+ vlog.VI(2).Infof(" Verify that other still can't access bini/shared.")
+ binary = repository.BinaryClient("bini/shared")
+ if _, _, err := binary.Stat(otherCtx); !verror.Is(err, verror.NoAccess) {
+ t.Fatalf("Stat() should have failed but didn't: %v", err)
+ }
+
+ vlog.VI(2).Infof("Self sets a root ACL.")
+ binary = repository.BinaryClient("bini")
+ newRootACL := make(access.TaggedACLMap)
+ for _, tag := range access.AllTypicalTags() {
+ newRootACL.Add("self", string(tag))
+ }
+ if err := binary.SetACL(selfCtx, newRootACL, ""); err != nil {
+ t.Fatalf("SetACL failed: %v", err)
+ }
+
+ vlog.VI(2).Infof("Verify that other can access bini/shared now but not access bini/private.")
+ binary = repository.BinaryClient("bini/shared")
+ if _, _, err := binary.Stat(otherCtx); err != nil {
+ t.Fatalf("Stat() shouldn't have failed: %v", err)
+ }
+ binary = repository.BinaryClient("bini/private")
+ if _, _, err := binary.Stat(otherCtx); !verror.Is(err, verror.NoAccess) {
+ t.Fatalf("Stat() should have failed but didn't: %v", err)
+ }
+
+ vlog.VI(2).Infof("Other still can't create so Self gives Other right to Create.")
+ binary = repository.BinaryClient("bini")
+ acl, tag, err := binary.GetACL(selfCtx)
+ if err != nil {
+ t.Fatalf("GetACL() failed: %v", err)
+ }
+ acl.Add("self/other", string("Write"))
+ err = binary.SetACL(selfCtx, acl, tag)
+ if err != nil {
+ t.Fatalf("SetACL() failed: %v", err)
+ }
+
+ vlog.VI(2).Infof("Other creates bini/otherbinary")
+ binary = repository.BinaryClient("bini/otherbinary")
+ if err := binary.Create(otherCtx, 1, repository.MediaInfo{Type: "application/octet-stream"}); err != nil {
+ t.Fatalf("Create() failed %v", err)
+ }
+ fakeDataOther := testData()
+ if streamErr, err := invokeUpload(t, otherCtx, binary, fakeDataOther, 0); streamErr != nil || err != nil {
+ t.FailNow()
+ }
+
+ vlog.VI(2).Infof("Other can read acls for bini/otherbinary.")
+ updated = access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"self/other"},
+ NotIn: []string{}},
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"self/other"},
+ NotIn: []string{}},
+ "Write": access.ACL{
+ In: []security.BlessingPattern{"self/other"},
+ NotIn: []string{}},
+ "Debug": access.ACL{In: []security.BlessingPattern{"self/other"},
+ NotIn: []string{}},
+ "Resolve": access.ACL{In: []security.BlessingPattern{"self/other"},
+ NotIn: []string{}}}
+ acl, _, err = binary.GetACL(otherCtx)
+ if err != nil {
+ t.Fatalf("GetACL failed: %v", err)
+ }
+ if got, want := acl.Normalize(), updated.Normalize(); !reflect.DeepEqual(want, got) {
+ t.Errorf("got %#v, exected %#v ", got, want)
+ }
+
+ vlog.VI(2).Infof("Other tries to exclude self by adding self to Read's notin")
+ acl, tag, err = binary.GetACL(otherCtx)
+ if err != nil {
+ t.Fatalf("GetACL() failed: %v", err)
+ }
+ acl.Blacklist("self", string("Read"))
+ err = binary.SetACL(otherCtx, acl, tag)
+ if err != nil {
+ t.Fatalf("SetACL() failed: %v", err)
+ }
+
+ vlog.VI(2).Infof("But self's rights are inherited from root so self can still access despite blacklist.")
+ binary = repository.BinaryClient("bini/otherbinary")
+ if _, _, err := binary.Stat(selfCtx); err != nil {
+ t.Fatalf("Stat() shouldn't have failed: %v", err)
+ }
+
+ vlog.VI(2).Infof("Self petulantly blacklists other back.")
+ binary = repository.BinaryClient("bini")
+ acl, tag, err = binary.GetACL(selfCtx)
+ if err != nil {
+ t.Fatalf("GetACL() failed: %v", err)
+ }
+ for _, tag := range access.AllTypicalTags() {
+ acl.Blacklist("self/other", string(tag))
+ }
+ err = binary.SetACL(selfCtx, acl, tag)
+ if err != nil {
+ t.Fatalf("SetACL() failed: %v", err)
+ }
+
+ vlog.VI(2).Infof("And now other can do nothing. Other should be penitent.")
+ binary = repository.BinaryClient("bini/nototherbinary")
+ if err := binary.Create(otherCtx, 1, repository.MediaInfo{Type: "application/octet-stream"}); !verror.Is(err, verror.NoAccess) {
+ t.Fatalf("Create() should have failed %v", err)
+ }
+
+ binary = repository.BinaryClient("bini/shared")
+ if _, _, err := binary.Stat(otherCtx); !verror.Is(err, verror.NoAccess) {
+ t.Fatalf("Stat() should have failed but didn't: %v", err)
+ }
+
+ vlog.VI(2).Infof("Pennance includes no access to the binary that other made.")
+ binary = repository.BinaryClient("bini/otherbinary")
+ if _, _, err := binary.Stat(otherCtx); !verror.Is(err, verror.NoAccess) {
+ t.Fatalf("Stat() should have failed but didn't: %v", err)
+ }
+}
diff --git a/services/mgmt/binary/impl/dispatcher.go b/services/mgmt/binary/impl/dispatcher.go
index 2105776..a8538d3 100644
--- a/services/mgmt/binary/impl/dispatcher.go
+++ b/services/mgmt/binary/impl/dispatcher.go
@@ -1,9 +1,16 @@
package impl
import (
- "v.io/core/veyron2/ipc"
+ "fmt"
+ "os"
+ "path/filepath"
+
"v.io/core/veyron2/security"
"v.io/core/veyron2/services/mgmt/repository"
+ "v.io/core/veyron2/services/security/access"
+ "v.io/core/veyron2/vlog"
+
+ "v.io/core/veyron/services/mgmt/lib/acls"
)
const (
@@ -13,20 +20,114 @@
// dispatcher holds the state of the binary repository dispatcher.
type dispatcher struct {
- auth security.Authorizer
- state *state
+ state *state
+ locks *acls.Locks
+ principal security.Principal
}
// NewDispatcher is the dispatcher factory.
-func NewDispatcher(state *state, authorizer security.Authorizer) ipc.Dispatcher {
+func NewDispatcher(principal security.Principal, state *state) (*dispatcher, error) {
return &dispatcher{
- auth: authorizer,
- state: state,
- }
+ state: state,
+ locks: acls.NewLocks(),
+ principal: principal,
+ }, nil
}
// DISPATCHER INTERFACE IMPLEMENTATION
+type hierarchicalAuthorizer struct {
+ root security.Authorizer
+ child security.Authorizer
+}
+
+func aclPath(rootDir, suffix string) string {
+ var dir string
+ if suffix == "" {
+ // Directory is in namespace overlapped with Vanadium namespace
+ // so hide it.
+ dir = filepath.Join(rootDir, "__acls")
+ } else {
+ dir = filepath.Join(rootDir, suffix, "acls")
+ }
+ return dir
+}
+
+func newAuthorizer(principal security.Principal, rootDir, suffix string, locks *acls.Locks) (security.Authorizer, error) {
+ aclDir := aclPath(rootDir, "")
+ rootTam, _, err := locks.GetPathACL(principal, aclDir)
+ if err != nil && os.IsNotExist(err) {
+ vlog.VI(2).Infof("GetPathACL(%s) failed: %v, using default authorizer", aclDir, err)
+ return nil, nil
+ } else if err != nil {
+ return nil, err
+ }
+
+ rootAuth, err := access.TaggedACLAuthorizer(rootTam, access.TypicalTagType())
+ if err != nil {
+ vlog.Errorf("Successfully obtained an ACL from the filesystem but TaggedACLAuthorizer couldn't use it: %v", err)
+ return nil, err
+ }
+
+ if suffix == "" {
+ return rootAuth, nil
+ }
+
+ // This is not fatal: the suffix may not exist if we are invoking
+ // a Create() method so we only use the root ACL.
+ aclDir = aclPath(rootDir, suffix)
+ childTam, _, err := locks.GetPathACL(principal, aclDir)
+ if err != nil && os.IsNotExist(err) {
+ return rootAuth, nil
+ } else if err != nil {
+ return nil, err
+ }
+ // Root's blacklist also applies to the child.
+ meldBlacklists(rootTam, childTam)
+
+ childAuth, err := access.TaggedACLAuthorizer(childTam, access.TypicalTagType())
+ if err != nil {
+ vlog.Errorf("Successfully obtained an ACL from the filesystem but TaggedACLAuthorizer couldn't use it: %v", err)
+ return nil, err
+ }
+
+ return &hierarchicalAuthorizer{
+ root: rootAuth,
+ child: childAuth,
+ }, nil
+}
+
+func meldBlacklists(root, child access.TaggedACLMap) {
+ for n, r := range root {
+ for _, b := range r.NotIn {
+ child.Blacklist(b, n)
+ }
+ }
+}
+
+// Authorize implements a chain of logic that works like this: If the
+// root blacklists or the child blacklists, (as implemented by
+// meldBlacklists) then the remote principal is forbidden to connect.
+// This way, the administrator can blacklist a principal even if the
+// child principal has admin permissions on the suffix. Otherwise, Authorize first
+// checks the child authorizer and then fallbacks to the root authorizer.
+// TODO(rjkroege): Introduce a different label for Create().
+func (ha *hierarchicalAuthorizer) Authorize(ctx security.Context) error {
+ childErr := ha.child.Authorize(ctx)
+ if childErr == nil {
+ return nil
+ }
+ rootErr := ha.root.Authorize(ctx)
+ if rootErr == nil {
+ return nil
+ }
+ return fmt.Errorf("Both root acls (%v) and child acls (%v) deny access.", rootErr, childErr)
+}
+
func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
- return repository.BinaryServer(newBinaryService(d.state, suffix)), d.auth, nil
+ auth, err := newAuthorizer(d.principal, d.state.rootDir, suffix, d.locks)
+ if err != nil {
+ return nil, nil, err
+ }
+ return repository.BinaryServer(newBinaryService(d.state, suffix, d.locks)), auth, nil
}
diff --git a/services/mgmt/binary/impl/impl_test.go b/services/mgmt/binary/impl/impl_test.go
index 09fb3a4..97ac906 100644
--- a/services/mgmt/binary/impl/impl_test.go
+++ b/services/mgmt/binary/impl/impl_test.go
@@ -10,9 +10,9 @@
"reflect"
"testing"
+ "v.io/core/veyron2"
"v.io/core/veyron2/context"
"v.io/core/veyron2/naming"
- "v.io/core/veyron2/rt"
"v.io/core/veyron2/services/mgmt/repository"
verror "v.io/core/veyron2/verror2"
"v.io/core/veyron2/vlog"
@@ -28,16 +28,7 @@
)
var gctx *context.T
-
-func init() {
- testutil.Init()
-
- runtime, err := rt.New()
- if err != nil {
- panic(err)
- }
- gctx = runtime.NewContext()
-}
+var globalCancel context.CancelFunc
// startServer starts the binary repository server.
func startServer(t *testing.T, depth int) (repository.BinaryClientMethods, string, string, func()) {
@@ -61,7 +52,10 @@
vlog.Fatalf("Serve() failed: %v", err)
}
}()
- dispatcher := impl.NewDispatcher(state, nil)
+ dispatcher, err := impl.NewDispatcher(veyron2.GetPrincipal(gctx), state)
+ if err != nil {
+ t.Fatalf("NewDispatcher failed: %v", err)
+ }
dontPublishName := ""
if err := server.ServeDispatcher(dontPublishName, dispatcher); err != nil {
t.Fatalf("Serve(%q) failed: %v", dontPublishName, err)
diff --git a/services/mgmt/binary/impl/service.go b/services/mgmt/binary/impl/service.go
index 0ce7bbe..12b1e54 100644
--- a/services/mgmt/binary/impl/service.go
+++ b/services/mgmt/binary/impl/service.go
@@ -1,14 +1,17 @@
-// The implementation of the binary repository interface stores
-// objects identified by object name suffixes using the local file
-// system. Given an object name suffix, the implementation computes an
-// MD5 hash of the suffix and generates the following path in the
-// local filesystem: /<root_dir>/<dir_1>/.../<dir_n>/<hash>. The root
-// directory and the directory depth are parameters of the
-// implementation. The contents of the directory include the checksum
-// and data for each of the individual parts of the binary, and the
-// name of the object:
+// The implementation of the binary repository interface stores objects
+// identified by object name suffixes using the local file system. Given
+// an object name suffix, the implementation computes an MD5 hash of the
+// suffix and generates the following path in the local filesystem:
+// /<root_dir>/<dir_1>/.../<dir_n>/<hash>. The root directory and the
+// directory depth are parameters of the implementation. <root_dir> also
+// contains __acls/data and __acls/sig files storing the ACLs for the
+// root level. The contents of the directory include the checksum and
+// data for each of the individual parts of the binary, the name of the
+// object and a directory containing the acls for this particular object:
//
// name
+// acls/data
+// acls/sig
// <part_1>/checksum
// <part_1>/data
// ...
@@ -31,9 +34,12 @@
"strings"
"syscall"
+ "v.io/core/veyron/services/mgmt/lib/acls"
"v.io/core/veyron2/ipc"
+ "v.io/core/veyron2/security"
"v.io/core/veyron2/services/mgmt/binary"
"v.io/core/veyron2/services/mgmt/repository"
+ "v.io/core/veyron2/services/security/access"
verror "v.io/core/veyron2/verror2"
"v.io/core/veyron2/vlog"
)
@@ -48,6 +54,7 @@
state *state
// suffix is the name of the binary object.
suffix string
+ locks *acls.Locks
}
const pkgPath = "v.io/core/veyron/services/mgmt/binary/impl"
@@ -67,16 +74,31 @@
}
// newBinaryService returns a new Binary service implementation.
-func newBinaryService(state *state, suffix string) *binaryService {
+func newBinaryService(state *state, suffix string, locks *acls.Locks) *binaryService {
return &binaryService{
path: state.dir(suffix),
state: state,
suffix: suffix,
+ locks: locks,
}
}
const BufferLength = 4096
+// insertACLs configures the starting ACL set for a newly "Create"-d binary based
+// on the caller's blessings.
+func insertACLs(dir string, principal security.Principal, locks *acls.Locks, blessings []string) error {
+ tam := make(access.TaggedACLMap)
+
+ // Add the invoker's blessings.
+ for _, b := range blessings {
+ for _, tag := range access.AllTypicalTags() {
+ tam.Add(security.BlessingPattern(b), string(tag))
+ }
+ }
+ return locks.SetPathACL(principal, dir, tam, "")
+}
+
func (i *binaryService) Create(context ipc.ServerContext, nparts int32, mediaInfo repository.MediaInfo) error {
vlog.Infof("%v.Create(%v, %v)", i.suffix, nparts, mediaInfo)
if nparts < 1 {
@@ -98,6 +120,14 @@
vlog.Errorf("WriteFile(%q) failed: %v", nameFile)
return verror.Make(ErrOperationFailed, context.Context())
}
+
+ lp := context.LocalPrincipal()
+ rb := context.RemoteBlessings().ForContext(context)
+ if err := insertACLs(aclPath(i.state.rootDir, i.suffix), lp, i.locks, rb); err != nil {
+ vlog.Errorf("insertACLs(%v, %v) failed: %v", lp, rb, err)
+ return verror.Make(ErrOperationFailed, context.Context())
+ }
+
infoFile := filepath.Join(tmpDir, "mediainfo")
jInfo, err := json.Marshal(mediaInfo)
if err != nil {
@@ -343,3 +373,11 @@
}()
return ch, nil
}
+
+func (i *binaryService) GetACL(ctx ipc.ServerContext) (acl access.TaggedACLMap, etag string, err error) {
+ return i.locks.GetPathACL(ctx.LocalPrincipal(), aclPath(i.state.rootDir, i.suffix))
+}
+
+func (i *binaryService) SetACL(ctx ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
+ return i.locks.SetPathACL(ctx.LocalPrincipal(), aclPath(i.state.rootDir, i.suffix), acl, etag)
+}
diff --git a/services/mgmt/device/impl/impl_test.go b/services/mgmt/device/impl/impl_test.go
index 99b86c4..54760db 100644
--- a/services/mgmt/device/impl/impl_test.go
+++ b/services/mgmt/device/impl/impl_test.go
@@ -749,7 +749,11 @@
}
server, _ := mgmttest.NewServer(globalCtx)
name := "realbin"
- if err := server.ServeDispatcher(name, binaryimpl.NewDispatcher(state, nil)); err != nil {
+ d, err := binaryimpl.NewDispatcher(veyron2.GetPrincipal(globalCtx), state)
+ if err != nil {
+ t.Fatalf("server.NewDispatcher failed: %v", err)
+ }
+ if err := server.ServeDispatcher(name, d); err != nil {
t.Fatalf("server.ServeDispatcher failed: %v", err)
}
diff --git a/services/mgmt/device/impl/mock_repo_test.go b/services/mgmt/device/impl/mock_repo_test.go
index 4c2dd10..eca07b1 100644
--- a/services/mgmt/device/impl/mock_repo_test.go
+++ b/services/mgmt/device/impl/mock_repo_test.go
@@ -165,3 +165,11 @@
vlog.VI(1).Infof("Upload()")
return nil
}
+
+func (i *brInvoker) GetACL(ctx ipc.ServerContext) (acl access.TaggedACLMap, etag string, err error) {
+ return nil, "", nil
+}
+
+func (i *brInvoker) SetACL(ctx ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
+ return nil
+}
diff --git a/services/mgmt/lib/acls/aclaccess.go b/services/mgmt/lib/acls/aclaccess.go
index 07e63a6..c81bfa4 100644
--- a/services/mgmt/lib/acls/aclaccess.go
+++ b/services/mgmt/lib/acls/aclaccess.go
@@ -7,7 +7,7 @@
import (
"io/ioutil"
"os"
- "path"
+ "path/filepath"
"sync"
"v.io/core/veyron2/security"
@@ -41,8 +41,8 @@
// GetPathACL returns the TaggedACLMap from the data file in dir.
func (locks Locks) GetPathACL(principal security.Principal, dir string) (access.TaggedACLMap, string, error) {
- aclpath := path.Join(dir, aclName)
- sigpath := path.Join(dir, sigName)
+ aclpath := filepath.Join(dir, aclName)
+ sigpath := filepath.Join(dir, sigName)
defer locks.lockPath(dir)()
return getCore(principal, aclpath, sigpath)
}
@@ -100,8 +100,8 @@
// directory with enforcement of etag synchronization mechanism and
// locking.
func (locks Locks) SetPathACL(principal security.Principal, dir string, acl access.TaggedACLMap, etag string) error {
- aclpath := path.Join(dir, aclName)
- sigpath := path.Join(dir, sigName)
+ aclpath := filepath.Join(dir, aclName)
+ sigpath := filepath.Join(dir, sigName)
defer locks.lockPath(dir)()
_, oetag, err := getCore(principal, aclpath, sigpath)
if err != nil && !os.IsNotExist(err) {
diff --git a/services/mgmt/lib/binary/impl_test.go b/services/mgmt/lib/binary/impl_test.go
index 1b77501..4a4deb4 100644
--- a/services/mgmt/lib/binary/impl_test.go
+++ b/services/mgmt/lib/binary/impl_test.go
@@ -56,7 +56,11 @@
if err != nil {
t.Fatalf("NewState(%v, %v) failed: %v", rootDir, depth, err)
}
- dispatcher := impl.NewDispatcher(state, nil)
+
+ dispatcher, err := impl.NewDispatcher(veyron2.GetPrincipal(gctx), state)
+ if err != nil {
+ t.Fatalf("NewDispatcher() failed: %v\n", err)
+ }
endpoints, err := server.Listen(profiles.LocalListenSpec)
if err != nil {
t.Fatalf("Listen(%s) failed: %v", profiles.LocalListenSpec, err)
diff --git a/tools/binary/impl_test.go b/tools/binary/impl_test.go
index d8708f7..8d19bbd 100644
--- a/tools/binary/impl_test.go
+++ b/tools/binary/impl_test.go
@@ -19,6 +19,7 @@
"v.io/core/veyron2/security"
"v.io/core/veyron2/services/mgmt/binary"
"v.io/core/veyron2/services/mgmt/repository"
+ "v.io/core/veyron2/services/security/access"
"v.io/core/veyron2/vlog"
"v.io/core/veyron/profiles"
@@ -74,6 +75,14 @@
return nil
}
+func (s *server) GetACL(ipc.ServerContext) (acl access.TaggedACLMap, etag string, err error) {
+ return nil, "", nil
+}
+
+func (s *server) SetACL(ctx ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
+ return nil
+}
+
type dispatcher struct {
}