ref/lib/security: Add a method to PrepareDischarges.

Change-Id: I4475b039c2252526e00f0ba406254d0950822da6
diff --git a/lib/security/prepare_discharges_test.go b/lib/security/prepare_discharges_test.go
new file mode 100644
index 0000000..292c58e
--- /dev/null
+++ b/lib/security/prepare_discharges_test.go
@@ -0,0 +1,137 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package security_test
+
+import (
+	"fmt"
+	"testing"
+	"time"
+
+	"v.io/v23"
+	"v.io/v23/context"
+	"v.io/v23/rpc"
+	"v.io/v23/security"
+	securitylib "v.io/x/ref/lib/security"
+	"v.io/x/ref/lib/xrpc"
+	_ "v.io/x/ref/runtime/factories/generic"
+	"v.io/x/ref/test"
+	"v.io/x/ref/test/testutil"
+)
+
+func init() {
+	test.Init()
+}
+
+type expiryDischarger struct {
+	called bool
+}
+
+func (ed *expiryDischarger) Discharge(ctx *context.T, call rpc.StreamServerCall, cav security.Caveat, _ security.DischargeImpetus) (security.Discharge, error) {
+	tp := cav.ThirdPartyDetails()
+	if tp == nil {
+		return security.Discharge{}, fmt.Errorf("discharger: %v does not represent a third-party caveat", cav)
+	}
+	if err := tp.Dischargeable(ctx, call.Security()); err != nil {
+		return security.Discharge{}, fmt.Errorf("third-party caveat %v cannot be discharged for this context: %v", cav, err)
+	}
+	expDur := 10 * time.Millisecond
+	if ed.called {
+		expDur = time.Second
+	}
+	expiry, err := security.NewExpiryCaveat(time.Now().Add(expDur))
+	if err != nil {
+		return security.Discharge{}, fmt.Errorf("failed to create an expiration on the discharge: %v", err)
+	}
+	d, err := call.Security().LocalPrincipal().MintDischarge(cav, expiry)
+	if err != nil {
+		return security.Discharge{}, err
+	}
+	ctx.Infof("got discharge on sever %#v", d)
+	ed.called = true
+	return d, nil
+}
+
+func TestPrepareDischarges(t *testing.T) {
+	ctx, shutdown := test.V23Init()
+	defer shutdown()
+
+	pclient := testutil.NewPrincipal("client")
+	cctx, err := v23.WithPrincipal(ctx, pclient)
+	if err != nil {
+		t.Fatal(err)
+	}
+	pdischarger := testutil.NewPrincipal("discharger")
+	dctx, err := v23.WithPrincipal(ctx, pdischarger)
+	if err != nil {
+		t.Fatal(err)
+	}
+	pclient.AddToRoots(pdischarger.BlessingStore().Default())
+	pclient.AddToRoots(v23.GetPrincipal(ctx).BlessingStore().Default())
+	pdischarger.AddToRoots(pclient.BlessingStore().Default())
+	pdischarger.AddToRoots(v23.GetPrincipal(ctx).BlessingStore().Default())
+
+	expcav, err := security.NewExpiryCaveat(time.Now().Add(time.Hour))
+	if err != nil {
+		t.Fatal(err)
+	}
+	tpcav, err := security.NewPublicKeyCaveat(
+		pdischarger.PublicKey(),
+		"discharger",
+		security.ThirdPartyRequirements{},
+		expcav)
+	if err != nil {
+		t.Fatal(err)
+	}
+	cbless, err := pclient.BlessSelf("clientcaveats", tpcav)
+	if err != nil {
+		t.Fatal(err)
+	}
+	tpid := tpcav.ThirdPartyDetails().ID()
+
+	v23.GetPrincipal(dctx)
+	_, err = xrpc.NewServer(dctx,
+		"discharger",
+		&expiryDischarger{},
+		security.AllowEveryone())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Fetch discharges for tpcav.
+	buffer := 100 * time.Millisecond
+	discharges := securitylib.PrepareDischarges(cctx, cbless,
+		security.DischargeImpetus{}, buffer)
+	if len(discharges) != 1 {
+		t.Errorf("Got %d discharges, expected 1.", len(discharges))
+	}
+	dis, has := discharges[tpid]
+	if !has {
+		t.Errorf("Got %#v, Expected discharge for %s", discharges, tpid)
+	}
+	// Check that the discharges is not yet expired, but is expired after 100 milliseconds.
+	expiry := dis.Expiry()
+	// The discharge should expire.
+	select {
+	case <-time.After(time.Now().Sub(expiry)):
+		break
+	case <-time.After(time.Second):
+		t.Fatalf("discharge didn't expire within a second")
+	}
+
+	// Preparing Discharges again to get fresh discharges.
+	discharges = securitylib.PrepareDischarges(cctx, cbless,
+		security.DischargeImpetus{}, buffer)
+	if len(discharges) != 1 {
+		t.Errorf("Got %d discharges, expected 1.", len(discharges))
+	}
+	dis, has = discharges[tpid]
+	if !has {
+		t.Errorf("Got %#v, Expected discharge for %s", discharges, tpid)
+	}
+	now := time.Now()
+	if expiry = dis.Expiry(); expiry.Before(now) {
+		t.Fatalf("discharge has expired %v, but should be fresh", dis)
+	}
+}