veyron/security/flag: add a convenience method to read ACLs from flags

NewAuthorizerOrDie generates an authorizer using ACLs taken from
command line flags. Add a convenience method that can return the
ACLs. At the same time, update NewAuthorizerOrDie to use the veyron
flags system and a modules based test.

Change-Id: Ib1d32f6a74c2560df9ea69de68b6e7fd41f2d24a
diff --git a/security/flag/flag_test.go b/security/flag/flag_test.go
index 1d58998..3dbc118 100644
--- a/security/flag/flag_test.go
+++ b/security/flag/flag_test.go
@@ -1,89 +1,162 @@
 package flag
 
 import (
+	"bytes"
 	"flag"
+	"fmt"
+	"io"
 	"os"
 	"reflect"
 	"testing"
 
-	tsecurity "v.io/core/veyron/lib/testutil/security"
-
 	"v.io/core/veyron2/security"
 	"v.io/core/veyron2/services/security/access"
+
+	"v.io/core/veyron/lib/modules"
+	"v.io/core/veyron/lib/testutil"
+	tsecurity "v.io/core/veyron/lib/testutil/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 = 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"},
-			},
-		}
+func TestHelperProcess(t *testing.T) {
+	modules.DispatchInTest()
+}
 
-		auth = func(a security.Authorizer, err error) security.Authorizer {
-			if err != nil {
-				panic(err)
-			}
-			return a
-		}
-	)
-	acl2File := tsecurity.SaveACLToFile(acl2)
-	defer os.Remove(acl2File)
+var (
+	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"},
+		},
+	}
+
+	expectedAuthorizer = map[string]security.Authorizer{
+		"empty": auth(access.TaggedACLAuthorizer(acl1, access.TypicalTagType())),
+		"acl2":  auth(access.TaggedACLAuthorizer(acl2, access.TypicalTagType())),
+	}
+)
+
+func init() {
+	testutil.Init()
+	modules.RegisterChild("fileAuth", "", fileAuth)
+	modules.RegisterChild("literalAuth", "", literalAuth)
+	modules.RegisterChild("tamFromFlag", "", tamFromFlag)
+}
+
+func auth(a security.Authorizer, err error) security.Authorizer {
+	if err != nil {
+		panic(err)
+	}
+	return a
+}
+
+func literalAuth(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	nfargs := flag.CommandLine.Args()
+	want := expectedAuthorizer[nfargs[0]]
+	if got := NewAuthorizerOrDie(); !reflect.DeepEqual(got, want) {
+		fmt.Fprintf(stdout, "args %#v\n", args)
+		fmt.Fprintf(stdout, "AuthorizerFromFlags() got Authorizer: %v, want: %v", got, want)
+	}
+	return nil
+}
+
+func fileAuth(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	// 0-th argument is the name of a file to open.
+	nfargs := flag.CommandLine.Args()
+	want := auth(access.TaggedACLAuthorizerFromFile(nfargs[0], access.TypicalTagType()))
+	if got := NewAuthorizerOrDie(); !reflect.DeepEqual(got, want) {
+		fmt.Fprintf(stdout, "args %#v\n", args)
+		fmt.Fprintf(stdout, "AuthorizerFromFlags() got Authorizer: %v, want: %v", got, want)
+	}
+	return nil
+}
+
+func tamFromFlag(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+	nfargs := flag.CommandLine.Args()
+	tam, err := TaggedACLMapFromFlag()
+	if err != nil {
+		fmt.Fprintf(stdout, "TaggedACLMapFromFlag() failed: %v", err)
+		return nil
+	}
+	got := auth(access.TaggedACLAuthorizer(tam, access.TypicalTagType()))
+	want := expectedAuthorizer[nfargs[0]]
+	if !reflect.DeepEqual(got, want) {
+		fmt.Fprintf(stdout, "args %#v\n", args)
+		fmt.Fprintf(stdout, "AuthorizerFromFlags() got Authorizer: %v, want: %v", got, want)
+	}
+	return nil
+}
+
+func TestNewAuthorizerOrDie(t *testing.T) {
+	sh, err := modules.NewShell(nil)
+	if err != nil {
+		t.Fatalf("unexpected error: %s", err)
+	}
+	defer sh.Cleanup(os.Stderr, os.Stderr)
+
+	// Create a file.
+	acl2FileName := tsecurity.SaveACLToFile(acl2)
+	defer os.Remove(acl2FileName)
 
 	testdata := []struct {
-		flags     flagValue
-		wantAuth  security.Authorizer
-		wantPanic bool
+		cmd   string
+		flags []string
+		auth  string
 	}{
 		{
-			flags:    flagValue{},
-			wantAuth: nil,
+			cmd:   "fileAuth",
+			flags: []string{"--veyron.acl.file", "runtime:" + acl2FileName},
+			auth:  acl2FileName,
 		},
 		{
-			flags:    flagValue{"acl": "{}"},
-			wantAuth: auth(access.TaggedACLAuthorizer(acl1, access.TypicalTagType())),
+			cmd:   "literalAuth",
+			flags: []string{"--veyron.acl.literal", "{}"},
+			auth:  "empty",
 		},
 		{
-			flags:    flagValue{"acl": `{"In":{"veyron/alice":"RW", "veyron/bob": "R"}}`},
-			wantAuth: auth(access.TaggedACLAuthorizer(acl2, access.TypicalTagType())),
+			cmd:   "literalAuth",
+			flags: []string{"--veyron.acl.literal", `{"In":{"veyron/alice":"RW", "veyron/bob": "R"}}`},
+			auth:  "acl2",
 		},
 		{
-			flags:    flagValue{"acl": `{"In":{"veyron/bob":"R", "veyron/alice": "WR"}}`},
-			wantAuth: auth(access.TaggedACLAuthorizer(acl2, access.TypicalTagType())),
+			cmd:   "literalAuth",
+			flags: []string{"--veyron.acl.literal", `{"In":{"veyron/bob":"R", "veyron/alice": "WR"}}`},
+			auth:  "acl2",
 		},
 		{
-			flags:    flagValue{"acl_file": acl2File},
-			wantAuth: auth(access.TaggedACLAuthorizerFromFile(acl2File, access.TypicalTagType())),
+			cmd:   "tamFromFlag",
+			flags: []string{"--veyron.acl.file", "runtime:" + acl2FileName},
+			auth:  "acl2",
 		},
 		{
-			flags:     flagValue{"acl_file": acl2File, "acl": `{"In":{"veyron/alice":"RW", "veyron/bob": "R"}}`},
-			wantPanic: true,
+			cmd:   "tamFromFlag",
+			flags: []string{"--veyron.acl.literal", "{}"},
+			auth:  "empty",
+		},
+		{
+			cmd:   "tamFromFlag",
+			flags: []string{"--veyron.acl.literal", `{"In":{"veyron/alice":"RW", "veyron/bob": "R"}}`},
+			auth:  "acl2",
+		},
+		{
+			cmd:   "tamFromFlag",
+			flags: []string{"--veyron.acl.literal", `{"In":{"veyron/bob":"R", "veyron/alice": "WR"}}`},
+			auth:  "acl2",
 		},
 	}
-	for _, d := range testdata {
-		clearACLFlags()
-		for f, v := range d.flags {
-			flag.Set(f, v)
+
+	for _, td := range testdata {
+		fp := append(td.flags, td.auth)
+		h, err := sh.Start(td.cmd, nil, fp...)
+		if err != nil {
+			t.Errorf("unexpected error: %s", err)
 		}
-		testNewAuthorizerOrDie(d.flags, d.wantAuth, d.wantPanic)
+		b := new(bytes.Buffer)
+		h.Shutdown(b, os.Stderr)
+		if got := b.String(); got != "" {
+			t.Errorf(got)
+		}
 	}
 }