"veyron/security/flag": Methods for processing Auth flags
This CL adds methods for parsing "--acl", "--acl_file" flags
and constructing an Authorizer from them.
Change-Id: I36fba5954bddf99b8348cbf5c26507952ed0eeeb
diff --git a/examples/fortune/fortune/main.go b/examples/fortune/fortune/main.go
index f1b2805..89c9327 100644
--- a/examples/fortune/fortune/main.go
+++ b/examples/fortune/fortune/main.go
@@ -6,17 +6,17 @@
"flag"
"fmt"
+ "veyron/examples/fortune"
+
"veyron2"
"veyron2/naming"
"veyron2/rt"
-
- "veyron/examples/fortune"
)
var (
address = flag.String("address", "", "the address/endpoint of the fortune server")
newFortune = flag.String("new_fortune", "", "an optional, new fortune to add to the server's set")
- serverPattern = flag.String("server_pattern", "*", "server_pattern is an optional pattern for the expected identity of the server. It is provided as an option to all RPCs made by the client. The server's identity must match this pattern otherwise the client would abort the call (see veyron2/security.PublicID.Match). For e.g., the pattern \"veyron/fooService\" only matches servers that either have the identity \"veyron/fooService\" or the identity \"veyron\". On the other hand the pattern \"veyron/*\" matches all servers whose identities have the root name \"veyron\". If the flag is absent then the default pattern \"*\" matches all identities.")
+ serverPattern = flag.String("server_pattern", "*", "server_pattern is an optional pattern for the expected identity of the fortune server. Example: the pattern \"myorg/fortune\" matches identities with names \"myorg/fortune\" or \"myorg\". If the flag is absent then the default pattern \"*\" matches all identities.")
)
func main() {
diff --git a/examples/fortune/fortuned/main.go b/examples/fortune/fortuned/main.go
index 1e3d338..a49f55a 100644
--- a/examples/fortune/fortuned/main.go
+++ b/examples/fortune/fortuned/main.go
@@ -3,21 +3,18 @@
package main
import (
- "flag"
"fmt"
"log"
"math/rand"
+ "veyron/examples/fortune"
"veyron/lib/signals"
+ vflag "veyron/security/flag"
+
"veyron2/ipc"
"veyron2/rt"
- "veyron2/security"
-
- "veyron/examples/fortune"
)
-var acl = flag.String("acl", "", "acl is an optional JSON-encoded security.ACL. The ACL is used to construct an authorizer for the fortune server. The behavior of the authorizer can be changed at runtime by simply changing the ACL stored in the file. If the flag is absent then a nil authorizer is constructed which results in default authorization for the server. Default authorization (provided by the Veyron framework) only permits clients that have either blessed the server or have been blessed by the server.")
-
type fortuned struct {
// The set of all fortunes.
fortunes []string
@@ -56,18 +53,11 @@
log.Fatal("failure creating server: ", err)
}
- // Construct an Authorizer for the server based on the provided ACL. If
- // no ACL is provided then a nil Authorizer is used.
- var authorizer security.Authorizer
- if len(*acl) != 0 {
- authorizer = security.NewFileACLAuthorizer(*acl)
- }
-
// Create the fortune server stub.
serverFortune := fortune.NewServerFortune(newFortuned())
// Register the "fortune" prefix with a fortune dispatcher.
- if err := s.Register("fortune", ipc.SoloDispatcher(serverFortune, authorizer)); err != nil {
+ if err := s.Register("fortune", ipc.SoloDispatcher(serverFortune, vflag.NewAuthorizerOrDie())); err != nil {
log.Fatal("error registering service: ", err)
}
diff --git a/lib/testutil/util.go b/lib/testutil/util.go
index 5f75d4b..d94633c 100644
--- a/lib/testutil/util.go
+++ b/lib/testutil/util.go
@@ -65,6 +65,23 @@
return filePath
}
+// SaveACLToFile saves the provided ACL in JSON format to a randomly created
+// 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 {
+ f, err := ioutil.TempFile("", "saved_acl")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ if err := security.SaveACL(f, acl); err != nil {
+ defer os.Remove(f.Name())
+ panic(err)
+ }
+ return f.Name()
+}
+
// NewBlessedIdentity creates a new identity and blesses it using the provided blesser
// under the provided name. This function is meant to be used for testing purposes only,
// it panics if there is an error.
diff --git a/lib/testutil/util_test.go b/lib/testutil/util_test.go
index 2209d92..cda91a6 100644
--- a/lib/testutil/util_test.go
+++ b/lib/testutil/util_test.go
@@ -55,6 +55,34 @@
}
}
+func TestSaveACLToFile(t *testing.T) {
+ r, err := rt.New()
+ if err != nil {
+ t.Fatalf("rt.New failed: %v", err)
+ }
+ defer r.Shutdown()
+ acl := security.ACL{
+ "veyron/alice": security.LabelSet(security.ReadLabel | security.WriteLabel),
+ "veyron/bob": security.LabelSet(security.ReadLabel),
+ }
+
+ filePath := testutil.SaveACLToFile(acl)
+ defer os.Remove(filePath)
+
+ f, err := os.Open(filePath)
+ if err != nil {
+ t.Fatalf("os.Open(%v) failed: %v", filePath, err)
+ }
+ defer f.Close()
+ loadedACL, err := security.LoadACL(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)
+ }
+}
+
func TestNewBlessedIdentity(t *testing.T) {
r, err := rt.New()
if err != nil {
diff --git a/runtimes/google/ipc/jni/dispatcher.go b/runtimes/google/ipc/jni/dispatcher.go
index 43ce39b..5b6408e 100644
--- a/runtimes/google/ipc/jni/dispatcher.go
+++ b/runtimes/google/ipc/jni/dispatcher.go
@@ -8,7 +8,6 @@
"veyron2/ipc"
- gsecurity "veyron/runtimes/google/security"
"veyron2/security"
)
@@ -74,5 +73,5 @@
}
// TODO(spetrovic): create JNI version of authorizer that invokes Java's
// authorizer methods.
- return i, gsecurity.NewACLAuthorizer(security.ACL{security.AllPrincipals: security.LabelSet(security.AdminLabel)}), nil
+ return i, security.NewACLAuthorizer(security.ACL{security.AllPrincipals: security.LabelSet(security.AdminLabel)}), nil
}
diff --git a/security/flag/flag.go b/security/flag/flag.go
new file mode 100644
index 0000000..fa032b8
--- /dev/null
+++ b/security/flag/flag.go
@@ -0,0 +1,37 @@
+// Package flag defines a method for parsing ACL flags and constructing
+// a security.Authorizer based on them.
+package flag
+
+import (
+ "bytes"
+ "errors"
+ "flag"
+
+ "veyron2/security"
+)
+
+var (
+ acl = flag.String("acl", "", "acl is an optional JSON-encoded security.ACL that is used to construct a security.Authorizer. Example: \"{\"veyron/alice\":\"RW\"}\" is a JSON-encoded ACL that allows all principals matching \"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.")
+)
+
+// NewAuthorizerOrDie constructs an Authorizer based on the provided "--acl" or
+// "--acl_file" flags. If both flags are provided the function panics, and if
+// neither flag is provided a nil Authorizer is returned (Note that services with
+// nil Authorizers are provided with default authorization by the framework.)
+func NewAuthorizerOrDie() security.Authorizer {
+ if len(*acl) == 0 && len(*aclFile) == 0 {
+ return nil
+ }
+ if len(*acl) != 0 && len(*aclFile) != 0 {
+ panic(errors.New("only one of the flags \"--acl\" or \"--acl_file\" must be provided"))
+ }
+ if len(*aclFile) != 0 {
+ return security.NewFileACLAuthorizer(*aclFile)
+ }
+ a, err := security.LoadACL(bytes.NewBufferString(*acl))
+ if err != nil {
+ return nil
+ }
+ return security.NewACLAuthorizer(a)
+}
diff --git a/security/flag/flag_test.go b/security/flag/flag_test.go
new file mode 100644
index 0000000..56640e0
--- /dev/null
+++ b/security/flag/flag_test.go
@@ -0,0 +1,74 @@
+package flag
+
+import (
+ "flag"
+ "os"
+ "reflect"
+ "testing"
+
+ "veyron/lib/testutil"
+
+ "veyron2/security"
+)
+
+func TestNewAuthorizerOrDie(t *testing.T) {
+ type flagValue map[string]string
+ testNewAuthorizerOrDie := func(flags flagValue, wantAuth security.Authorizer, wantPanic bool) {
+ defer func() {
+ if gotPanic := (recover() != nil); wantPanic != gotPanic {
+ t.Errorf("AuthorizerFromFlags() with flags %v, got panic: %v, want panic: %v ", flags, gotPanic, wantPanic)
+ }
+ }()
+ if got := NewAuthorizerOrDie(); !reflect.DeepEqual(got, wantAuth) {
+ t.Errorf("AuthorizerFromFlags() with flags %v: got Authorizer: %v, want: %v", flags, got, wantAuth)
+ }
+ }
+ clearACLFlags := func() {
+ flag.Set("acl", "")
+ flag.Set("acl_file", "")
+ }
+ var (
+ acl1 = security.ACL{}
+ acl2 = security.ACL{"veyron/alice": security.LabelSet(security.ReadLabel | security.WriteLabel), "veyron/bob": security.LabelSet(security.ReadLabel)}
+ )
+ acl2File := testutil.SaveACLToFile(acl2)
+ defer os.Remove(acl2File)
+
+ testdata := []struct {
+ flags flagValue
+ wantAuth security.Authorizer
+ wantPanic bool
+ }{
+ {
+ flags: flagValue{},
+ wantAuth: nil,
+ },
+ {
+ flags: flagValue{"acl": "{}"},
+ wantAuth: security.NewACLAuthorizer(acl1),
+ },
+ {
+ flags: flagValue{"acl": "{\"veyron/alice\":\"RW\", \"veyron/bob\": \"R\"}"},
+ wantAuth: security.NewACLAuthorizer(acl2),
+ },
+ {
+ flags: flagValue{"acl": "{\"veyron/bob\":\"R\", \"veyron/alice\": \"WR\"}"},
+ wantAuth: security.NewACLAuthorizer(acl2),
+ },
+ {
+ flags: flagValue{"acl_file": acl2File},
+ wantAuth: security.NewFileACLAuthorizer(acl2File),
+ },
+ {
+ flags: flagValue{"acl_file": acl2File, "acl": "{\"veyron/alice\":\"RW\", \"veyron/bob\": \"R\"}"},
+ wantPanic: true,
+ },
+ }
+ for _, d := range testdata {
+ clearACLFlags()
+ for f, v := range d.flags {
+ flag.Set(f, v)
+ }
+ testNewAuthorizerOrDie(d.flags, d.wantAuth, d.wantPanic)
+ }
+}
diff --git a/services/mgmt/application/applicationd/main.go b/services/mgmt/application/applicationd/main.go
index 2c944c7..7c98555 100644
--- a/services/mgmt/application/applicationd/main.go
+++ b/services/mgmt/application/applicationd/main.go
@@ -4,16 +4,15 @@
"flag"
"veyron/lib/signals"
+ vflag "veyron/security/flag"
"veyron/services/mgmt/application/impl"
"veyron2/rt"
- "veyron2/security"
"veyron2/vlog"
)
func main() {
- var aclFile, address, protocol, name, storeName string
- flag.StringVar(&aclFile, "acl_file", "", "file that contains the JSON-encoded security.ACL")
+ var address, protocol, name, storeName string
flag.StringVar(&address, "address", "localhost:0", "network address to listen on")
flag.StringVar(&name, "name", "", "name to mount the application manager as")
flag.StringVar(&protocol, "protocol", "tcp", "network type to listen on")
@@ -29,7 +28,8 @@
vlog.Fatalf("NewServer() failed: %v", err)
}
defer server.Stop()
- dispatcher, err := impl.NewDispatcher(storeName, security.NewFileACLAuthorizer(aclFile))
+
+ dispatcher, err := impl.NewDispatcher(storeName, vflag.NewAuthorizerOrDie())
if err != nil {
vlog.Fatalf("NewDispatcher() failed: %v", err)
}
diff --git a/services/mgmt/content/contentd/main.go b/services/mgmt/content/contentd/main.go
index 4dea545..102a009 100644
--- a/services/mgmt/content/contentd/main.go
+++ b/services/mgmt/content/contentd/main.go
@@ -6,11 +6,11 @@
"os"
"veyron/lib/signals"
+ vflag "veyron/security/flag"
"veyron/services/mgmt/content/impl"
"veyron2/rt"
- "veyron2/security"
"veyron2/vlog"
)
@@ -20,8 +20,7 @@
)
func main() {
- var aclFile, address, protocol, name, root string
- flag.StringVar(&aclFile, "acl_file", "", "file that contains the JSON-encoded security.ACL")
+ var address, protocol, name, root string
flag.StringVar(&address, "address", "localhost:0", "network address to listen on")
flag.StringVar(&name, "name", "", "name to mount the content manager as")
flag.StringVar(&protocol, "protocol", "tcp", "network type to listen on")
@@ -50,7 +49,8 @@
return
}
defer server.Stop()
- dispatcher := impl.NewDispatcher(root, defaultDepth, security.NewFileACLAuthorizer(aclFile))
+
+ dispatcher := impl.NewDispatcher(root, defaultDepth, vflag.NewAuthorizerOrDie())
suffix := ""
if err := server.Register(suffix, dispatcher); err != nil {
vlog.Errorf("Register(%v, %v) failed: %v", suffix, dispatcher, err)