Revoker implements the revoker service using a directory and files to store.

Change-Id: Ia22c65f5d810bc09681f8542e628e86240e2ebc3
diff --git a/services/security/discharger/discharger.go b/services/security/discharger/discharger.go
index 9bcb9ae..47aaf6f 100644
--- a/services/security/discharger/discharger.go
+++ b/services/security/discharger/discharger.go
@@ -25,7 +25,7 @@
 	return d.id.MintDischarge(caveat, ctx, time.Minute, nil)
 }
 
-// New returns a Discharger server that can be passed to a dispatcher
-func New(id security.PrivateID) interface{} {
-	return ssecurity.NewServerDischarger(&dischargerd{id})
+// NewDischarger returns a discharger service implementation that grants discharges using id.MintDischarge.
+func NewDischarger(id security.PrivateID) ssecurity.DischargerService {
+	return dischargerd{id}
 }
diff --git a/services/security/discharger/revoker.go b/services/security/discharger/revoker.go
index a87043e..7f325ed 100644
--- a/services/security/discharger/revoker.go
+++ b/services/security/discharger/revoker.go
@@ -5,46 +5,53 @@
 	"crypto/sha256"
 	"encoding/hex"
 	"fmt"
+	"io/ioutil"
+	"os"
 	"path/filepath"
-	"strings"
 	"sync"
-	"time"
 	"veyron/security/caveat"
 	ssecurity "veyron/services/security"
 	"veyron2/ipc"
-	"veyron2/naming"
-	"veyron2/rt"
 	"veyron2/security"
-	"veyron2/storage"
-	"veyron2/storage/vstore"
-	"veyron2/vlog"
 	"veyron2/vom"
 )
 
-// TODO(ataly, andreser) This package uses a global variable to store the
-// revoker state to make it accessible to caveat.Validate. Ideally, we would
-// pass this in through the context (or something equivalent).
+// The revocation caveats will be stored in a directory with a file for each.
+type revocationDir string
 
-type revocationServiceT struct {
-	store       storage.Store
-	pathInStore string
+// Returns whether the caveat exists in the recovation file.
+func (dir revocationDir) exists(cav string) (bool, error) {
+	if len(dir) == 0 {
+		return false, fmt.Errorf("missing call to NewRecocationCaveat")
+	}
+	_, err := os.Stat(filepath.Join(string(dir), cav))
+	return !os.IsNotExist(err), nil
+}
+
+func (dir revocationDir) put(caveatNonce string, caveatPreimage []byte) error {
+	if len(dir) == 0 {
+		return fmt.Errorf("missing call to NewRecocationCaveat")
+	}
+	return ioutil.WriteFile(filepath.Join(string(dir), caveatNonce), caveatPreimage, 0777)
+}
+
+func (dir revocationDir) Revoke(ctx ipc.ServerContext, caveatPreimage ssecurity.RevocationToken) error {
+	if len(dir) == 0 {
+		return fmt.Errorf("missing call to NewRecocationCaveat")
+	}
+	caveatNonce := sha256.Sum256(caveatPreimage[:])
+	return revocationService.put(hex.EncodeToString(caveatNonce[:]), caveatPreimage[:])
 }
 
 var revocationService struct {
-	*revocationServiceT
+	revocationDir
 	sync.Mutex
 }
 
 type revocationCaveat [32]byte
 
 func (cav revocationCaveat) Validate(security.Context) error {
-	// TODO(ashankar,mattr): Figure out how to get the context of an existing RPC here
-	rctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
-	defer cancel()
-
-	revocation := revocationService.store.BindObject(
-		naming.Join(revocationService.pathInStore, hex.EncodeToString(cav[:])))
-	exists, err := revocation.Exists(rctx)
+	exists, err := revocationService.exists(hex.EncodeToString(cav[:]))
 	if err != nil {
 		return err
 	}
@@ -67,59 +74,20 @@
 	return revocation, cav, err
 }
 
-func (revoceationService *revocationServiceT) Revoke(ctx ipc.ServerContext, caveatPreimage ssecurity.RevocationToken) error {
-	caveatNonce := sha256.Sum256(caveatPreimage[:])
-	revocation := revocationService.store.BindObject(naming.Join(revocationService.pathInStore, hex.EncodeToString(caveatNonce[:])))
-	if _, err := revocation.Put(ctx, caveatPreimage[:]); err != nil {
-		return err
-	}
-	return nil
-}
-
-// NewRevoker returns a new revoker service that can be passed to a dispatcher.
-// Currently, due to the use of global variables, this function can be called only once.
-func NewRevoker(storeName, pathInStore string) (interface{}, error) {
+// NewRevoker returns a revoker service implementation that persists information about revocations on the filesystem in dir.
+// Can only be called once due to global variables.
+func NewRevoker(dir string) (ssecurity.RevokerService, error) {
 	revocationService.Lock()
 	defer revocationService.Unlock()
-	if revocationService.revocationServiceT != nil {
-		return nil, fmt.Errorf("revoker.Revoker called more than once")
+	if len(revocationService.revocationDir) > 0 {
+		return nil, fmt.Errorf("revoker.Revoker called more than once.")
 	}
-	var err error
-	revocationService.revocationServiceT = &revocationServiceT{pathInStore: pathInStore}
-	revocationService.store, err = vstore.New(storeName)
-	if err != nil {
-		return nil, err
-	}
+	revocationService.revocationDir = revocationDir(dir)
 
-	rctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
-	defer cancel()
+	// create the directory if not already there.
+	os.MkdirAll(dir, 0700)
 
-	// Transaction is rooted at "", so tname == tid.
-	tname, err := revocationService.store.BindTransactionRoot("").CreateTransaction(rctx)
-	if err != nil {
-		return nil, err
-	}
-
-	// Create parent directories for the revoker root, if necessary
-	// TODO(tilaks,andreser): provide a `mkdir -p` equivalent in store
-	l := strings.Split(pathInStore, "/")
-	fmt.Println(l)
-	for i := 0; i <= len(l); i++ {
-		fmt.Println(i, filepath.Join(l[:i]...))
-		prefix := filepath.Join(l[:i]...)
-		o := revocationService.store.BindObject(naming.Join(tname, prefix))
-		if exist, err := o.Exists(rctx); err != nil {
-			vlog.Infof("Error checking existence at %q: %s", prefix, err)
-		} else if !exist {
-			if _, err := o.Put(rctx, &Dir{}); err != nil {
-				vlog.Infof("Error creating directory %q: %s", prefix, err)
-			}
-		}
-	}
-	if err := revocationService.store.BindTransaction(tname).Commit(rctx); err != nil {
-		vlog.Fatalf("Failed to commit creation of revoker root at %s: %s", pathInStore, err)
-	}
-	return ssecurity.NewServerRevoker(revocationService.revocationServiceT), nil
+	return revocationService.revocationDir, nil
 }
 
 func init() {
diff --git a/services/security/discharger/revoker_test.go b/services/security/discharger/revoker_test.go
index acb89e9..8dc971a 100644
--- a/services/security/discharger/revoker_test.go
+++ b/services/security/discharger/revoker_test.go
@@ -1,29 +1,22 @@
 package discharger
 
 import (
+	"os"
+	"path/filepath"
 	"testing"
 	ssecurity "veyron/services/security"
-	teststore "veyron/services/store/testutil"
+	"veyron2"
 	"veyron2/ipc"
 	"veyron2/naming"
 	"veyron2/rt"
 	"veyron2/security"
 )
 
-func init() {
-	rt.Init()
-}
-
-func setup(t *testing.T) (dischargerID security.PublicID, dischargerEndpoint, revokerEndpoint string, closeFunc func()) {
-	// Create and start the store instance that the revoker will use
-	storeServer, err := rt.R().NewServer()
-	if err != nil {
-		t.Fatalf("rt.R().NewServer: %s", err)
-	}
-	storeVeyronName, closeStore := teststore.NewStore(t, storeServer, rt.R().Identity().PublicID())
-
+func revokerSetup(t *testing.T) (dischargerID security.PublicID, dischargerEndpoint, revokerEndpoint string, closeFunc func(), runtime veyron2.Runtime) {
+	var revokerDirPath = filepath.Join(os.TempDir(), "revoker_dir")
+	r := rt.Init()
 	// Create and start revoker and revocation discharge service
-	revokerServer, err := rt.R().NewServer()
+	revokerServer, err := r.NewServer()
 	if err != nil {
 		t.Fatalf("rt.R().NewServer: %s", err)
 	}
@@ -31,11 +24,12 @@
 	if err != nil {
 		t.Fatalf("revokerServer.Listen failed: %v", err)
 	}
-	revokerService, err := NewRevoker(storeVeyronName, "/testrevoker")
+	revokerService, err := NewRevoker(revokerDirPath)
 	if err != nil {
-		t.Fatalf("setup revoker service: %s", err)
+		t.Fatalf("NewRevoker failed: $v", err)
 	}
-	err = revokerServer.Serve("", ipc.SoloDispatcher(revokerService, nil))
+	revokerServiceStub := ssecurity.NewServerRevoker(revokerService)
+	err = revokerServer.Serve("", ipc.SoloDispatcher(revokerServiceStub, nil))
 	if err != nil {
 		t.Fatalf("revokerServer.Serve discharger: %s", err)
 	}
@@ -48,22 +42,23 @@
 	if err != nil {
 		t.Fatalf("revokerServer.Listen failed: %v", err)
 	}
-	err = dischargerServer.Serve("", ipc.SoloDispatcher(New(rt.R().Identity()), nil))
-	if err != nil {
+	dischargerServiceStub := ssecurity.NewServerDischarger(NewDischarger(r.Identity()))
+	if err := dischargerServer.Serve("", ipc.SoloDispatcher(dischargerServiceStub, nil)); err != nil {
 		t.Fatalf("revokerServer.Serve revoker: %s", err)
 	}
-	return rt.R().Identity().PublicID(),
+	return r.Identity().PublicID(),
 		naming.JoinAddressName(dischargerEP.String(), ""),
 		naming.JoinAddressName(revokerEP.String(), ""),
 		func() {
+			defer os.RemoveAll(revokerDirPath)
 			revokerServer.Stop()
 			dischargerServer.Stop()
-			closeStore()
-		}
+		},
+		r
 }
 
 func TestDischargeRevokeDischargeRevokeDischarge(t *testing.T) {
-	dcID, dc, rv, closeFunc := setup(t)
+	dcID, dc, rv, closeFunc, r := revokerSetup(t)
 	defer closeFunc()
 	revoker, err := ssecurity.BindRevoker(rv)
 	if err != nil {
@@ -78,20 +73,19 @@
 	if err != nil {
 		t.Fatalf("failed to create public key caveat: %s", err)
 	}
-
-	if _, err = discharger.Discharge(rt.R().NewContext(), cav); err != nil {
+	if _, err = discharger.Discharge(r.NewContext(), cav); err != nil {
 		t.Fatalf("failed to get discharge: %s", err)
 	}
-	if err = revoker.Revoke(rt.R().NewContext(), preimage); err != nil {
+	if err = revoker.Revoke(r.NewContext(), preimage); err != nil {
 		t.Fatalf("failed to revoke: %s", err)
 	}
-	if discharge, err := discharger.Discharge(rt.R().NewContext(), cav); err == nil || discharge != nil {
+	if discharge, err := discharger.Discharge(r.NewContext(), cav); err == nil || discharge != nil {
 		t.Fatalf("got a discharge for a revoked caveat: %s", err)
 	}
-	if err = revoker.Revoke(rt.R().NewContext(), preimage); err != nil {
+	if err = revoker.Revoke(r.NewContext(), preimage); err != nil {
 		t.Fatalf("failed to revoke again: %s", err)
 	}
-	if discharge, err := discharger.Discharge(rt.R().NewContext(), cav); err == nil || discharge != nil {
+	if discharge, err := discharger.Discharge(r.NewContext(), cav); err == nil || discharge != nil {
 		t.Fatalf("got a discharge for a doubly revoked caveat: %s", err)
 	}
 }
diff --git a/services/security/dischargerd/main.go b/services/security/dischargerd/main.go
deleted file mode 100644
index 6afb52b..0000000
--- a/services/security/dischargerd/main.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package main
-
-import (
-	"flag"
-
-	"veyron/lib/signals"
-	vsecurity "veyron/security"
-	"veyron/services/security/discharger"
-
-	"veyron2/ipc"
-	"veyron2/rt"
-	"veyron2/security"
-	"veyron2/vlog"
-)
-
-var (
-	// TODO(rthellend): Remove the protocol and address flags when the config
-	// manager is working.
-	protocol = flag.String("protocol", "tcp", "protocol to listen on")
-	address  = flag.String("address", ":0", "address to listen on")
-
-	aclFile = flag.String("discharger-acl", "", "ACL to use for the discharge service")
-	publish = flag.String("publish", "discharger", "the Object Name under which to publish this service")
-
-	storeName      = flag.String("revocation-store", "", "Object Name of the Veyron store to be used for revocation. Omit to disable revocation functionality.")
-	publishRevoker = flag.String("publish-revoker", "revoker", "the Object Name under which to publish this service")
-	pathInStore    = flag.String("path-in-store", "/revoker", "the location in store where the revoker keeps its state")
-	revokerAclFile = flag.String("revoker-acl", "", "ACL to use for the revocation service")
-)
-
-func authorizer(file string) security.Authorizer {
-	if file == "" {
-		return vsecurity.NewACLAuthorizer(security.NewWhitelistACL(
-			map[security.PrincipalPattern]security.LabelSet{
-				security.AllPrincipals: security.AllLabels,
-			}))
-	}
-	return vsecurity.NewFileACLAuthorizer(file)
-}
-
-func main() {
-	r := rt.Init()
-	defer r.Cleanup()
-
-	dischargerServer, err := r.NewServer()
-	if err != nil {
-		vlog.Fatal(err)
-	}
-	defer dischargerServer.Stop()
-	dischargerEndpoint, err := dischargerServer.Listen(*protocol, *address)
-	if err != nil {
-		vlog.Fatal(err)
-	}
-	if err = dischargerServer.Serve(*publish, ipc.SoloDispatcher(discharger.New(r.Identity()), authorizer(*aclFile))); err != nil {
-		vlog.Fatal(err)
-	}
-	vlog.Infof("discharger: %s", dischargerEndpoint.String())
-
-	if *storeName != "" {
-		revokerServer, err := r.NewServer()
-		if err != nil {
-			vlog.Fatal(err)
-		}
-		defer revokerServer.Stop()
-		revokerEndpoint, err := revokerServer.Listen(*protocol, *address)
-		if err != nil {
-			vlog.Fatal(err)
-		}
-		revokerService, err := discharger.NewRevoker(*storeName, *pathInStore)
-		if err != nil {
-			vlog.Fatal(err)
-		}
-		err = revokerServer.Serve(*publish, ipc.SoloDispatcher(revokerService, authorizer(*revokerAclFile)))
-		if err != nil {
-			vlog.Fatal(err)
-		}
-		vlog.Infof("revoker: %s", revokerEndpoint.String())
-	}
-
-	<-signals.ShutdownOnSignals()
-}