veyron2/services/security/access: Update to the new ACL format.
Background:
https://drive.google.com/open?id=1DZUu2sGKrf-b4p9JFdIbN0ULhz9Loa8reuPNKsdMKD0&authuser=0
This commit is step 1 of (3 or 4) to remove the old format ACL
(security.ACL type), Labels and LabelSet types and switch everything to
the "tagged acl".
Specifically this change:
- Removes security.Label from security.Context
(a subsequent change will get rid of Label and LabelSet types
completely)
- Updates the access API to use the new format ACLs and tags.
- Updates all usage of the old format labels and ACLs in the core
repository to the new format
- Provides for backward compatibility - if flags/ACL files are lying
around in the old format, they will still work. Files will be
overwritten in place and at some point in the near future the
old format will be dropped completely.
- The "monitoring" label has no current equivalent. Can be added
later if needed, but so far its usage seemed to coincide with
"debug"
Change-Id: Ie665fdccd8c82c30273399fc17406cdc95be03ee
diff --git a/lib/testutil/security/util.go b/lib/testutil/security/util.go
index f92cd99..a696269 100644
--- a/lib/testutil/security/util.go
+++ b/lib/testutil/security/util.go
@@ -9,6 +9,7 @@
vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
)
// NewVeyronCredentials generates a directory with a new principal
@@ -88,13 +89,13 @@
// temporary file, and returns the path to the file. This function is meant
// to be used for testing purposes only, it panics if there is an error. The
// caller must ensure that the created file is removed once it is no longer needed.
-func SaveACLToFile(acl security.ACL) string {
+func SaveACLToFile(acl access.TaggedACLMap) string {
f, err := ioutil.TempFile("", "saved_acl")
if err != nil {
panic(err)
}
defer f.Close()
- if err := vsecurity.SaveACL(f, acl); err != nil {
+ if err := acl.WriteTo(f); err != nil {
defer os.Remove(f.Name())
panic(err)
}
diff --git a/lib/testutil/security/util_test.go b/lib/testutil/security/util_test.go
index 08eb1e1..36718c3 100644
--- a/lib/testutil/security/util_test.go
+++ b/lib/testutil/security/util_test.go
@@ -7,6 +7,7 @@
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
_ "veyron.io/veyron/veyron/profiles"
vsecurity "veyron.io/veyron/veyron/security"
@@ -37,14 +38,11 @@
t.Fatalf("rt.New failed: %v", err)
}
defer r.Cleanup()
- acl := security.ACL{}
- acl.In = map[security.BlessingPattern]security.LabelSet{
- "veyron/...": security.LabelSet(security.ReadLabel),
- "veyron/alice": security.LabelSet(security.ReadLabel | security.WriteLabel),
- "veyron/bob": security.LabelSet(security.AdminLabel),
- }
- acl.NotIn = map[string]security.LabelSet{
- "veyron/che": security.LabelSet(security.ReadLabel),
+ acl := access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"comics/..."},
+ NotIn: []string{"comics/villain"},
+ },
}
filePath := SaveACLToFile(acl)
@@ -55,12 +53,12 @@
t.Fatalf("os.Open(%v) failed: %v", filePath, err)
}
defer f.Close()
- loadedACL, err := vsecurity.LoadACL(f)
+ loadedACL, err := access.ReadTaggedACLMap(f)
if err != nil {
t.Fatalf("LoadACL failed: %v", err)
}
if !reflect.DeepEqual(loadedACL, acl) {
- t.Fatalf("Got ACL %v, but want %v", loadedACL, acl)
+ t.Fatalf("Got %#v, want %#v", loadedACL, acl)
}
}
diff --git a/runtimes/google/ipc/flow_test.go b/runtimes/google/ipc/flow_test.go
index 703a44f..c44b050 100644
--- a/runtimes/google/ipc/flow_test.go
+++ b/runtimes/google/ipc/flow_test.go
@@ -73,7 +73,7 @@
}
func (closureInvoker) Prepare(method string, numArgs int) (argptrs, tags []interface{}, err error) {
- return nil, []interface{}{security.AdminLabel}, nil
+ return nil, nil, nil
}
func (inv closureInvoker) Invoke(method string, call ipc.ServerCall, argptrs []interface{}) (results []interface{}, err error) {
if inv.suffix == "" {
@@ -101,7 +101,7 @@
func (echoInvoker) Prepare(method string, numArgs int) (argptrs, tags []interface{}, err error) {
var arg string
- return []interface{}{&arg}, []interface{}{security.AdminLabel}, nil
+ return []interface{}{&arg}, nil, nil
}
func (inv echoInvoker) Invoke(method string, call ipc.ServerCall, argptrs []interface{}) (results []interface{}, err error) {
result := fmt.Sprintf("method:%q,suffix:%q,arg:%q", method, inv.suffix, *argptrs[0].(*string))
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 266f3c1..02c2633 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -20,6 +20,7 @@
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/uniqueid"
"veyron.io/veyron/veyron2/vdl/vdlutil"
verror "veyron.io/veyron/veyron2/verror2"
@@ -37,7 +38,6 @@
inaming "veyron.io/veyron/veyron/runtimes/google/naming"
tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming"
"veyron.io/veyron/veyron/runtimes/google/vtrace"
- vsecurity "veyron.io/veyron/veyron/security"
)
func init() {
@@ -154,10 +154,9 @@
authorizer = nil
case "aclAuth":
// Only authorize clients matching patterns "client" or "server/...".
- authorizer = vsecurity.NewACLAuthorizer(security.ACL{In: map[security.BlessingPattern]security.LabelSet{
- "server/...": security.LabelSet(security.AdminLabel),
- "client": security.LabelSet(security.AdminLabel),
- }})
+ authorizer = &access.ACL{
+ In: []security.BlessingPattern{"client", "server/..."},
+ }
default:
authorizer = testServerAuthorizer{}
}
diff --git a/runtimes/google/ipc/glob.go b/runtimes/google/ipc/glob.go
index 480f087..33c897c 100644
--- a/runtimes/google/ipc/glob.go
+++ b/runtimes/google/ipc/glob.go
@@ -8,6 +8,7 @@
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
@@ -160,8 +161,8 @@
// AllGlobber or ChildrenGlobber interface of objects as many times as needed.
//
// Before accessing an object, globInternal ensures that the requester is
-// authorized to access it. Internal objects require either security.DebugLabel
-// or security.MonitoringLabel. Service objects require security.ResolveLabel.
+// authorized to access it. Internal objects require access.Debug. Service
+// objects require access.Resolve.
type globInternal struct {
dispNormal ipc.Dispatcher
dispReserved ipc.Dispatcher
@@ -182,10 +183,10 @@
}
disp := i.dispNormal
call.M.Method = ipc.GlobMethod
- call.M.MethodTags = []interface{}{security.ResolveLabel}
+ call.M.MethodTags = []interface{}{access.Resolve}
if naming.IsReserved(i.receiver) || (i.receiver == "" && naming.IsReserved(pattern)) {
disp = i.dispReserved
- call.M.MethodTags = []interface{}{security.DebugLabel | security.MonitoringLabel}
+ call.M.MethodTags = []interface{}{access.Debug}
}
if disp == nil {
return verror.NoExistf("ipc: Glob is not implemented by %q", i.receiver)
@@ -328,14 +329,11 @@
}
}
-func (c *mutableContext) Timestamp() time.Time { return c.M.Timestamp }
-func (c *mutableContext) Method() string { return c.M.Method }
-func (c *mutableContext) MethodTags() []interface{} { return c.M.MethodTags }
-func (c *mutableContext) Name() string { return c.M.Suffix }
-func (c *mutableContext) Suffix() string { return c.M.Suffix }
-func (c *mutableContext) Label() security.Label {
- return security.LabelFromMethodTags(c.M.MethodTags)
-}
+func (c *mutableContext) Timestamp() time.Time { return c.M.Timestamp }
+func (c *mutableContext) Method() string { return c.M.Method }
+func (c *mutableContext) MethodTags() []interface{} { return c.M.MethodTags }
+func (c *mutableContext) Name() string { return c.M.Suffix }
+func (c *mutableContext) Suffix() string { return c.M.Suffix }
func (c *mutableContext) LocalPrincipal() security.Principal { return c.M.LocalPrincipal }
func (c *mutableContext) LocalBlessings() security.Blessings { return c.M.LocalBlessings }
func (c *mutableContext) RemoteBlessings() security.Blessings { return c.M.RemoteBlessings }
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 009e56c..b90dbe4 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -16,6 +16,7 @@
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron2/vom"
@@ -796,7 +797,7 @@
return nil, verr
}
// Check if the caller is permitted to view debug information.
- // TODO(mattr): Is DebugLabel the right thing to check?
+ // TODO(mattr): Is access.Debug the right thing to check?
fs.allowDebug = authorize(debugContext{fs}, auth) == nil
// Invoke the method.
results, err := invoker.Invoke(fs.method, fs, argptrs)
@@ -909,12 +910,14 @@
}
// debugContext is a context which wraps another context but always returns
-// the debug label.
+// the debug tag.
type debugContext struct {
security.Context
}
-func (debugContext) Label() security.Label { return security.DebugLabel }
+func (debugContext) MethodTags() []interface{} {
+ return []interface{}{access.Debug}
+}
// Send implements the ipc.Stream method.
func (fs *flowServer) Send(item interface{}) error {
@@ -973,10 +976,6 @@
//nologcall
return fs.suffix
}
-func (fs *flowServer) Label() security.Label {
- //nologcall
- return security.LabelFromMethodTags(fs.tags)
-}
func (fs *flowServer) LocalPrincipal() security.Principal {
//nologcall
return fs.flow.LocalPrincipal()
diff --git a/runtimes/google/rt/ipc_test.go b/runtimes/google/rt/ipc_test.go
index 018817b..7f7c589 100644
--- a/runtimes/google/rt/ipc_test.go
+++ b/runtimes/google/rt/ipc_test.go
@@ -15,7 +15,6 @@
"veyron.io/veyron/veyron/lib/testutil"
tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
"veyron.io/veyron/veyron/profiles"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/vdl/vdlutil"
"veyron.io/veyron/veyron2/verror"
)
@@ -96,7 +95,7 @@
return nil, "", err
}
serverObjectName := naming.JoinAddressName(endpoint.String(), "")
- if err := server.Serve("", s, vsecurity.NewACLAuthorizer(vsecurity.OpenACL())); err != nil {
+ if err := server.Serve("", s, allowEveryone{}); err != nil {
return nil, "", err
}
return server, serverObjectName, nil
@@ -277,3 +276,7 @@
t.Errorf("client.StartCall passed unexpectedly with remote end authenticated as: %v", remoteBlessings)
}
}
+
+type allowEveryone struct{}
+
+func (allowEveryone) Authorize(security.Context) error { return nil }
diff --git a/security/acl/acl.go b/security/acl/acl.go
deleted file mode 100644
index 3971530..0000000
--- a/security/acl/acl.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package acl
-
-import (
- "encoding/json"
- "io"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-// Includes returns true iff the ACL grants access to a principal
-// that presents blessings.
-func (acl ACL) Includes(blessings ...string) bool {
- blessings = pruneBlacklisted(acl.NotIn, blessings)
- for _, pattern := range acl.In {
- if pattern.MatchedBy(blessings...) {
- return true
- }
- }
- return false
-}
-
-// WriteTo writes the JSON-encoded representation of a TaggedACLMap to w.
-func (m TaggedACLMap) WriteTo(w io.Writer) error {
- return json.NewEncoder(w).Encode(m)
-}
-
-// ReadTaggedACLMap reads the JSON-encoded representation of a TaggedACLMap from r.
-func ReadTaggedACLMap(r io.Reader) (m TaggedACLMap, err error) {
- err = json.NewDecoder(r).Decode(&m)
- return
-}
-
-func pruneBlacklisted(blacklist, blessings []string) []string {
- if len(blacklist) == 0 {
- return blessings
- }
- var filtered []string
- for _, b := range blessings {
- if !security.BlessingPattern(b).MatchedBy(blacklist...) {
- filtered = append(filtered, b)
- }
- }
- return filtered
-}
diff --git a/security/acl/acl.vdl b/security/acl/acl.vdl
deleted file mode 100644
index 51bc01c..0000000
--- a/security/acl/acl.vdl
+++ /dev/null
@@ -1,38 +0,0 @@
-// Package acl defines types and methods to represent Access Control Lists and enforce authorization policies based on them.
-package acl
-
-import "veyron.io/veyron/veyron2/security"
-
-// ACL represents an Access Control List - a set of blessings that should be
-// granted access.
-type ACL struct {
- // In denotes the set of blessings (represented as BlessingPatterns) that
- // should be granted access, unless blacklisted by an entry in NotIn.
- //
- // For example:
- // In: {"alice/family/..."}
- // grants access to a principal that presents at least one of "alice",
- // "alice/family", "alice/family/friend" etc. as a blessing.
- In []security.BlessingPattern
-
- // NotIn denotes the set of blessings (and their delegates) that
- // have been explicitly blacklisted from the In set.
- //
- // For example:
- // In: {"alice/friend/..."}, NotIn: {"alice/friend/bob"}
- // grants access to principals that present "alice", "alice/friend",
- // "alice/friend/carol" etc. but NOT to a principal that presents
- // "alice/friend/bob" or "alice/friend/bob/spouse" etc.
- NotIn []string
-
- // TODO(ashankar,ataly): At some point, introduce group identifiers here?
-}
-
-// TaggedACLMap maps string tags to access control lists specifying the
-// blessings required to invoke methods with that tag.
-//
-// These tags are meant to add a layer of interposition between set of users
-// (blessings, specifically) and the set of methods, much like "Roles" do in
-// Role Based Access Control (RBAC).
-// (http://en.wikipedia.org/wiki/Role-based_access_control)
-type TaggedACLMap map[string]ACL
diff --git a/security/acl/acl.vdl.go b/security/acl/acl.vdl.go
deleted file mode 100644
index 4a658de..0000000
--- a/security/acl/acl.vdl.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// This file was auto-generated by the veyron vdl tool.
-// Source: acl.vdl
-
-// Package acl defines types and methods to represent Access Control Lists and enforce authorization policies based on them.
-package acl
-
-import (
- "veyron.io/veyron/veyron2/security"
-)
-
-// ACL represents an Access Control List - a set of blessings that should be
-// granted access.
-type ACL struct {
- // In denotes the set of blessings (represented as BlessingPatterns) that
- // should be granted access, unless blacklisted by an entry in NotIn.
- //
- // For example:
- // In: {"alice/family/..."}
- // grants access to a principal that presents at least one of "alice",
- // "alice/family", "alice/family/friend" etc. as a blessing.
- In []security.BlessingPattern
- // NotIn denotes the set of blessings (and their delegates) that
- // have been explicitly blacklisted from the In set.
- //
- // For example:
- // In: {"alice/friend/..."}, NotIn: {"alice/friend/bob"}
- // grants access to principals that present "alice", "alice/friend",
- // "alice/friend/carol" etc. but NOT to a principal that presents
- // "alice/friend/bob" or "alice/friend/bob/spouse" etc.
- NotIn []string
-}
-
-// TaggedACLMap maps string tags to access control lists specifying the
-// blessings required to invoke methods with that tag.
-//
-// These tags are meant to add a layer of interposition between set of users
-// (blessings, specifically) and the set of methods, much like "Roles" do in
-// Role Based Access Control (RBAC).
-// (http://en.wikipedia.org/wiki/Role-based_access_control)
-type TaggedACLMap map[string]ACL
diff --git a/security/acl/acl_test.go b/security/acl/acl_test.go
deleted file mode 100644
index 4187b18..0000000
--- a/security/acl/acl_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package acl
-
-import (
- "bytes"
- "reflect"
- "testing"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-func TestInclude(t *testing.T) {
- acl := ACL{
- In: []security.BlessingPattern{"alice", "alice/friend/...", "bob/family/..."},
- NotIn: []string{"alice/friend/carol", "bob/family/mallory"},
- }
- type V []string // shorthand
- tests := []struct {
- Blessings []string
- Want bool
- }{
- {nil, false}, // No blessings presented, cannot access
- {V{}, false},
- {V{"alice"}, true},
- {V{"bob"}, true},
- {V{"carol"}, false},
- {V{"alice/colleague"}, false},
- {V{"alice", "carol/friend"}, true}, // Presenting one blessing that grants access is sufficient
- {V{"alice/friend/bob"}, true},
- {V{"alice/friend/carol"}, false}, // alice/friend/carol is blacklisted
- {V{"alice/friend/carol/family"}, false}, // alice/friend/carol is blacklisted, thus her delegates must be too.
- {V{"alice/friend/bob", "alice/friend/carol"}, true},
- {V{"bob/family/eve", "bob/family/mallory"}, true},
- {V{"bob/family/mallory", "alice/friend/carol"}, false},
- }
- for _, test := range tests {
- if got, want := acl.Includes(test.Blessings...), test.Want; got != want {
- t.Errorf("Includes(%v): Got %v, want %v", test.Blessings, got, want)
- }
- }
-}
-
-func TestOpenACL(t *testing.T) {
- acl := ACL{In: []security.BlessingPattern{security.AllPrincipals}}
- if !acl.Includes() {
- t.Errorf("OpenACL should allow principals that present no blessings")
- }
- if !acl.Includes("frank") {
- t.Errorf("OpenACL should allow principals that present any blessings")
- }
-}
-
-func TestTaggedACLMapSerialization(t *testing.T) {
- obj := TaggedACLMap{
- "R": ACL{
- In: []security.BlessingPattern{"foo/...", "bar/..."},
- NotIn: []string{"bar/baz"},
- },
- "W": ACL{
- In: []security.BlessingPattern{"foo/...", "bar"},
- NotIn: []string{"foo/bar", "foo/baz/boz"},
- },
- }
- txt := `
-{
- "R": {
- "In":["foo/...","bar/..."],
- "NotIn":["bar/baz"]
- },
- "W": {
- "In":["foo/...","bar"],
- "NotIn":["foo/bar","foo/baz/boz"]
- }
-}
-`
- if got, err := ReadTaggedACLMap(bytes.NewBufferString(txt)); err != nil || !reflect.DeepEqual(got, obj) {
- t.Errorf("Got error %v, TaggedACLMap: %v, want %v", err, got, obj)
- }
- // And round-trip (don't compare with 'txt' because indentation/spacing might differ).
- var buf bytes.Buffer
- if err := obj.WriteTo(&buf); err != nil {
- t.Fatal(err)
- }
- if got, err := ReadTaggedACLMap(&buf); err != nil || !reflect.DeepEqual(got, obj) {
- t.Errorf("Got error %v, TaggedACLMap: %v, want %v", err, got, obj)
- }
-}
diff --git a/security/acl/authorizer.go b/security/acl/authorizer.go
deleted file mode 100644
index f4d60c9..0000000
--- a/security/acl/authorizer.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package acl
-
-import (
- "fmt"
- "os"
- "reflect"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-// TaggedACLAuthorizer implements an authorization policy where access is
-// granted if the remote end presents blessings included in the Access Control
-// Lists (ACLs) associated with the set of relevant tags.
-//
-// The set of relevant tags is the subset of tags associated with the
-// method (security.Context.MethodTags) that have the same type as tagType.
-// Currently, tagType.Kind must be reflect.String, i.e., only tags that are
-// named string types are supported.
-//
-// If multiple tags of tagType are associated with the method, then access is
-// granted if the peer presents blessings that match the ACLs of each one of
-// those tags. If no tags of tagType are associated with the method, then
-// access is denied.
-//
-// If the TaggedACLMap provided is nil, then a nil authorizer is returned.
-//
-// Sample usage:
-//
-// (1) Attach tags to methods in the VDL (eg. myservice.vdl)
-// package myservice
-//
-// type MyTag string
-// const (
-// ReadAccess = MyTag("R")
-// WriteAccess = MyTag("W")
-// )
-//
-// type MyService interface {
-// Get() ([]string, error) {ReadAccess}
-// GetIndex(int) (string, error) {ReadAccess}
-//
-// Set([]string) error {WriteAccess}
-// SetIndex(int, string) error {WriteAccess}
-//
-// GetAndSet([]string) ([]string, error) {ReadAccess, WriteAccess}
-// }
-//
-// (2) Setup the ipc.Dispatcher to use the TaggedACLAuthorizer
-// import (
-// "reflect"
-// "veyron.io/veyron/veyron/security/acl"
-//
-// "veyron.io/veyron/veyron2/ipc"
-// "veyron.io/veyron/veyron2/security"
-// )
-//
-// type dispatcher struct{}
-// func (d dispatcher) Lookup(suffix, method) (ipc.Invoker, security.Authorizer, error) {
-// acl := acl.TaggedACLMap{
-// "R": acl.ACL{In: []security.BlessingPattern{"alice/friends/...", "alice/family/..."} },
-// "W": acl.ACL{In: []security.BlessingPattern{"alice/family/...", "alice/colleagues/..." } },
-// }
-// typ := reflect.TypeOf(ReadAccess) // equivalently, reflect.TypeOf(WriteAccess)
-// return newInvoker(), acl.TaggedACLAuthorizer(acl, typ), nil
-// }
-//
-// With the above dispatcher, the server will grant access to a peer with the blessing
-// "alice/friend/bob" access only to the "Get" and "GetIndex" methods. A peer presenting
-// the blessing "alice/colleague/carol" will get access only to the "Set" and "SetIndex"
-// methods. A peer presenting "alice/family/mom" will get access to all methods, even
-// GetAndSet - which requires that the blessing appear in the ACLs for both the
-// ReadAccess and WriteAccess tags.
-func TaggedACLAuthorizer(acls TaggedACLMap, tagType reflect.Type) (security.Authorizer, error) {
- if tagType.Kind() != reflect.String {
- return nil, fmt.Errorf("tag type(%v) must be backed by a string not %v", tagType, tagType.Kind())
- }
- return &authorizer{acls, tagType}, nil
-}
-
-// TaggedACLAuthorizerFromFile applies the same authorization policy as
-// TaggedACLAuthorizer, with the TaggedACLMap to be used sourced from a file named
-// filename.
-//
-// Changes to the file are monitored and affect subsequent calls to Authorize.
-// Currently, this is achieved by re-reading the file on every call to
-// Authorize.
-// TODO(ashankar,ataly): Use inotify or a similar mechanism to watch for
-// changes.
-func TaggedACLAuthorizerFromFile(filename string, tagType reflect.Type) (security.Authorizer, error) {
- if tagType.Kind() != reflect.String {
- return nil, fmt.Errorf("tag type(%v) must be backed by a string not %v", tagType, tagType.Kind())
- }
- return &fileAuthorizer{filename, tagType}, nil
-}
-
-type authorizer struct {
- acls TaggedACLMap
- tagType reflect.Type
-}
-
-func (a *authorizer) Authorize(ctx security.Context) error {
- // "Self-RPCs" are always authorized.
- if l, r := ctx.LocalBlessings(), ctx.RemoteBlessings(); l != nil && r != nil && reflect.DeepEqual(l.PublicKey(), r.PublicKey()) {
- return nil
- }
- var blessings []string
- if ctx.RemoteBlessings() != nil {
- blessings = ctx.RemoteBlessings().ForContext(ctx)
- }
- grant := false
- for _, tag := range ctx.MethodTags() {
- if v := reflect.ValueOf(tag); v.Type() == a.tagType {
- if acl, exists := a.acls[v.String()]; !exists || !acl.Includes(blessings...) {
- return errACLMatch(blessings)
- }
- grant = true
- }
- }
- if grant {
- return nil
- }
- return errACLMatch(blessings)
-}
-
-type fileAuthorizer struct {
- filename string
- tagType reflect.Type
-}
-
-func (a *fileAuthorizer) Authorize(ctx security.Context) error {
- acl, err := loadTaggedACLMapFromFile(a.filename)
- if err != nil {
- // TODO(ashankar): Information leak?
- return fmt.Errorf("failed to read ACL from file: %v", err)
- }
- return (&authorizer{acl, a.tagType}).Authorize(ctx)
-}
-
-func loadTaggedACLMapFromFile(filename string) (TaggedACLMap, error) {
- file, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- defer file.Close()
- return ReadTaggedACLMap(file)
-}
-
-func errACLMatch(blessings []string) error {
- return fmt.Errorf("%v does not match ACL", blessings)
-}
diff --git a/security/acl/authorizer_test.go b/security/acl/authorizer_test.go
deleted file mode 100644
index 27803f0..0000000
--- a/security/acl/authorizer_test.go
+++ /dev/null
@@ -1,224 +0,0 @@
-package acl
-
-import (
- "io/ioutil"
- "reflect"
- "testing"
-
- vsecurity "veyron.io/veyron/veyron/security"
- "veyron.io/veyron/veyron/security/acl/test"
- "veyron.io/veyron/veyron2/security"
-)
-
-// TestTaggedACLAuthorizer is both a test and a demonstration of the use of the
-// TaggedACLAuthorizer and interaction with interface specification in VDL.
-func TestTaggedACLAuthorizer(t *testing.T) {
- type P []security.BlessingPattern
- type S []string
- // TaggedACLMap to test against.
- acl := TaggedACLMap{
- "R": {
- In: P{"..."},
- },
- "W": {
- In: P{"ali/family/...", "bob/...", "che"},
- NotIn: S{"bob/acquaintances"},
- },
- "X": {
- In: P{"ali/family/boss", "superman"},
- },
- }
- type testcase struct {
- Method string
- Client security.Blessings
- }
- var (
- authorizer, _ = TaggedACLAuthorizer(acl, reflect.TypeOf(test.Read))
- // Two principals: The "server" and the "client"
- pserver, _ = vsecurity.NewPrincipal()
- pclient, _ = vsecurity.NewPrincipal()
- server, _ = pserver.BlessSelf("server")
-
- // B generates the provided blessings for the client and ensures
- // that the server will recognize them.
- B = func(names ...string) security.Blessings {
- var ret security.Blessings
- for _, name := range names {
- b, err := pclient.BlessSelf(name)
- if err != nil {
- t.Fatalf("%q: %v", name, err)
- }
- if err := pserver.AddToRoots(b); err != nil {
- t.Fatalf("%q: %v", name, err)
- }
- if ret, err = security.UnionOfBlessings(ret, b); err != nil {
- t.Fatal(err)
- }
- }
- return ret
- }
-
- run = func(test testcase) error {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: test.Client,
- Method: test.Method,
- MethodTags: methodTags(test.Method),
- })
- return authorizer.Authorize(ctx)
- }
- )
-
- // Test cases where access should be granted to methods with tags on
- // them.
- for _, test := range []testcase{
- {"Get", nil},
- {"Get", B("ali")},
- {"Get", B("bob/friend", "che/enemy")},
-
- {"Put", B("ali")},
- {"Put", B("ali/family/mom")},
- {"Put", B("bob/friends")},
- {"Put", B("bob/acquantainces/carol", "che")}, // Access granted because of "che"
-
- {"Resolve", B("ali")},
- {"Resolve", B("ali/family/boss")},
-
- {"AllTags", B("ali/family/boss")},
- } {
- if err := run(test); err != nil {
- t.Errorf("Access denied to method %q to %v: %v", test.Method, test.Client, err)
- }
- }
- // Test cases where access should be denied.
- for _, test := range []testcase{
- // Nobody is denied access to "Get"
- {"Put", B("bob/acquaintances/dave", "che/friend", "dave")},
- {"Resolve", B("ali/family/friend")},
- // Since there are no tags on the NoTags method, it has an
- // empty ACL. No client will have access.
- {"NoTags", B("ali", "ali/family/boss", "bob")},
- // On a method with multiple tags on it, all must be satisfied.
- {"AllTags", B("superman")}, // Only in the X ACL, not in R or W
- {"AllTags", B("superman", "clark")}, // In X and in R, but not W
- } {
- if err := run(test); err == nil {
- t.Errorf("Access to %q granted to %v", test.Method, test.Client)
- }
- }
-}
-
-func TestTaggedACLAuthorizerSelfRPCs(t *testing.T) {
- var (
- // Client and server are the same principal, though have
- // different blessings.
- p, _ = vsecurity.NewPrincipal()
- client, _ = p.BlessSelf("client")
- server, _ = p.BlessSelf("server")
- // Authorizer with a TaggedACLMap that grants read access to
- // anyone, write/execute access to noone.
- typ test.MyTag
- authorizer, _ = TaggedACLAuthorizer(TaggedACLMap{"R": {In: []security.BlessingPattern{"nobody"}}}, reflect.TypeOf(typ))
- )
- for _, test := range []string{"Put", "Get", "Resolve", "NoTags", "AllTags"} {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: p,
- LocalBlessings: server,
- RemoteBlessings: client,
- Method: test,
- MethodTags: methodTags(test),
- })
- if err := authorizer.Authorize(ctx); err != nil {
- t.Errorf("Got error %v for method %q", err, test)
- }
- }
-}
-
-func TestTaggedACLAuthorizerWithNilACL(t *testing.T) {
- var (
- authorizer, _ = TaggedACLAuthorizer(nil, reflect.TypeOf(test.Read))
- pserver, _ = vsecurity.NewPrincipal()
- pclient, _ = vsecurity.NewPrincipal()
- server, _ = pserver.BlessSelf("server")
- client, _ = pclient.BlessSelf("client")
- )
- for _, test := range []string{"Put", "Get", "Resolve", "NoTags", "AllTags"} {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: client,
- Method: test,
- MethodTags: methodTags(test),
- })
- if err := authorizer.Authorize(ctx); err == nil {
- t.Errorf("nil TaggedACLMap authorized method %q", test)
- }
- }
-}
-
-func TestTaggedACLAuthorizerFromFile(t *testing.T) {
- file, err := ioutil.TempFile("", "TestTaggedACLAuthorizerFromFile")
- if err != nil {
- t.Fatal(err)
- }
- filename := file.Name()
- file.Close()
-
- var (
- authorizer, _ = TaggedACLAuthorizerFromFile(filename, reflect.TypeOf(test.Read))
- pserver, _ = vsecurity.NewPrincipal()
- pclient, _ = vsecurity.NewPrincipal()
- server, _ = pserver.BlessSelf("alice")
- alicefriend, _ = pserver.Bless(pclient.PublicKey(), server, "friend/bob", security.UnconstrainedUse())
- ctx = security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: alicefriend,
- Method: "Get",
- MethodTags: methodTags("Get"),
- })
- )
- // Make pserver recognize itself as an authority on "alice/..." blessings.
- if err := pserver.AddToRoots(server); err != nil {
- t.Fatal(err)
- }
- // "alice/friend/bob" should not have access to test.Read methods like Get.
- if err := authorizer.Authorize(ctx); err == nil {
- t.Fatalf("Expected authorization error as %v is not on the ACL for Read operations", ctx.RemoteBlessings())
- }
- // Rewrite the file giving access
- if err := ioutil.WriteFile(filename, []byte(`{"R": { "In":["alice/friend/..."] }}`), 0600); err != nil {
- t.Fatal(err)
- }
- // Now should have access
- if err := authorizer.Authorize(ctx); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestTagTypeMustBeString(t *testing.T) {
- type I int
- if auth, err := TaggedACLAuthorizer(TaggedACLMap{}, reflect.TypeOf(I(0))); err == nil || auth != nil {
- t.Errorf("Got (%v, %v), wanted error since tag type is not a string", auth, err)
- }
- if auth, err := TaggedACLAuthorizerFromFile("does_not_matter", reflect.TypeOf(I(0))); err == nil || auth != nil {
- t.Errorf("Got (%v, %v), wanted error since tag type is not a string", auth, err)
- }
-}
-
-func methodTags(name string) []interface{} {
- server := test.MyObjectServer(nil)
- for _, iface := range server.Describe__() {
- for _, method := range iface.Methods {
- if method.Name == name {
- tags := make([]interface{}, len(method.Tags))
- for index, tag := range method.Tags {
- tags[index] = tag
- }
- return tags
- }
- }
- }
- return nil
-}
diff --git a/security/acl/test/vdl.vdl b/security/acl/test/vdl.vdl
deleted file mode 100644
index fd3a73f..0000000
--- a/security/acl/test/vdl.vdl
+++ /dev/null
@@ -1,23 +0,0 @@
-// Package test provides a VDL specification for a service used in the unittest of the acl package.
-package test
-
-// Any package can define tags (of arbitrary types) to be attached to methods.
-// This type can be used to index into a TaggedACLMap.
-type MyTag string
-
-const (
- // For this example/unittest, there are three possible values of MyTag,
- // each represented by a single-character string.
- Read = MyTag("R")
- Write = MyTag("W")
- Execute = MyTag("X")
-)
-
-// MyObject demonstrates how tags are attached to methods.
-type MyObject interface {
- Get() error {Read}
- Put() error {Write}
- Resolve() error {Execute}
- NoTags() error // No tags attached to this.
- AllTags() error {Read, Write, Execute}
-}
diff --git a/security/acl/test/vdl.vdl.go b/security/acl/test/vdl.vdl.go
deleted file mode 100644
index 284a509..0000000
--- a/security/acl/test/vdl.vdl.go
+++ /dev/null
@@ -1,301 +0,0 @@
-// This file was auto-generated by the veyron vdl tool.
-// Source: vdl.vdl
-
-// Package test provides a VDL specification for a service used in the unittest of the acl package.
-package test
-
-import (
- // The non-user imports are prefixed with "__" to prevent collisions.
- __veyron2 "veyron.io/veyron/veyron2"
- __context "veyron.io/veyron/veyron2/context"
- __ipc "veyron.io/veyron/veyron2/ipc"
- __vdlutil "veyron.io/veyron/veyron2/vdl/vdlutil"
- __wiretype "veyron.io/veyron/veyron2/wiretype"
-)
-
-// TODO(toddw): Remove this line once the new signature support is done.
-// It corrects a bug where __wiretype is unused in VDL pacakges where only
-// bootstrap types are used on interfaces.
-const _ = __wiretype.TypeIDInvalid
-
-// Any package can define tags (of arbitrary types) to be attached to methods.
-// This type can be used to index into a TaggedACLMap.
-type MyTag string
-
-// For this example/unittest, there are three possible values of MyTag,
-// each represented by a single-character string.
-const Read = MyTag("R")
-
-const Write = MyTag("W")
-
-const Execute = MyTag("X")
-
-// MyObjectClientMethods is the client interface
-// containing MyObject methods.
-//
-// MyObject demonstrates how tags are attached to methods.
-type MyObjectClientMethods interface {
- Get(__context.T, ...__ipc.CallOpt) error
- Put(__context.T, ...__ipc.CallOpt) error
- Resolve(__context.T, ...__ipc.CallOpt) error
- NoTags(__context.T, ...__ipc.CallOpt) error // No tags attached to this.
- AllTags(__context.T, ...__ipc.CallOpt) error
-}
-
-// MyObjectClientStub adds universal methods to MyObjectClientMethods.
-type MyObjectClientStub interface {
- MyObjectClientMethods
- __ipc.UniversalServiceMethods
-}
-
-// MyObjectClient returns a client stub for MyObject.
-func MyObjectClient(name string, opts ...__ipc.BindOpt) MyObjectClientStub {
- var client __ipc.Client
- for _, opt := range opts {
- if clientOpt, ok := opt.(__ipc.Client); ok {
- client = clientOpt
- }
- }
- return implMyObjectClientStub{name, client}
-}
-
-type implMyObjectClientStub struct {
- name string
- client __ipc.Client
-}
-
-func (c implMyObjectClientStub) c(ctx __context.T) __ipc.Client {
- if c.client != nil {
- return c.client
- }
- return __veyron2.RuntimeFromContext(ctx).Client()
-}
-
-func (c implMyObjectClientStub) Get(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "Get", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) Put(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "Put", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) Resolve(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "Resolve", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) NoTags(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "NoTags", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) AllTags(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "AllTags", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) Signature(ctx __context.T, opts ...__ipc.CallOpt) (o0 __ipc.ServiceSignature, err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "Signature", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&o0, &err); ierr != nil {
- err = ierr
- }
- return
-}
-
-// MyObjectServerMethods is the interface a server writer
-// implements for MyObject.
-//
-// MyObject demonstrates how tags are attached to methods.
-type MyObjectServerMethods interface {
- Get(__ipc.ServerContext) error
- Put(__ipc.ServerContext) error
- Resolve(__ipc.ServerContext) error
- NoTags(__ipc.ServerContext) error // No tags attached to this.
- AllTags(__ipc.ServerContext) error
-}
-
-// MyObjectServerStubMethods is the server interface containing
-// MyObject methods, as expected by ipc.Server.
-// There is no difference between this interface and MyObjectServerMethods
-// since there are no streaming methods.
-type MyObjectServerStubMethods MyObjectServerMethods
-
-// MyObjectServerStub adds universal methods to MyObjectServerStubMethods.
-type MyObjectServerStub interface {
- MyObjectServerStubMethods
- // Describe the MyObject interfaces.
- Describe__() []__ipc.InterfaceDesc
- // Signature will be replaced with Describe__.
- Signature(ctx __ipc.ServerContext) (__ipc.ServiceSignature, error)
-}
-
-// MyObjectServer returns a server stub for MyObject.
-// It converts an implementation of MyObjectServerMethods into
-// an object that may be used by ipc.Server.
-func MyObjectServer(impl MyObjectServerMethods) MyObjectServerStub {
- stub := implMyObjectServerStub{
- impl: impl,
- }
- // Initialize GlobState; always check the stub itself first, to handle the
- // case where the user has the Glob method defined in their VDL source.
- if gs := __ipc.NewGlobState(stub); gs != nil {
- stub.gs = gs
- } else if gs := __ipc.NewGlobState(impl); gs != nil {
- stub.gs = gs
- }
- return stub
-}
-
-type implMyObjectServerStub struct {
- impl MyObjectServerMethods
- gs *__ipc.GlobState
-}
-
-func (s implMyObjectServerStub) Get(ctx __ipc.ServerContext) error {
- return s.impl.Get(ctx)
-}
-
-func (s implMyObjectServerStub) Put(ctx __ipc.ServerContext) error {
- return s.impl.Put(ctx)
-}
-
-func (s implMyObjectServerStub) Resolve(ctx __ipc.ServerContext) error {
- return s.impl.Resolve(ctx)
-}
-
-func (s implMyObjectServerStub) NoTags(ctx __ipc.ServerContext) error {
- return s.impl.NoTags(ctx)
-}
-
-func (s implMyObjectServerStub) AllTags(ctx __ipc.ServerContext) error {
- return s.impl.AllTags(ctx)
-}
-
-func (s implMyObjectServerStub) VGlob() *__ipc.GlobState {
- return s.gs
-}
-
-func (s implMyObjectServerStub) Describe__() []__ipc.InterfaceDesc {
- return []__ipc.InterfaceDesc{MyObjectDesc}
-}
-
-// MyObjectDesc describes the MyObject interface.
-var MyObjectDesc __ipc.InterfaceDesc = descMyObject
-
-// descMyObject hides the desc to keep godoc clean.
-var descMyObject = __ipc.InterfaceDesc{
- Name: "MyObject",
- PkgPath: "veyron.io/veyron/veyron/security/acl/test",
- Doc: "// MyObject demonstrates how tags are attached to methods.",
- Methods: []__ipc.MethodDesc{
- {
- Name: "Get",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- Tags: []__vdlutil.Any{MyTag("R")},
- },
- {
- Name: "Put",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- Tags: []__vdlutil.Any{MyTag("W")},
- },
- {
- Name: "Resolve",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- Tags: []__vdlutil.Any{MyTag("X")},
- },
- {
- Name: "NoTags",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- },
- {
- Name: "AllTags",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- Tags: []__vdlutil.Any{MyTag("R"), MyTag("W"), MyTag("X")},
- },
- },
-}
-
-func (s implMyObjectServerStub) Signature(ctx __ipc.ServerContext) (__ipc.ServiceSignature, error) {
- // TODO(toddw): Replace with new Describe__ implementation.
- result := __ipc.ServiceSignature{Methods: make(map[string]__ipc.MethodSignature)}
- result.Methods["AllTags"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
- result.Methods["Get"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
- result.Methods["NoTags"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
- result.Methods["Put"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
- result.Methods["Resolve"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
-
- result.TypeDefs = []__vdlutil.Any{
- __wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
-
- return result, nil
-}
diff --git a/security/acl_authorizer.go b/security/acl_authorizer.go
deleted file mode 100644
index 2adfa80..0000000
--- a/security/acl_authorizer.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package security
-
-// This file provides an implementation of security.Authorizer.
-//
-// Definitions
-// * Self-RPC: An RPC request is said to be a "self-RPC" if the identities
-// at the local and remote ends are identical.
-
-import (
- "errors"
- "os"
- "reflect"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-var (
- errACL = errors.New("no matching ACL entry found")
- errInvalidLabel = errors.New("label is invalid")
-)
-
-// aclAuthorizer implements Authorizer.
-type aclAuthorizer security.ACL
-
-// Authorize verifies a request iff the identity at the remote end has a name authorized by
-// the aclAuthorizer's ACL for the request's label, or the request corresponds to a self-RPC.
-func (a aclAuthorizer) Authorize(ctx security.Context) error {
- // Test if the request corresponds to a self-RPC.
- if ctx.LocalBlessings() != nil && ctx.RemoteBlessings() != nil && reflect.DeepEqual(ctx.LocalBlessings().PublicKey(), ctx.RemoteBlessings().PublicKey()) {
- return nil
- }
- var blessings []string
- if ctx.RemoteBlessings() != nil {
- blessings = ctx.RemoteBlessings().ForContext(ctx)
- }
- return matchesACL(blessings, ctx.Label(), security.ACL(a))
-}
-
-// NewACLAuthorizer creates an authorizer from the provided ACL. The
-// authorizer authorizes a request iff the identity at the remote end has a name
-// authorized by the provided ACL for the request's label, or the request
-// corresponds to a self-RPC.
-func NewACLAuthorizer(acl security.ACL) security.Authorizer { return aclAuthorizer(acl) }
-
-// fileACLAuthorizer implements Authorizer.
-type fileACLAuthorizer string
-
-// Authorize reads and decodes the fileACLAuthorizer's ACL file into a ACL and
-// then verifies the request according to an aclAuthorizer based on the ACL. If
-// reading or decoding the file fails then no requests are authorized.
-func (a fileACLAuthorizer) Authorize(ctx security.Context) error {
- acl, err := loadACLFromFile(string(a))
- if err != nil {
- return err
- }
- return aclAuthorizer(acl).Authorize(ctx)
-}
-
-// NewFileACLAuthorizer creates an authorizer from the provided path to a file
-// containing a JSON-encoded ACL. Each call to "Authorize" involves reading and
-// decoding a ACL from the file and then authorizing the request according to the
-// ACL. The authorizer monitors the file so out of band changes to the contents of
-// the file are reflected in the ACL. If reading or decoding the file fails then
-// no requests are authorized.
-//
-// The JSON-encoding of a ACL is essentially a JSON object describing a map from
-// BlessingPatterns to encoded LabelSets (see LabelSet.MarshalJSON).
-// Examples:
-// * `{"In": {"..." : "RW"}}` encodes an ACL that allows all principals to access all methods with
-// ReadLabel or WriteLabel.
-// * `{"In":{"veyron/alice": "RW", "veyron/bob/...": "R"}}` encodes an ACL that allows all principals
-// matched by "veyron/alice" to access methods with ReadLabel or WriteLabel, and all
-// principals matched by "veyron/bob/..." to access methods with ReadLabel.
-// * `{"In": {"...": "RW"}, "NotIn": {"veyron/alice": "W"}}` encodes an ACL that allows all principals
-// access to all ReadLabel or WriteLabel methods, EXCEPT that methods with a WriteLabel are not
-// accessible to veyron/alice and her delegates.
-// (Also see BlessingPattern.MatchedBy)
-//
-// TODO(ataly, ashankar): Instead of reading the file on each call we should use the "inotify"
-// mechanism to watch the file. Eventually we should also support ACLs stored in the Veyron
-// store.
-func NewFileACLAuthorizer(filePath string) security.Authorizer { return fileACLAuthorizer(filePath) }
-
-func matchesACL(blessings []string, label security.Label, acl security.ACL) error {
- if len(blessings) == 0 && acl.CanAccess("", label) {
- // No blessings, check if that satisfies the ACL (it will be if AllPrincipals appears in the ACL).
- return nil
- }
- for _, b := range blessings {
- if acl.CanAccess(b, label) {
- return nil
- }
- }
- return errACL
-}
-
-func loadACLFromFile(filePath string) (security.ACL, error) {
- f, err := os.Open(filePath)
- if err != nil {
- return nullACL, err
- }
- defer f.Close()
- return LoadACL(f)
-}
diff --git a/security/acl_authorizer_test.go b/security/acl_authorizer_test.go
deleted file mode 100644
index 98b0e2a..0000000
--- a/security/acl_authorizer_test.go
+++ /dev/null
@@ -1,238 +0,0 @@
-package security
-
-import (
- "io/ioutil"
- "os"
- "runtime"
- "testing"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-func saveACLToTempFile(acl security.ACL) string {
- f, err := ioutil.TempFile("", "saved_acl")
- if err != nil {
- panic(err)
- }
- defer f.Close()
- if err := SaveACL(f, acl); err != nil {
- defer os.Remove(f.Name())
- panic(err)
- }
- return f.Name()
-}
-
-func updateACLInFile(fileName string, acl security.ACL) {
- f, err := os.OpenFile(fileName, os.O_WRONLY, 0600)
- if err != nil {
- panic(err)
- }
- defer f.Close()
- if err := SaveACL(f, acl); err != nil {
- panic(err)
- }
-}
-
-func testSelfRPCs(t *testing.T, authorizer security.Authorizer) {
- _, file, line, _ := runtime.Caller(1)
- var (
- pserver, server = newPrincipal("server")
- _, imposter = newPrincipal("server")
- palice, alice = newPrincipal("alice")
- aliceServer = bless(palice, pserver, alice, "server")
-
- ctxp = &security.ContextParams{LocalPrincipal: pserver, LocalBlessings: server}
- tests = []struct {
- remote security.Blessings
- isAuthorized bool
- }{
- {server, true},
- {imposter, false},
- // A principal talking to itself (even if with a different blessing) is authorized.
- // TODO(ashankar,ataly): Is this a desired property?
- {aliceServer, true},
- }
- )
- for _, test := range tests {
- ctxp.RemoteBlessings = test.remote
- ctx := security.NewContext(ctxp)
- if got, want := authorizer.Authorize(ctx), test.isAuthorized; (got == nil) != want {
- t.Errorf("%s:%d: %+v.Authorize(%v) returned error: %v, want error: %v", file, line, authorizer, ctx, got, !want)
- }
- }
-}
-
-func testNothingPermitted(t *testing.T, authorizer security.Authorizer) {
- _, file, line, _ := runtime.Caller(1)
- var (
- pserver, server = newPrincipal("server")
- palice, alice = newPrincipal("alice")
- pbob, bob = newPrincipal("random")
-
- serverAlice = bless(pserver, palice, server, "alice")
- serverAliceFriend = bless(palice, pbob, serverAlice, "friend")
- serverBob = bless(pserver, pbob, server, "bob")
-
- users = []security.Blessings{
- // blessings not recognized by "server" (since they are rooted at public
- // keys not recognized as roots by pserver)
- alice,
- bob,
- // blessings recognized by "server" (since they are its delegates)
- serverAlice,
- serverAliceFriend,
- serverBob,
- }
-
- invalidLabel = security.Label(3)
- )
- // No principal (other than the server itself - self-RPCs are allowed) should have access to any
- // valid or invalid label.
- for _, u := range users {
- for _, l := range security.ValidLabels {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: u,
- MethodTags: []interface{}{l},
- })
- if got := authorizer.Authorize(ctx); got == nil {
- t.Errorf("%s:%d: %+v.Authorize(%v) returns nil, want error", file, line, authorizer, ctx)
- }
- }
-
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: u,
- MethodTags: []interface{}{invalidLabel},
- })
- if got := authorizer.Authorize(ctx); got == nil {
- t.Errorf("%s:%d: %+v.Authorize(%v) returns nil, want error", file, line, authorizer, ctx)
- }
- }
-}
-
-func TestACLAuthorizer(t *testing.T) {
- const (
- // Shorthands
- R = security.ReadLabel
- W = security.WriteLabel
- A = security.AdminLabel
- D = security.DebugLabel
- M = security.MonitoringLabel
- X = security.ResolveLabel
- )
- type Expectations map[security.Blessings]security.LabelSet
- // Principals to test
- var (
- // Principals
- pserver, server = newPrincipal("server")
- palice, alice = newPrincipal("alice")
- pbob, bob = newPrincipal("bob")
- pche, che = newPrincipal("che")
-
- // Blessings from the server
- serverAlice = bless(pserver, palice, server, "alice")
- serverBob = bless(pserver, pbob, server, "bob")
- serverChe = bless(pserver, pche, server, "che")
- serverAliceFriend = bless(palice, pbob, serverAlice, "friend")
- serverCheFriend = bless(pche, pbob, serverChe, "friend")
-
- authorizer security.Authorizer // the authorizer to test.
-
- runTests = func(expectations Expectations) {
- _, file, line, _ := runtime.Caller(1)
- for user, labels := range expectations {
- for _, l := range security.ValidLabels {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: user,
- MethodTags: []interface{}{l},
- })
- if got, want := authorizer.Authorize(ctx), labels.HasLabel(l); (got == nil) != want {
- t.Errorf("%s:%d: %+v.Authorize(%v) returned error: %v, want error: %v", file, line, authorizer, ctx, got, !want)
- }
- }
- }
- }
- )
- // Convenience function for combining Labels into a LabelSet.
- LS := func(labels ...security.Label) security.LabelSet {
- var ret security.LabelSet
- for _, l := range labels {
- ret = ret | security.LabelSet(l)
- }
- return ret
- }
-
- // ACL for testing
- acl := security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "...": LS(R),
- "server/alice/...": LS(W, R),
- "server/alice": LS(A, D, M),
- "server/bob": LS(D, M),
- "server/che/...": LS(W, R),
- "server/che": LS(W, R),
- },
- NotIn: map[string]security.LabelSet{
- "server/che/friend": LS(W),
- },
- }
-
- // Authorizations for the above ACL.
- expectations := Expectations{
- alice: LS(R), // "..." ACL entry.
- bob: LS(R), // "..." ACL entry.
- che: LS(R), // "..." ACL entry.
- server: security.AllLabels, // self RPC
- serverAlice: LS(R, W, A, D, M), // "server/alice/..." ACL entry
- serverBob: LS(R, D, M), // "..." and "server/bob" ACL entries
- serverAliceFriend: LS(W, R), // "server/alice/..." ACL entry.
- serverChe: LS(W, R), // "server/che" ACL entry.
- serverCheFriend: LS(R), // "server/che/..." ACL entry, with the "server/che/friend" NotIn exception.
- nil: LS(R), // No blessings presented, same authorizations as "..." ACL entry.
- }
- // Create an aclAuthorizer based on the ACL and verify the authorizations.
- authorizer = NewACLAuthorizer(acl)
- runTests(expectations)
- testSelfRPCs(t, authorizer)
-
- // Create a fileACLAuthorizer by saving the ACL in a file, and verify the
- // authorizations.
- fileName := saveACLToTempFile(acl)
- defer os.Remove(fileName)
- authorizer = NewFileACLAuthorizer(fileName)
- runTests(expectations)
- testSelfRPCs(t, authorizer)
-
- // Modify the ACL stored in the file and verify that the authorizations appropriately
- // change for the fileACLAuthorizer.
- acl.In["server/bob"] = LS(R, W, A, D, M)
- expectations[serverBob] = LS(R, W, A, D, M)
- updateACLInFile(fileName, acl)
- runTests(expectations)
- testSelfRPCs(t, authorizer)
-
- // Update the ACL file with invalid contents and verify that no requests are
- // authorized.
- f, err := os.OpenFile(fileName, os.O_WRONLY, 0600)
- if err != nil {
- panic(err)
- }
- f.Write([]byte("invalid ACL"))
- f.Close()
- testNothingPermitted(t, authorizer)
-}
-
-func TestFileACLAuthorizerOnNonExistentFile(t *testing.T) {
- testNothingPermitted(t, NewFileACLAuthorizer("fileDoesNotExist"))
-}
-
-func TestNilACLAuthorizer(t *testing.T) {
- authorizer := NewACLAuthorizer(nullACL)
- testNothingPermitted(t, authorizer)
- testSelfRPCs(t, authorizer)
-}
diff --git a/security/agent/pingpong/main.go b/security/agent/pingpong/main.go
index e09ef7b..dcc38d0 100644
--- a/security/agent/pingpong/main.go
+++ b/security/agent/pingpong/main.go
@@ -6,7 +6,6 @@
"veyron.io/veyron/veyron/lib/signals"
_ "veyron.io/veyron/veyron/profiles"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/rt"
@@ -51,10 +50,7 @@
log.Fatal("error listening to service: ", err)
}
- auth := vsecurity.NewACLAuthorizer(security.ACL{In: map[security.BlessingPattern]security.LabelSet{
- security.AllPrincipals: security.AllLabels,
- }})
- if err := s.Serve("pingpong", serverPong, auth); err != nil {
+ if err := s.Serve("pingpong", serverPong, allowEveryone{}); err != nil {
log.Fatal("error serving service: ", err)
}
@@ -62,6 +58,10 @@
<-signals.ShutdownOnSignals()
}
+type allowEveryone struct{}
+
+func (allowEveryone) Authorize(security.Context) error { return nil }
+
func main() {
flag.Parse()
if *runServer {
diff --git a/security/flag/flag.go b/security/flag/flag.go
index 91b54c2..cd1086e 100644
--- a/security/flag/flag.go
+++ b/security/flag/flag.go
@@ -7,14 +7,13 @@
"errors"
"flag"
- vsecurity "veyron.io/veyron/veyron/security"
-
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
)
var (
- acl = flag.String("acl", "", `acl is an optional JSON-encoded security.ACL that is used to construct a security.Authorizer. Example: {"In":{"veyron/alice/...":"RW"}} is a JSON-encoded ACL that allows all delegates of "veyron/alice" to access all methods with ReadLabel or WriteLabel. If this flag is provided then the \"--acl_file\" must be absent.`)
- aclFile = flag.String("acl_file", "", "acl_file is an optional path to a file containing a JSON-encoded security.ACL that is used to construct a security.Authorizer. If this flag is provided then the \"--acl_file\" flag must be absent.")
+ acl = flag.String("acl", "", `acl is an optional JSON-encoded access.TaggedACLMap that is used to construct a security.Authorizer using the tags defined in the access package. Example: {"Read": {"In": ["veyron/alice/..."]}} allows all delegates of "veyron/alice" to access all methods with the "Read" access tag on them. If this flag is set then the --acl_file flag must not be set.`)
+ aclFile = flag.String("acl_file", "", "acl_file is an optional path to a file containing a JSON-encoded access.TaggedACLMap that is used to construct a security.Authorizer (with the set of tags defined in the access package). If this flag is set then --acl must not be set")
)
// NewAuthorizerOrDie constructs an Authorizer based on the provided "--acl" or
@@ -28,12 +27,18 @@
if len(*acl) != 0 && len(*aclFile) != 0 {
panic(errors.New("only one of the flags \"--acl\" or \"--acl_file\" must be provided"))
}
+ var a security.Authorizer
+ var err error
if len(*aclFile) != 0 {
- return vsecurity.NewFileACLAuthorizer(*aclFile)
+ a, err = access.TaggedACLAuthorizerFromFile(*aclFile, access.TypicalTagType())
+ } else {
+ var tam access.TaggedACLMap
+ if tam, err = access.ReadTaggedACLMap(bytes.NewBufferString(*acl)); err == nil {
+ a, err = access.TaggedACLAuthorizer(tam, access.TypicalTagType())
+ }
}
- a, err := vsecurity.LoadACL(bytes.NewBufferString(*acl))
if err != nil {
panic(err)
}
- return vsecurity.NewACLAuthorizer(a)
+ return a
}
diff --git a/security/flag/flag_test.go b/security/flag/flag_test.go
index de56a92..0da0bde 100644
--- a/security/flag/flag_test.go
+++ b/security/flag/flag_test.go
@@ -7,9 +7,9 @@
"testing"
tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
)
func TestNewAuthorizerOrDie(t *testing.T) {
@@ -29,11 +29,22 @@
flag.Set("acl_file", "")
}
var (
- acl1 = security.ACL{}
- acl2 = security.ACL{In: map[security.BlessingPattern]security.LabelSet{
- "veyron/alice": security.LabelSet(security.ReadLabel | security.WriteLabel),
- "veyron/bob": security.LabelSet(security.ReadLabel),
- }}
+ acl1 = access.TaggedACLMap{}
+ acl2 = access.TaggedACLMap{
+ string(access.Read): access.ACL{
+ In: []security.BlessingPattern{"veyron/alice", "veyron/bob"},
+ },
+ string(access.Write): access.ACL{
+ In: []security.BlessingPattern{"veyron/alice"},
+ },
+ }
+
+ auth = func(a security.Authorizer, err error) security.Authorizer {
+ if err != nil {
+ panic(err)
+ }
+ return a
+ }
)
acl2File := tsecurity.SaveACLToFile(acl2)
defer os.Remove(acl2File)
@@ -49,19 +60,19 @@
},
{
flags: flagValue{"acl": "{}"},
- wantAuth: vsecurity.NewACLAuthorizer(acl1),
+ wantAuth: auth(access.TaggedACLAuthorizer(acl1, access.TypicalTagType())),
},
{
flags: flagValue{"acl": `{"In":{"veyron/alice":"RW", "veyron/bob": "R"}}`},
- wantAuth: vsecurity.NewACLAuthorizer(acl2),
+ wantAuth: auth(access.TaggedACLAuthorizer(acl2, access.TypicalTagType())),
},
{
flags: flagValue{"acl": `{"In":{"veyron/bob":"R", "veyron/alice": "WR"}}`},
- wantAuth: vsecurity.NewACLAuthorizer(acl2),
+ wantAuth: auth(access.TaggedACLAuthorizer(acl2, access.TypicalTagType())),
},
{
flags: flagValue{"acl_file": acl2File},
- wantAuth: vsecurity.NewFileACLAuthorizer(acl2File),
+ wantAuth: auth(access.TaggedACLAuthorizerFromFile(acl2File, access.TypicalTagType())),
},
{
flags: flagValue{"acl_file": acl2File, "acl": `{"In":{"veyron/alice":"RW", "veyron/bob": "R"}}`},
diff --git a/security/testdata/blessingstore.sig b/security/testdata/blessingstore.sig
index 1040fb8..b06ba85 100644
--- a/security/testdata/blessingstore.sig
+++ b/security/testdata/blessingstore.sig
Binary files differ
diff --git a/security/util.go b/security/util.go
index ea9df1c..1ce53ef 100644
--- a/security/util.go
+++ b/security/util.go
@@ -6,7 +6,6 @@
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
- "encoding/json"
"encoding/pem"
"errors"
"fmt"
@@ -19,15 +18,6 @@
const ecPrivateKeyPEMType = "EC PRIVATE KEY"
-var nullACL security.ACL
-
-// OpenACL creates an ACL that grants access to all principals.
-func OpenACL() security.ACL {
- acl := security.ACL{}
- acl.In = map[security.BlessingPattern]security.LabelSet{security.AllPrincipals: security.AllLabels}
- return acl
-}
-
var PassphraseErr = errors.New("passphrase incorrect for decrypting private key")
// NewPrincipalKey generates an ECDSA (public, private) key pair.
@@ -104,20 +94,6 @@
return pem.Encode(w, pemKey)
}
-// LoadACL reads an ACL from the provided Reader containing a JSON encoded ACL.
-func LoadACL(r io.Reader) (security.ACL, error) {
- var acl security.ACL
- if err := json.NewDecoder(r).Decode(&acl); err != nil {
- return nullACL, err
- }
- return acl, nil
-}
-
-// SaveACL encodes an ACL in JSON format and writes it to the provided Writer.
-func SaveACL(w io.Writer, acl security.ACL) error {
- return json.NewEncoder(w).Encode(acl)
-}
-
// ThirdPartyCaveats returns the set of security.ThirdPartyCaveats
// that could be successfully decoded from the provided caveat bytes.
func ThirdPartyCaveats(caveats ...security.Caveat) []security.ThirdPartyCaveat {
diff --git a/security/util_test.go b/security/util_test.go
index 2274a70..6d07a22 100644
--- a/security/util_test.go
+++ b/security/util_test.go
@@ -65,31 +65,6 @@
}
}
-func TestLoadSaveACL(t *testing.T) {
- acl := security.ACL{}
- acl.In = map[security.BlessingPattern]security.LabelSet{
- "veyron/...": security.LabelSet(security.ReadLabel),
- "veyron/alice": security.LabelSet(security.ReadLabel | security.WriteLabel),
- "veyron/bob": security.LabelSet(security.AdminLabel),
- }
- acl.NotIn = map[string]security.LabelSet{
- "veyron/che": security.LabelSet(security.ReadLabel),
- }
-
- var buf bytes.Buffer
- if err := SaveACL(&buf, acl); err != nil {
- t.Fatalf("Failed to save ACL %q: %v", acl, err)
- }
-
- loadedACL, err := LoadACL(&buf)
- if err != nil {
- t.Fatalf("Failed to load ACL: %v", err)
- }
- if !reflect.DeepEqual(loadedACL, acl) {
- t.Fatalf("Got ACL %v, but want %v", loadedACL, acl)
- }
-}
-
// fpCaveat implements security.CaveatValidator.
type fpCaveat struct{}
diff --git a/services/mgmt/node/impl/app_service.go b/services/mgmt/node/impl/app_service.go
index 5a278aa..dff4976 100644
--- a/services/mgmt/node/impl/app_service.go
+++ b/services/mgmt/node/impl/app_service.go
@@ -135,6 +135,7 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/appcycle"
"veyron.io/veyron/veyron2/services/mgmt/application"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
@@ -205,7 +206,7 @@
uat BlessingSystemAssociationStore
locks aclLocks
// Reference to the nodemanager top-level ACL list.
- nodeACL security.ACL
+ nodeACL access.TaggedACLMap
// securityAgent holds state related to the security agent (nil if not
// using the agent).
securityAgent *securityAgentState
@@ -363,23 +364,14 @@
return versionDir, updateLink(versionDir, filepath.Join(installationDir, "current"))
}
-// TODO(rjkroege): Refactor this code with the intance creation code.
-func initializeInstallationACLs(dir string, blessings []string, acl security.ACL) error {
- // Start out with the claimant's ACLs and add the invoker's blessings.
-
- var labels security.LabelSet
- if acl.In == nil {
- // The acl.In will be empty for an unclaimed node manager. In this case,
- // create it.
- acl.In = make(map[security.BlessingPattern]security.LabelSet)
+// TODO(rjkroege): Refactor this code with the instance creation code.
+func initializeInstallationACLs(dir string, blessings []string, acl access.TaggedACLMap) error {
+ // Add the invoker's blessings.
+ for _, b := range blessings {
+ for _, tag := range access.AllTypicalTags() {
+ acl.Add(security.BlessingPattern(b), string(tag))
+ }
}
- labels = security.AllLabels
-
- for _, name := range blessings {
- // TODO(rjkroege): Use custom labels.
- acl.In[security.BlessingPattern(name)] = labels
- }
-
aclDir := path.Join(dir, "acls")
aclData := path.Join(aclDir, "data")
aclSig := path.Join(aclDir, "signature")
@@ -416,7 +408,10 @@
return "", err
}
- if err := initializeInstallationACLs(installationDir, call.RemoteBlessings().ForContext(call), i.nodeACL); err != nil {
+ // TODO(caprita,rjkroege): Should the installation ACLs really be
+ // seeded with the node ACL? Instead, might want to hide the nodeACL
+ // from the app?
+ if err := initializeInstallationACLs(installationDir, call.RemoteBlessings().ForContext(call), i.nodeACL.Copy()); err != nil {
return "", err
}
deferrer = nil
@@ -603,23 +598,15 @@
return nil
}
-func initializeInstanceACLs(instanceDir string, blessings []string, acl security.ACL) error {
- if acl.In == nil {
- // The acl.In will be empty for an unclaimed node manager. In this case,
- // create it
- acl.In = make(map[security.BlessingPattern]security.LabelSet)
+func initializeInstanceACLs(instanceDir string, blessings []string, acl access.TaggedACLMap) error {
+ for _, b := range blessings {
+ for _, tag := range access.AllTypicalTags() {
+ acl.Add(security.BlessingPattern(b), string(tag))
+ }
}
-
- labels := security.AllLabels
- for _, name := range blessings {
- // TODO(rjkroege): Use custom labels.
- acl.In[security.BlessingPattern(name)] = labels
- }
-
aclDir := path.Join(instanceDir, "acls")
aclData := path.Join(aclDir, "data")
aclSig := path.Join(aclDir, "signature")
-
return writeACLs(aclData, aclSig, aclDir, acl)
}
@@ -663,7 +650,7 @@
return instanceDir, instanceID, err
}
- if err := initializeInstanceACLs(instanceDir, call.RemoteBlessings().ForContext(call), i.nodeACL); err != nil {
+ if err := initializeInstanceACLs(instanceDir, call.RemoteBlessings().ForContext(call), i.nodeACL.Copy()); err != nil {
return instanceDir, instanceID, err
}
return instanceDir, instanceID, nil
@@ -1260,7 +1247,7 @@
}
// TODO(rjkroege): Consider maintaining an in-memory ACL cache.
-func (i *appService) SetACL(_ ipc.ServerContext, acl security.ACL, etag string) error {
+func (i *appService) SetACL(_ ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
dir, err := dirFromSuffix(i.suffix, i.config.Root)
if err != nil {
return err
@@ -1268,10 +1255,10 @@
return setAppACL(i.locks, dir, acl, etag)
}
-func (i *appService) GetACL(_ ipc.ServerContext) (acl security.ACL, etag string, err error) {
+func (i *appService) GetACL(_ ipc.ServerContext) (acl access.TaggedACLMap, etag string, err error) {
dir, err := dirFromSuffix(i.suffix, i.config.Root)
if err != nil {
- return security.ACL{}, "", err
+ return nil, "", err
}
return getAppACL(i.locks, dir)
}
diff --git a/services/mgmt/node/impl/dispatcher.go b/services/mgmt/node/impl/dispatcher.go
index 47d5092..deeddcd 100644
--- a/services/mgmt/node/impl/dispatcher.go
+++ b/services/mgmt/node/impl/dispatcher.go
@@ -12,7 +12,6 @@
"strings"
"sync"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron/security/agent"
"veyron.io/veyron/veyron/security/agent/keymgr"
vflag "veyron.io/veyron/veyron/security/flag"
@@ -48,7 +47,7 @@
type dispatcher struct {
// acl/auth hold the acl and authorizer used to authorize access to the
// node manager methods.
- acl security.ACL
+ acl access.TaggedACLMap
auth security.Authorizer
// etag holds the version string for the ACL. We use this for optimistic
// concurrency control when clients update the ACLs for the node manager.
@@ -124,7 +123,7 @@
if err != nil {
return nil, fmt.Errorf("failed to read nodemanager ACL file:%v", err)
}
- acl, err := vsecurity.LoadACL(reader)
+ acl, err := access.ReadTaggedACLMap(reader)
if err != nil {
return nil, fmt.Errorf("failed to load nodemanager ACL:%v", err)
}
@@ -135,7 +134,7 @@
if d.auth = vflag.NewAuthorizerOrDie(); d.auth == nil {
// If there are no specified ACLs we grant nodemanager access to all
// principals until it is claimed.
- d.auth = vsecurity.NewACLAuthorizer(vsecurity.OpenACL())
+ d.auth = allowEveryone{}
}
}
// If we're in 'security agent mode', set up the key manager agent.
@@ -167,9 +166,11 @@
rt.R().Principal().BlessingStore().Set(proof, security.AllPrincipals)
rt.R().Principal().BlessingStore().SetDefault(proof)
// Create ACLs to transfer nodemanager permissions to the provided identity.
- acl := security.ACL{In: make(map[security.BlessingPattern]security.LabelSet)}
- for _, name := range names {
- acl.In[security.BlessingPattern(name)] = security.AllLabels
+ acl := make(access.TaggedACLMap)
+ for _, n := range names {
+ for _, tag := range access.AllTypicalTags() {
+ acl.Add(security.BlessingPattern(n), string(tag))
+ }
}
_, etag, err := d.getACL()
if err != nil {
@@ -184,7 +185,7 @@
}
// TODO(rjkroege): Further refactor ACL-setting code.
-func setAppACL(locks aclLocks, dir string, acl security.ACL, etag string) error {
+func setAppACL(locks aclLocks, dir string, acl access.TaggedACLMap, etag string) error {
aclpath := path.Join(dir, "acls", "data")
sigpath := path.Join(dir, "acls", "signature")
@@ -204,9 +205,9 @@
}
defer f.Close()
- curACL, err := vsecurity.LoadACL(f)
+ curACL, err := access.ReadTaggedACLMap(f)
if err != nil {
- vlog.Errorf("LoadACL(%s) failed: %v", aclpath, err)
+ vlog.Errorf("ReadTaggedACLMap(%s) failed: %v", aclpath, err)
return err
}
curEtag, err := computeEtag(curACL)
@@ -222,7 +223,7 @@
return writeACLs(aclpath, sigpath, dir, acl)
}
-func getAppACL(locks aclLocks, dir string) (security.ACL, string, error) {
+func getAppACL(locks aclLocks, dir string) (access.TaggedACLMap, string, error) {
aclpath := path.Join(dir, "acls", "data")
// Acquire lock. Locks are per path to an acls file.
@@ -236,30 +237,26 @@
f, err := os.Open(aclpath)
if err != nil {
- vlog.Errorf("LoadACL(%s) failed: %v", aclpath, err)
- return security.ACL{}, "", err
+ vlog.Errorf("Open(%s) failed: %v", aclpath, err)
+ return nil, "", err
}
defer f.Close()
- acl, err := vsecurity.LoadACL(f)
+ acl, err := access.ReadTaggedACLMap(f)
if err != nil {
- vlog.Errorf("LoadACL(%s) failed: %v", aclpath, err)
- return security.ACL{}, "", err
+ vlog.Errorf("ReadTaggedACLMap(%s) failed: %v", aclpath, err)
+ return nil, "", err
}
curEtag, err := computeEtag(acl)
if err != nil {
- return security.ACL{}, "", err
- }
-
- if err != nil {
- return security.ACL{}, "", err
+ return nil, "", err
}
return acl, curEtag, nil
}
-func computeEtag(acl security.ACL) (string, error) {
+func computeEtag(acl access.TaggedACLMap) (string, error) {
b := new(bytes.Buffer)
- if err := vsecurity.SaveACL(b, acl); err != nil {
+ if err := acl.WriteTo(b); err != nil {
vlog.Errorf("Failed to save ACL:%v", err)
return "", err
}
@@ -269,7 +266,7 @@
return etag, nil
}
-func writeACLs(aclFile, sigFile, dir string, acl security.ACL) error {
+func writeACLs(aclFile, sigFile, dir string, acl access.TaggedACLMap) error {
// Create dir directory if it does not exist
os.MkdirAll(dir, os.FileMode(0700))
// Save the object to temporary data and signature files, and then move
@@ -291,7 +288,7 @@
vlog.Errorf("Failed to create NewSigningWriteCloser:%v", err)
return errOperationFailed
}
- if err = vsecurity.SaveACL(writer, acl); err != nil {
+ if err = acl.WriteTo(writer); err != nil {
vlog.Errorf("Failed to SaveACL:%v", err)
return errOperationFailed
}
@@ -308,7 +305,7 @@
return nil
}
-func (d *dispatcher) setACL(acl security.ACL, etag string, writeToFile bool) error {
+func (d *dispatcher) setACL(acl access.TaggedACLMap, etag string, writeToFile bool) error {
d.mu.Lock()
defer d.mu.Unlock()
aclFile, sigFile, nodedata := d.getACLFilePaths()
@@ -326,11 +323,15 @@
if err != nil {
return err
}
- d.acl, d.etag, d.auth = acl, etag, vsecurity.NewACLAuthorizer(acl)
+ auth, err := access.TaggedACLAuthorizer(acl, access.TypicalTagType())
+ if err != nil {
+ return err
+ }
+ d.acl, d.etag, d.auth = acl, etag, auth
return nil
}
-func (d *dispatcher) getACL() (acl security.ACL, etag string, err error) {
+func (d *dispatcher) getACL() (acl access.TaggedACLMap, etag string, err error) {
d.mu.RLock()
defer d.mu.RUnlock()
return d.acl, d.etag, nil
@@ -384,18 +385,15 @@
if !instanceStateIs(appInstanceDir, started) {
return nil, nil, errInvalidSuffix
}
- var label security.Label
var sigStub signatureStub
if kind == "pprof" {
- label = security.DebugLabel
sigStub = pprof.PProfServer(nil)
} else {
- label = security.DebugLabel | security.MonitoringLabel
sigStub = stats.StatsServer(nil)
}
suffix := naming.Join("__debug", naming.Join(components[4:]...))
remote := naming.JoinAddressName(info.AppCycleMgrName, suffix)
- return &proxyInvoker{remote, label, sigStub}, d.auth, nil
+ return &proxyInvoker{remote, access.Debug, sigStub}, d.auth, nil
}
}
nodeACLs, _, err := d.getACL()
@@ -445,23 +443,27 @@
return sec, nil
}
// Otherwise, we require a per-installation and per-instance ACL file.
-
if len(suffix) == 2 {
p, err := installationDirCore(suffix, config.Root)
if err != nil {
vlog.Errorf("newAppSpecificAuthorizer failed: %v", err)
return nil, err
}
- p = path.Join(p, "acls", "data")
- return vsecurity.NewFileACLAuthorizer(p), nil
- } else if len(suffix) > 2 {
+ return access.TaggedACLAuthorizerFromFile(path.Join(p, "acls", "data"), access.TypicalTagType())
+ }
+ if len(suffix) > 2 {
p, err := instanceDir(config.Root, suffix[0:3])
if err != nil {
vlog.Errorf("newAppSpecificAuthorizer failed: %v", err)
return nil, err
}
- p = path.Join(p, "acls", "data")
- return vsecurity.NewFileACLAuthorizer(p), nil
+ return access.TaggedACLAuthorizerFromFile(path.Join(p, "acls", "data"), access.TypicalTagType())
}
return nil, errInvalidSuffix
}
+
+// allowEveryone implements the authorization policy that allows all principals
+// access.
+type allowEveryone struct{}
+
+func (allowEveryone) Authorize(security.Context) error { return nil }
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 170d8cc..06eb18a 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -37,6 +37,7 @@
"veyron.io/veyron/veyron2/services/mgmt/node"
"veyron.io/veyron/veyron2/services/mgmt/pprof"
"veyron.io/veyron/veyron2/services/mgmt/stats"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron2/vom"
@@ -46,7 +47,6 @@
"veyron.io/veyron/veyron/lib/signals"
"veyron.io/veyron/veyron/lib/testutil"
tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron/services/mgmt/node/config"
"veyron.io/veyron/veyron/services/mgmt/node/impl"
suidhelper "veyron.io/veyron/veyron/services/mgmt/suidhelper/impl"
@@ -833,10 +833,13 @@
if err := nodeStub.Claim(selfRT.NewContext(), &granter{p: selfRT.Principal(), extension: "mydevice"}); err != nil {
t.Fatal(err)
}
- expectedACL := security.ACL{In: map[security.BlessingPattern]security.LabelSet{"root/self/mydevice": security.AllLabels}}
+ expectedACL := make(access.TaggedACLMap)
+ for _, tag := range access.AllTypicalTags() {
+ expectedACL[string(tag)] = access.ACL{In: []security.BlessingPattern{"root/self/mydevice"}}
+ }
var b bytes.Buffer
- if err := vsecurity.SaveACL(&b, expectedACL); err != nil {
- t.Fatalf("Failed to saveACL:%v", err)
+ if err := expectedACL.WriteTo(&b); err != nil {
+ t.Fatalf("Failed to save ACL:%v", err)
}
md5hash := md5.Sum(b.Bytes())
expectedETAG := hex.EncodeToString(md5hash[:])
@@ -850,7 +853,10 @@
if err := tryInstall(otherRT); err == nil {
t.Fatalf("Install should have failed with random identity")
}
- newACL := security.ACL{In: map[security.BlessingPattern]security.LabelSet{"root/other": security.AllLabels}}
+ newACL := make(access.TaggedACLMap)
+ for _, tag := range access.AllTypicalTags() {
+ newACL.Add("root/other", string(tag))
+ }
if err := nodeStub.SetACL(selfRT.NewContext(), newACL, "invalid"); err == nil {
t.Fatalf("SetACL should have failed with invalid etag")
}
@@ -1282,7 +1288,7 @@
if err != nil {
t.Fatalf("GetACL failed %v", err)
}
- newACL.In["root/other/..."] = security.LabelSet(security.WriteLabel)
+ newACL.Add("root/other", string(access.Write))
if err := nodeStub.SetACL(selfRT.NewContext(), newACL, ""); err != nil {
t.Fatalf("SetACL failed %v", err)
}
@@ -1299,7 +1305,7 @@
if err != nil {
t.Fatalf("GetACL on appID: %v failed %v", appID, err)
}
- newACL.In["root/other/..."] = security.LabelSet(security.ReadLabel)
+ newACL.Add("root/other", string(access.Read))
if err = appStub(appID).SetACL(selfRT.NewContext(), newACL, ""); err != nil {
t.Fatalf("SetACL on appID: %v failed: %v", appID, err)
}
diff --git a/services/mgmt/node/impl/node_service.go b/services/mgmt/node/impl/node_service.go
index 98fb0c0..0d4799a 100644
--- a/services/mgmt/node/impl/node_service.go
+++ b/services/mgmt/node/impl/node_service.go
@@ -43,10 +43,10 @@
"veyron.io/veyron/veyron2/mgmt"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
- "veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/application"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/node"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/vlog"
vexec "veyron.io/veyron/veyron/lib/exec"
@@ -436,11 +436,11 @@
return nil
}
-func (i *nodeService) SetACL(_ ipc.ServerContext, acl security.ACL, etag string) error {
+func (i *nodeService) SetACL(_ ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
return i.disp.setACL(acl, etag, true /* store ACL on disk */)
}
-func (i *nodeService) GetACL(_ ipc.ServerContext) (acl security.ACL, etag string, err error) {
+func (i *nodeService) GetACL(_ ipc.ServerContext) (acl access.TaggedACLMap, etag string, err error) {
return i.disp.getACL()
}
diff --git a/services/mgmt/node/impl/proxy_invoker.go b/services/mgmt/node/impl/proxy_invoker.go
index 33914e7..1f8cf98 100644
--- a/services/mgmt/node/impl/proxy_invoker.go
+++ b/services/mgmt/node/impl/proxy_invoker.go
@@ -6,7 +6,7 @@
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/rt"
- "veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
)
// proxyInvoker is an ipc.Invoker implementation that proxies all requests
@@ -14,11 +14,11 @@
// <remote> transparently.
//
// remote is the name of the remote object.
-// label is the security label required to access this object.
+// access is the access tag require to access the object.
// sigStub is used to get the signature of the remote object.
type proxyInvoker struct {
remote string
- label security.Label
+ access access.Tag
sigStub signatureStub
}
@@ -34,7 +34,7 @@
var x interface{}
argptrs[i] = &x
}
- tags = []interface{}{p.label}
+ tags = []interface{}{p.access}
return
}
diff --git a/services/mgmt/node/impl/proxy_invoker_test.go b/services/mgmt/node/impl/proxy_invoker_test.go
index 0fcb836..b729d10 100644
--- a/services/mgmt/node/impl/proxy_invoker_test.go
+++ b/services/mgmt/node/impl/proxy_invoker_test.go
@@ -11,6 +11,7 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/stats"
"veyron.io/veyron/veyron2/services/mounttable"
+ "veyron.io/veyron/veyron2/services/security/access"
)
// TODO(toddw): Add tests of Signature and MethodSignature.
@@ -45,7 +46,6 @@
}
disp := &proxyDispatcher{
naming.JoinAddressName(ep1.String(), "__debug/stats"),
- security.Label(security.AllLabels),
stats.StatsServer(nil),
}
if err := server2.ServeDispatcher("", disp); err != nil {
@@ -97,10 +97,9 @@
type proxyDispatcher struct {
remote string
- label security.Label
sigStub signatureStub
}
func (d *proxyDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
- return &proxyInvoker{naming.Join(d.remote, suffix), d.label, d.sigStub}, nil, nil
+ return &proxyInvoker{naming.Join(d.remote, suffix), access.Debug, d.sigStub}, nil, nil
}
diff --git a/services/mgmt/repository/repository.vdl b/services/mgmt/repository/repository.vdl
index 3b553b8..ab9acb4 100644
--- a/services/mgmt/repository/repository.vdl
+++ b/services/mgmt/repository/repository.vdl
@@ -4,8 +4,8 @@
import (
"veyron.io/veyron/veyron/services/mgmt/profile"
- "veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/application"
+ "veyron.io/veyron/veyron2/services/security/access"
public "veyron.io/veyron/veyron2/services/mgmt/repository"
)
@@ -17,7 +17,7 @@
// Put adds the given tuple of application version (specified
// through the object name suffix) and application envelope to all
// of the given application profiles.
- Put(Profiles []string, Envelope application.Envelope) error {security.WriteLabel}
+ Put(Profiles []string, Envelope application.Envelope) error {access.Write}
// Remove removes the application envelope for the given profile
// name and application version (specified through the object name
// suffix). If no version is specified as part of the suffix, the
@@ -25,7 +25,7 @@
//
// TODO(jsimsa): Add support for using "*" to specify all profiles
// when Matt implements Globing (or Ken implements querying).
- Remove(Profile string) error {security.WriteLabel}
+ Remove(Profile string) error {access.Write}
}
// Profile describes a profile internally. Besides the public Profile
@@ -34,11 +34,11 @@
public.Profile
// Specification returns the profile specification for the profile
// identified through the object name suffix.
- Specification() (profile.Specification, error) {security.ReadLabel}
+ Specification() (profile.Specification, error) {access.Read}
// Put sets the profile specification for the profile identified
// through the object name suffix.
- Put(Specification profile.Specification) error {security.WriteLabel}
+ Put(Specification profile.Specification) error {access.Write}
// Remove removes the profile specification for the profile
// identified through the object name suffix.
- Remove() error {security.WriteLabel}
+ Remove() error {access.Write}
}
diff --git a/services/mgmt/repository/repository.vdl.go b/services/mgmt/repository/repository.vdl.go
index 028e6f6..48541ae 100644
--- a/services/mgmt/repository/repository.vdl.go
+++ b/services/mgmt/repository/repository.vdl.go
@@ -8,12 +8,12 @@
import (
"veyron.io/veyron/veyron/services/mgmt/profile"
- "veyron.io/veyron/veyron2/security"
-
"veyron.io/veyron/veyron2/services/mgmt/application"
"veyron.io/veyron/veyron2/services/mgmt/repository"
+ "veyron.io/veyron/veyron2/services/security/access"
+
// The non-user imports are prefixed with "__" to prevent collisions.
__veyron2 "veyron.io/veyron/veyron2"
__context "veyron.io/veyron/veyron2/context"
@@ -232,7 +232,7 @@
OutArgs: []__ipc.ArgDesc{
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(4)},
+ Tags: []__vdlutil.Any{access.Tag("Write")},
},
{
Name: "Remove",
@@ -243,7 +243,7 @@
OutArgs: []__ipc.ArgDesc{
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(4)},
+ Tags: []__vdlutil.Any{access.Tag("Write")},
},
},
}
@@ -536,7 +536,7 @@
{"", ``}, // profile.Specification
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(2)},
+ Tags: []__vdlutil.Any{access.Tag("Read")},
},
{
Name: "Put",
@@ -547,7 +547,7 @@
OutArgs: []__ipc.ArgDesc{
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(4)},
+ Tags: []__vdlutil.Any{access.Tag("Write")},
},
{
Name: "Remove",
@@ -555,7 +555,7 @@
OutArgs: []__ipc.ArgDesc{
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(4)},
+ Tags: []__vdlutil.Any{access.Tag("Write")},
},
},
}
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index 5ca2af2..719a321 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -10,13 +10,13 @@
"time"
"veyron.io/veyron/veyron/lib/glob"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mounttable"
+ "veyron.io/veyron/veyron2/services/security/access"
verror "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -58,7 +58,12 @@
children map[string]*node
}
-// NewMountTable creates a new server that uses the default authorization policy.
+// NewMountTable creates a new server that uses the ACLs specified in
+// aclfile for authorization.
+//
+// aclfile is a JSON-encoded mapping from paths in the mounttable to the
+// access.TaggedACLMap for that path. The tags used in the map are the typical
+// access tags (the Tag type defined in veyron2/services/security/access).
func NewMountTable(aclfile string) (*mountTable, error) {
acls, err := parseACLs(aclfile)
if err != nil {
@@ -74,7 +79,7 @@
if path == "" {
return nil, nil
}
- var acls map[string]security.ACL
+ var acls map[string]access.TaggedACLMap
f, err := os.Open(path)
if err != nil {
return nil, err
@@ -85,7 +90,10 @@
}
result := make(map[string]security.Authorizer)
for name, acl := range acls {
- result[name] = vsecurity.NewACLAuthorizer(acl)
+ result[name], err = access.TaggedACLAuthorizer(acl, access.TypicalTagType())
+ if err != nil {
+ return nil, fmt.Errorf("Unable to create ACL for %q: %v", name, err)
+ }
}
if result["/"] == nil {
return nil, fmt.Errorf("No acl for / in %s", path)
@@ -173,7 +181,7 @@
mt.Lock()
acl := mt.acls[name]
mt.Unlock()
- vlog.VI(2).Infof("authorizeStep(%s) %s %s %s", name, c.RemoteBlessings(), c.Label(), acl)
+ vlog.VI(2).Infof("authorizeStep(%v) %v %v %v", name, c.RemoteBlessings(), c.MethodTags(), acl)
if acl != nil {
return acl.Authorize(c)
}
diff --git a/services/mounttable/lib/testdata/noRoot.acl b/services/mounttable/lib/testdata/noRoot.acl
index 0ff2383..b663a07 100644
--- a/services/mounttable/lib/testdata/noRoot.acl
+++ b/services/mounttable/lib/testdata/noRoot.acl
@@ -1,3 +1,6 @@
{
-"/foo/bar": {"fake/root": "RW"},
-}
\ No newline at end of file
+"/foo/bar": {
+ "Read": { "In": ["fake/root"] },
+ "Write": { "In": ["fake/root"] }
+}
+}
diff --git a/services/mounttable/lib/testdata/test.acl b/services/mounttable/lib/testdata/test.acl
index 597dda9..7990532 100644
--- a/services/mounttable/lib/testdata/test.acl
+++ b/services/mounttable/lib/testdata/test.acl
@@ -1,5 +1,14 @@
{
-"/": {"In": {"root": "RW", "...": "R"}},
-"/stuff": {"In": {"root": "RW", "bob": "R"}},
-"/a": {"In": {"root": "RW", "alice": "R"}}
-}
\ No newline at end of file
+"/": {
+ "Read": { "In": ["..."] },
+ "Write": { "In": ["root"] }
+},
+"/stuff": {
+ "Read": { "In": ["bob", "root"] },
+ "Write": { "In": ["root"] }
+},
+"/a": {
+ "Read": { "In": ["alice", "root"] },
+ "Write": { "In": ["root"] }
+}
+}
diff --git a/services/security/discharger.vdl b/services/security/discharger.vdl
index 037c6b4..a7dfbd3 100644
--- a/services/security/discharger.vdl
+++ b/services/security/discharger.vdl
@@ -12,5 +12,5 @@
// respectively. (not enforced here because vdl does not know these types)
// TODO(ataly,ashankar): Figure out a VDL representation for ThirdPartyCaveat
// and Discharge and use those here?
- Discharge(Caveat any, Impetus security.DischargeImpetus) (Discharge any, err error) {security.ReadLabel}
+ Discharge(Caveat any, Impetus security.DischargeImpetus) (Discharge any, err error)
}
diff --git a/services/security/discharger.vdl.go b/services/security/discharger.vdl.go
index 420c1c6..8fbf3c6 100644
--- a/services/security/discharger.vdl.go
+++ b/services/security/discharger.vdl.go
@@ -171,7 +171,6 @@
{"Discharge", ``}, // __vdlutil.Any
{"err", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(2)},
},
},
}
diff --git a/tools/mgmt/nodex/acl_fmt.go b/tools/mgmt/nodex/acl_fmt.go
new file mode 100644
index 0000000..1690edd
--- /dev/null
+++ b/tools/mgmt/nodex/acl_fmt.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "veyron.io/veyron/veyron2/security"
+)
+
+// aclEntries maps blessing patterns to the kind of access they should have.
+type aclEntries map[string]accessTags
+
+// accessTags maps access tags to whether they should be blacklisted
+// (i.e., part of the NotIn list) or not (part of the In list).
+//
+// TODO(ashankar,caprita): This structure is not friendly to a blessing
+// appearing in both the "In" and "NotIn" lists of an ACL. Arguably, such an
+// ACL is silly (In: ["foo"], NotIn: ["foo"]), but is legal. This structure can
+// end up hiding that.
+type accessTags map[string]bool
+
+// String representation of access tags. Between String and parseAccessTags,
+// the "get" and "set" commands are able to speak the same language: the output
+// of "get" and be copied/pasted into "set".
+func (tags accessTags) String() string {
+ // Sort tags and then apply "!".
+ var list []string
+ for tag, _ := range tags {
+ list = append(list, tag)
+ }
+ sort.Strings(list)
+ for ix, tag := range list {
+ if tags[tag] {
+ list[ix] = "!" + list[ix]
+ }
+ }
+ return strings.Join(list, ",")
+}
+
+func parseAccessTags(input string) (accessTags, error) {
+ ret := make(accessTags)
+ if input == "^" {
+ return ret, nil
+ }
+ for _, tag := range strings.Split(input, ",") {
+ blacklist := strings.HasPrefix(tag, "!")
+ if blacklist {
+ tag = tag[1:]
+ }
+ if len(tag) == 0 {
+ return nil, fmt.Errorf("empty access tag in %q", input)
+ }
+ ret[tag] = blacklist
+ }
+ return ret, nil
+}
+
+func (entries aclEntries) String() string {
+ var list []string
+ for pattern, _ := range entries {
+ list = append(list, pattern)
+ }
+ sort.Strings(list)
+ for ix, pattern := range list {
+ list[ix] = fmt.Sprintf("%s %v", pattern, entries[pattern])
+ }
+ return strings.Join(list, "\n")
+}
+
+func (entries aclEntries) Tags(pattern string) accessTags {
+ tags, exists := entries[pattern]
+ if !exists {
+ tags = make(accessTags)
+ entries[pattern] = tags
+ }
+ return tags
+}
+
+type byPattern []security.BlessingPattern
+
+func (a byPattern) Len() int { return len(a) }
+func (a byPattern) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byPattern) Less(i, j int) bool { return a[i] < a[j] }
diff --git a/tools/mgmt/nodex/acl_impl.go b/tools/mgmt/nodex/acl_impl.go
index ca2b0a9..e9d2f7c 100644
--- a/tools/mgmt/nodex/acl_impl.go
+++ b/tools/mgmt/nodex/acl_impl.go
@@ -4,7 +4,6 @@
import (
"fmt"
- "sort"
"veyron.io/lib/cmdline"
"veyron.io/veyron/veyron2/rt"
@@ -25,19 +24,6 @@
application installation or instance.`,
}
-type formattedACLEntry struct {
- blessing string
- inout string
- label string
-}
-
-// Code to make formattedACLEntry sorted.
-type byBlessing []formattedACLEntry
-
-func (a byBlessing) Len() int { return len(a) }
-func (a byBlessing) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a byBlessing) Less(i, j int) bool { return a[i].blessing < a[j].blessing }
-
func runGet(cmd *cmdline.Command, args []string) error {
if expected, got := 1, len(args); expected != got {
return cmd.UsageErrorf("get: incorrect number of arguments, expected %d, got %d", expected, got)
@@ -48,21 +34,17 @@
if err != nil {
return fmt.Errorf("GetACL on %s failed: %v", vanaName, err)
}
-
- // TODO(rjkroege): Update for custom labels.
- output := make([]formattedACLEntry, 0)
- for k, _ := range objACL.In {
- output = append(output, formattedACLEntry{string(k), "", objACL.In[k].String()})
+ // Convert objACL (TaggedACLMap) into aclEntries for pretty printing.
+ entries := make(aclEntries)
+ for tag, acl := range objACL {
+ for _, p := range acl.In {
+ entries.Tags(string(p))[tag] = false
+ }
+ for _, b := range acl.NotIn {
+ entries.Tags(b)[tag] = true
+ }
}
- for k, _ := range objACL.NotIn {
- output = append(output, formattedACLEntry{string(k), "!", objACL.NotIn[k].String()})
- }
-
- sort.Sort(byBlessing(output))
-
- for _, e := range output {
- fmt.Fprintf(cmd.Stdout(), "%s %s%s\n", e.blessing, e.inout, e.label)
- }
+ fmt.Fprintf(cmd.Stdout(), "%v", entries)
return nil
}
@@ -71,32 +53,28 @@
Name: "set",
Short: "Set ACLs for the given target.",
Long: "Set ACLs for the given target",
- ArgsName: "<node manager name> (<blessing> [!]<label>)...",
+ ArgsName: "<node manager name> (<blessing> [!]<tag>(,[!]<tag>)*",
ArgsLong: `
<node manager name> can be a Vanadium name for a node manager,
application installation or instance.
<blessing> is a blessing pattern.
+If the same pattern is repeated multiple times in the command, then
+the only the last occurrence will be honored.
-<label> is a character sequence defining a set of rights: some subset
-of the defined standard Vanadium labels of XRWADM where X is resolve,
-R is read, W for write, A for admin, D for debug and M is for
-monitoring. By default, the combination of <blessing>, <label>
-replaces whatever entry is present in the ACL's In field for the
-<blessing> but it can instead be added to the NotIn field by prefacing
-<label> with a '!' character. Use the <label> of 0 to clear the label.
+<tag> is a subset of defined access types ("Admin", "Read", "Write" etc.).
+If the access right is prefixed with a '!' then <blessing> is added to the
+NotIn list for that right. Using "^" as a "tag" causes all occurrences of
+<blessing> in the current ACL to be cleared.
-For example: root/self !0 will clear the NotIn field for blessingroot/self.`,
-}
+Examples:
+set root/self ^
+will remove "root/self" from the In and NotIn lists for all access rights.
-type inAdditionTuple struct {
- blessing security.BlessingPattern
- ls *security.LabelSet
-}
-
-type notInAdditionTuple struct {
- blessing string
- ls *security.LabelSet
+set root/self Read,!Write
+will add "root/self" to the In list for Read access and the NotIn list
+for Write access (and remove "root/self" from both the In and NotIn
+lists of all other access rights)`,
}
func runSet(cmd *cmdline.Command, args []string) error {
@@ -107,70 +85,32 @@
vanaName := args[0]
pairs := args[1:]
- // Parse each pair and aggregate what should happen to all of them
- notInDeletions := make([]string, 0)
- inDeletions := make([]security.BlessingPattern, 0)
- inAdditions := make([]inAdditionTuple, 0)
- notInAdditions := make([]notInAdditionTuple, 0)
-
+ entries := make(aclEntries)
for i := 0; i < len(pairs); i += 2 {
- blessing, label := pairs[i], pairs[i+1]
- if label == "" || label == "!" {
- return cmd.UsageErrorf("failed to parse LabelSet pair %s, %s", blessing, label)
+ blessing := pairs[i]
+ tags, err := parseAccessTags(pairs[i+1])
+ if err != nil {
+ return cmd.UsageErrorf("failed to parse access tags for %q: %v", blessing, err)
}
-
- switch {
- case label == "!0":
- notInDeletions = append(notInDeletions, blessing)
- case label == "0":
- inDeletions = append(inDeletions, security.BlessingPattern(blessing))
- case label[0] == '!':
- // e.g. !RW
- ls := new(security.LabelSet)
- if err := ls.FromString(label[1:]); err != nil {
- return cmd.UsageErrorf("failed to parse LabelSet %s: %v", label, err)
- }
- notInAdditions = append(notInAdditions, notInAdditionTuple{blessing, ls})
- default:
- // e.g. X
- ls := new(security.LabelSet)
- if err := ls.FromString(label); err != nil {
- return fmt.Errorf("failed to parse LabelSet %s: %v", label, err)
- }
- inAdditions = append(inAdditions, inAdditionTuple{security.BlessingPattern(blessing), ls})
- }
+ entries[blessing] = tags
}
- // Set the ACLs on the specified name.
+ // Set the ACLs on the specified names.
for {
objACL, etag, err := node.ApplicationClient(vanaName).GetACL(rt.R().NewContext())
if err != nil {
return cmd.UsageErrorf("GetACL(%s) failed: %v", vanaName, err)
}
-
- // Insert into objACL
- for _, b := range notInDeletions {
- if _, contains := objACL.NotIn[b]; !contains {
- fmt.Fprintf(cmd.Stderr(), "WARNING: ignoring attempt to remove non-existing NotIn ACL for %s\n", b)
+ for blessingOrPattern, tags := range entries {
+ objACL.Clear(blessingOrPattern) // Clear out any existing references
+ for tag, blacklist := range tags {
+ if blacklist {
+ objACL.Blacklist(blessingOrPattern, tag)
+ } else {
+ objACL.Add(security.BlessingPattern(blessingOrPattern), tag)
+ }
}
- delete(objACL.NotIn, b)
}
-
- for _, b := range inDeletions {
- if _, contains := objACL.In[b]; !contains {
- fmt.Fprintf(cmd.Stderr(), "WARNING: ignoring attempt to remove non-existing In ACL for %s\n", b)
- }
- delete(objACL.In, b)
- }
-
- for _, b := range inAdditions {
- objACL.In[b.blessing] = *b.ls
- }
-
- for _, b := range notInAdditions {
- objACL.NotIn[b.blessing] = *b.ls
- }
-
switch err := node.ApplicationClient(vanaName).SetACL(rt.R().NewContext(), objACL, etag); {
case err != nil && !verror.Is(err, access.ErrBadEtag):
return cmd.UsageErrorf("SetACL(%s) failed: %v", vanaName, err)
diff --git a/tools/mgmt/nodex/acl_test.go b/tools/mgmt/nodex/acl_test.go
index 9f4d581..1b2a332 100644
--- a/tools/mgmt/nodex/acl_test.go
+++ b/tools/mgmt/nodex/acl_test.go
@@ -29,30 +29,33 @@
cmd.Init(nil, &stdout, &stderr)
nodeName := naming.JoinAddressName(endpoint.String(), "")
- // Test the 'list' command.
+ // Test the 'get' command.
tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/other": security.LabelSet(security.ReadLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"self/..."},
+ NotIn: []string{"self/bad"},
},
- NotIn: map[string]security.LabelSet{
- "root/bob/...": security.LabelSet(security.WriteLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other", "self/..."},
},
},
etag: "anEtagForToday",
err: nil,
- },
- })
+ }})
if err := cmd.Execute([]string{"acl", "get", nodeName}); err != nil {
- t.Fatalf("%v, ouput: %v, error: %v", err)
+ t.Fatalf("%v, output: %v, error: %v", err)
}
- if expected, got := "root/bob/... !W\nroot/other R\nroot/self/... XRWADM", strings.TrimSpace(stdout.String()); got != expected {
- t.Fatalf("Unexpected output from get. Got %q, expected %q", got, expected)
+ if expected, got := strings.TrimSpace(`
+other Read
+self/... Admin,Read
+self/bad !Admin
+`), strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Unexpected output from get. Got %q, expected %q", got, expected)
}
if got, expected := tape.Play(), []interface{}{"GetACL"}; !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
tape.Rewind()
stdout.Reset()
@@ -104,21 +107,21 @@
if err := cmd.Execute([]string{"acl", "set", nodeName, "foo", "!"}); err == nil {
t.Fatalf("failed to detect invalid parameter")
}
- if expected, got := "ERROR: failed to parse LabelSet pair foo, !", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
- t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
+ if expected, got := "ERROR: failed to parse access tags for \"foo\": empty access tag", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+ t.Errorf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
}
// Correct operation in the absence of errors.
stderr.Reset()
stdout.Reset()
tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/other": security.LabelSet(security.ReadLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"self/..."},
},
- NotIn: map[string]security.LabelSet{
- "root/bob": security.LabelSet(security.WriteLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other/...", "self/..."},
+ NotIn: []string{"other/bob"},
},
},
etag: "anEtagForToday",
@@ -126,14 +129,13 @@
},
verror.Make(access.ErrBadEtag, fmt.Sprintf("etag mismatch in:%s vers:%s", "anEtagForToday", "anEtagForTomorrow")),
GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/other": security.LabelSet(security.ReadLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"self/..."},
},
- NotIn: map[string]security.LabelSet{
- "root/bob": security.LabelSet(security.WriteLabel),
- "root/alice/cat": security.LabelSet(security.AdminLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other/...", "self/..."},
+ NotIn: []string{"other/bob/baddevice"},
},
},
etag: "anEtagForTomorrow",
@@ -142,7 +144,20 @@
nil,
})
- if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err != nil {
+ // set command that:
+ // - Adds entry for "friends/..." to "Write" & "Admin"
+ // - Adds a blacklist entry for "friend/alice" for "Admin"
+ // - Edits existing entry for "self/..." (adding "Write" access)
+ // - Removes entry for "other/bob/baddevice"
+ if err := cmd.Execute([]string{
+ "acl",
+ "set",
+ nodeName,
+ "friends/...", "Admin,Write",
+ "friends/alice", "!Admin,Write",
+ "self/...", "Admin,Write,Read",
+ "other/bob/baddevice", "^",
+ }); err != nil {
t.Fatalf("SetACL failed: %v", err)
}
@@ -152,18 +167,22 @@
if expected, got := "WARNING: trying again because of asynchronous change", strings.TrimSpace(stderr.String()); got != expected {
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
}
-
expected := []interface{}{
"GetACL",
SetACLStimulus{
fun: "SetACL",
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"friends/...", "self/..."},
+ NotIn: []string{"friends/alice"},
},
- NotIn: map[string]security.LabelSet{
- "root/vana/bad": security.LabelSet(security.ResolveLabel | security.ReadLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other/...", "self/..."},
+ NotIn: []string{"other/bob"},
+ },
+ "Write": access.ACL{
+ In: []security.BlessingPattern{"friends/...", "friends/alice", "self/..."},
+ NotIn: []string{},
},
},
etag: "anEtagForToday",
@@ -171,14 +190,18 @@
"GetACL",
SetACLStimulus{
fun: "SetACL",
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"friends/...", "self/..."},
+ NotIn: []string{"friends/alice"},
},
- NotIn: map[string]security.LabelSet{
- "root/alice/cat": security.LabelSet(security.AdminLabel),
- "root/vana/bad": security.LabelSet(security.ResolveLabel | security.ReadLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other/...", "self/..."},
+ NotIn: []string{},
+ },
+ "Write": access.ACL{
+ In: []security.BlessingPattern{"friends/...", "friends/alice", "self/..."},
+ NotIn: []string{},
},
},
etag: "anEtagForTomorrow",
@@ -186,7 +209,7 @@
}
if got := tape.Play(); !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
tape.Rewind()
stdout.Reset()
@@ -194,13 +217,13 @@
// GetACL fails.
tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{In: nil, NotIn: nil},
+ acl: access.TaggedACLMap{},
etag: "anEtagForToday",
err: verror.BadArgf("oops!"),
},
})
- if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err == nil {
+ if err := cmd.Execute([]string{"acl", "set", nodeName, "vana/bad", "Read"}); err == nil {
t.Fatalf("GetACL RPC inside acl set command failed but error wrongly not detected")
}
if expected, got := "ERROR: GetACL("+nodeName+") failed: oops!", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
@@ -214,21 +237,17 @@
}
if got := tape.Play(); !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
tape.Rewind()
stdout.Reset()
stderr.Reset()
- // SetACL fails with not a bad etag failure.
+ // SetACL fails with something other than a bad etag failure.
tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/other": security.LabelSet(security.ReadLabel),
- },
- NotIn: map[string]security.LabelSet{
- "root/bob": security.LabelSet(security.WriteLabel),
+ acl: access.TaggedACLMap{
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other", "self/..."},
},
},
etag: "anEtagForToday",
@@ -237,7 +256,7 @@
verror.BadArgf("oops!"),
})
- if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err == nil {
+ if err := cmd.Execute([]string{"acl", "set", nodeName, "friend", "Read"}); err == nil {
t.Fatalf("SetACL should have failed: %v", err)
}
if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
@@ -251,13 +270,10 @@
"GetACL",
SetACLStimulus{
fun: "SetACL",
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
- },
- NotIn: map[string]security.LabelSet{
- "root/vana/bad": security.LabelSet(security.ResolveLabel | security.ReadLabel),
+ acl: access.TaggedACLMap{
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"friend", "other", "self/..."},
+ NotIn: []string{},
},
},
etag: "anEtagForToday",
@@ -265,46 +281,7 @@
}
if got := tape.Play(); !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
- }
- tape.Rewind()
- stdout.Reset()
- stderr.Reset()
-
- // Trying to delete non-existent items.
- stderr.Reset()
- stdout.Reset()
- tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{},
- NotIn: map[string]security.LabelSet{},
- },
- etag: "anEtagForToday",
- err: nil,
- },
- nil,
- })
-
- if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/notin/missing", "!0", "root/vana/in/missing", "0"}); err != nil {
- t.Fatalf("SetACL failed: %v", err)
- }
- if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
- t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
- }
- if expected, got := "WARNING: ignoring attempt to remove non-existing NotIn ACL for root/vana/notin/missing\nWARNING: ignoring attempt to remove non-existing In ACL for root/vana/in/missing", strings.TrimSpace(stderr.String()); got != expected {
- t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
- }
-
- expected = []interface{}{
- "GetACL",
- SetACLStimulus{
- fun: "SetACL",
- acl: security.ACL{},
- etag: "anEtagForToday",
- },
- }
- if got := tape.Play(); !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
tape.Rewind()
stdout.Reset()
diff --git a/tools/mgmt/nodex/nodemanager_mock_test.go b/tools/mgmt/nodex/nodemanager_mock_test.go
index 0fa29fd..6449110 100644
--- a/tools/mgmt/nodex/nodemanager_mock_test.go
+++ b/tools/mgmt/nodex/nodemanager_mock_test.go
@@ -10,6 +10,7 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/node"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron/profiles"
@@ -92,18 +93,18 @@
// Mock ACL getting and setting
type GetACLResponse struct {
- acl security.ACL
+ acl access.TaggedACLMap
etag string
err error
}
type SetACLStimulus struct {
fun string
- acl security.ACL
+ acl access.TaggedACLMap
etag string
}
-func (mni *mockNodeInvoker) SetACL(_ ipc.ServerContext, acl security.ACL, etag string) error {
+func (mni *mockNodeInvoker) SetACL(_ ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
ri := mni.tape.Record(SetACLStimulus{"SetACL", acl, etag})
switch r := ri.(type) {
case nil:
@@ -115,7 +116,7 @@
return nil
}
-func (mni *mockNodeInvoker) GetACL(ipc.ServerContext) (security.ACL, string, error) {
+func (mni *mockNodeInvoker) GetACL(ipc.ServerContext) (access.TaggedACLMap, string, error) {
ir := mni.tape.Record("GetACL")
r := ir.(GetACLResponse)
return r.acl, r.etag, r.err