"veyron/services/wsprd/wspr": Update HTTP server to new security model

Update the WSPR HTTP server to use the new security model whenever
possible for adding principals/blessings for accounts.

Change-Id: I72f7de46a8a171dca6d593469268d74e7323e252
diff --git a/services/wsprd/wspr/wspr.go b/services/wsprd/wspr/wspr.go
index b75cc09..2850e7b 100644
--- a/services/wsprd/wspr/wspr.go
+++ b/services/wsprd/wspr/wspr.go
@@ -27,13 +27,15 @@
 	"time"
 
 	"veyron.io/veyron/veyron2"
+	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/rt"
 	"veyron.io/veyron/veyron2/security"
+	"veyron.io/veyron/veyron2/vdl/vdlutil"
 	"veyron.io/veyron/veyron2/vlog"
 
-	veyron_identity "veyron.io/veyron/veyron/services/identity"
 	"veyron.io/wspr/veyron/services/wsprd/identity"
+	"veyron.io/wspr/veyron/services/wsprd/principal"
 )
 
 const (
@@ -41,21 +43,51 @@
 	pongTimeout  = pingInterval + 10*time.Second // maximum wait for pong.
 )
 
+type blesserService interface {
+	BlessUsingAccessToken(ctx context.T, token string, opts ...ipc.CallOpt) (blessingObj vdlutil.Any, blessings []string, err error)
+}
+
+type bs struct {
+	client ipc.Client
+	name   string
+}
+
+func (s *bs) BlessUsingAccessToken(ctx context.T, token string, opts ...ipc.CallOpt) (blessingObj vdlutil.Any, blessings []string, err error) {
+	var call ipc.Call
+	if call, err = s.client.StartCall(ctx, s.name, "BlessUsingAccessToken", []interface{}{token}, opts...); err != nil {
+		return
+	}
+	var email string
+	if ierr := call.Finish(&blessingObj, &email, &err); ierr != nil {
+		err = ierr
+	}
+	serverBlessings, _ := call.RemoteBlessings()
+	for _, b := range serverBlessings {
+		blessings = append(blessings, b+security.ChainSeparator+email)
+	}
+	return
+}
+
 type wsprConfig struct {
 	MounttableRoot []string
 }
 
 type WSPR struct {
-	mu             sync.Mutex
-	tlsCert        *tls.Certificate
-	rt             veyron2.Runtime
-	httpPort       int // HTTP port for WSPR to serve on. Port rather than address to discourage serving in a way that isn't local.
-	logger         vlog.Logger
-	listenSpec     ipc.ListenSpec
-	identdEP       string
-	idManager      *identity.IDManager
-	blesserService veyron_identity.OAuthBlesser
-	pipes          map[*http.Request]*pipe
+	mu               sync.Mutex
+	tlsCert          *tls.Certificate
+	rt               veyron2.Runtime
+	httpPort         int // HTTP port for WSPR to serve on. Port rather than address to discourage serving in a way that isn't local.
+	logger           vlog.Logger
+	listenSpec       ipc.ListenSpec
+	identdEP         string
+	principalManager *principal.PrincipalManager
+	blesser          blesserService
+	pipes            map[*http.Request]*pipe
+
+	// TODO(ataly, ashankar): Get rid of the fields below once the old
+	// security model is killed.
+	useOldModel bool
+	idManager   *identity.IDManager
 }
 
 var logger vlog.Logger
@@ -72,12 +104,8 @@
 
 // Starts the proxy and listens for requests. This method is blocking.
 func (ctx WSPR) Run() {
-	// Bind to the OAuth Blesser service
-	blesserService, err := veyron_identity.BindOAuthBlesser(ctx.identdEP)
-	if err != nil {
-		log.Fatalf("Failed to bind to identity service at %v: %v", ctx.identdEP, err)
-	}
-	ctx.blesserService = blesserService
+	// Initialize the Blesser service
+	ctx.blesser = &bs{client: ctx.rt.Client(), name: ctx.identdEP}
 
 	// HTTP routes
 	http.HandleFunc("/debug", ctx.handleDebug)
@@ -120,21 +148,42 @@
 		log.Fatalf("rt.New failed: %s", err)
 	}
 
-	// TODO(nlacasse, bjornick) use a serializer that can actually persist.
-	idManager, err := identity.NewIDManager(newrt, &identity.InMemorySerializer{})
-	if err != nil {
-		log.Fatalf("identity.NewIDManager failed: %s", err)
+	wspr := &WSPR{
+		httpPort:    httpPort,
+		listenSpec:  listenSpec,
+		identdEP:    identdEP,
+		rt:          newrt,
+		logger:      newrt.Logger(),
+		pipes:       map[*http.Request]*pipe{},
+		useOldModel: true,
 	}
 
-	return &WSPR{
-		httpPort:   httpPort,
-		listenSpec: listenSpec,
-		identdEP:   identdEP,
-		rt:         newrt,
-		logger:     newrt.Logger(),
-		idManager:  idManager,
-		pipes:      map[*http.Request]*pipe{},
+	for _, o := range opts {
+		if _, ok := o.(veyron2.ForceNewSecurityModel); ok {
+			wspr.useOldModel = false
+			break
+		}
 	}
+
+	if wspr.useOldModel {
+		if wspr.idManager, err = identity.NewIDManager(newrt, &identity.InMemorySerializer{}); err != nil {
+			log.Fatalf("identity.NewIDManager failed: %s", err)
+		}
+		return wspr
+	}
+
+	// TODO(nlacasse, bjornick) use a serializer that can actually persist.
+	if wspr.principalManager, err = principal.NewPrincipalManager(newrt.Principal(), &principal.InMemorySerializer{}); err != nil {
+		log.Fatalf("principal.NewPrincipalManager failed: %s", err)
+	}
+
+	return wspr
+}
+
+func (ctx WSPR) logAndSendBadReqErr(w http.ResponseWriter, msg string) {
+	ctx.logger.Error(msg)
+	http.Error(w, msg, http.StatusBadRequest)
+	return
 }
 
 // HTTP Handlers
@@ -182,12 +231,11 @@
 	Names []string `json:"names"`
 }
 
-// Handler for creating an account in the identity manager.
-// A valid OAuth2 access token must be supplied in the request body. That
-// access token is exchanged for a blessing from the identd server.  A new
-// privateID is then derived from WSPR's privateID and the blessing. That
-// privateID is stored in the identity manager. The name of the new privateID
-// is returned to the client.
+// Handler for creating an account in the principal manager.
+// A valid OAuth2 access token must be supplied in the request body,
+// which is exchanged for blessings from the veyron blessing server.
+// An account based on the blessings is then added to WSPR's principal
+// manager, and the set of blessing strings are returned to the client.
 func (ctx WSPR) handleCreateAccount(w http.ResponseWriter, r *http.Request) {
 	if r.Method != "POST" {
 		http.Error(w, "Method not allowed.", http.StatusMethodNotAllowed)
@@ -197,51 +245,48 @@
 	// Parse request body.
 	var data createAccountInput
 	if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
-		msg := fmt.Sprintf("Error parsing body: %v", err)
-		ctx.logger.Error(msg)
-		http.Error(w, msg, http.StatusBadRequest)
+		ctx.logAndSendBadReqErr(w, fmt.Sprintf("Error parsing body: %v", err))
 	}
 
 	// Get a blessing for the access token from identity server.
 	rctx, cancel := ctx.rt.NewContext().WithTimeout(time.Minute)
 	defer cancel()
-	blessingAny, _, err := ctx.blesserService.BlessUsingAccessToken(rctx, data.AccessToken)
+	blessingsAny, blessings, err := ctx.blesser.BlessUsingAccessToken(rctx, data.AccessToken)
 	if err != nil {
-		msg := fmt.Sprintf("Error getting blessing for access token: %v", err)
-		ctx.logger.Error(msg)
-		http.Error(w, msg, http.StatusBadRequest)
-		return
-	}
-	blessing := blessingAny.(security.PublicID)
-
-	// Derive a new identity from the runtime's identity and the blessing.
-	identity, err := ctx.rt.Identity().Derive(blessing)
-	if err != nil {
-		msg := fmt.Sprintf("Error deriving identity: %v", err)
-		ctx.logger.Error(msg)
-		http.Error(w, msg, http.StatusBadRequest)
+		ctx.logAndSendBadReqErr(w, fmt.Sprintf("Error getting blessing for access token: %v", err))
 		return
 	}
 
-	for _, name := range blessing.Names() {
-		// Store identity in identity manager.
-		if err := ctx.idManager.AddAccount(name, identity); err != nil {
-			msg := fmt.Sprintf("Error storing identity: %v", err)
-			ctx.logger.Error(msg)
-			http.Error(w, msg, http.StatusBadRequest)
+	// Shortcut for old security model.
+	if ctx.useOldModel {
+		ctx.handleCreateAccountOldModel(blessingsAny, w)
+		return
+	}
+
+	accountBlessings, err := security.NewBlessings(blessingsAny.(security.WireBlessings))
+	if err != nil {
+		ctx.logAndSendBadReqErr(w, fmt.Sprintf("Error creating blessings from wire data: %v", err))
+		return
+	}
+	// Add accountBlessings to principalManager under each of the
+	// returned blessing strings.
+	// TODO(ataly, ashankar): Adding the same account under different
+	// different names seems a little weird. Figure out a cleaner way
+	// of adding the account.
+	for _, b := range blessings {
+		if err := ctx.principalManager.AddAccount(b, accountBlessings); err != nil {
+			ctx.logAndSendBadReqErr(w, fmt.Sprintf("Error adding account: %v", err))
 			return
 		}
 	}
 
-	// Return the names to the client.
+	// Return blessings to the client.
 	out := createAccountOutput{
-		Names: blessing.Names(),
+		Names: blessings,
 	}
 	outJson, err := json.Marshal(out)
 	if err != nil {
-		msg := fmt.Sprintf("Error mashalling names: %v", err)
-		ctx.logger.Error(msg)
-		http.Error(w, msg, http.StatusInternalServerError)
+		ctx.logAndSendBadReqErr(w, fmt.Sprintf("Error mashalling names: %v", err))
 		return
 	}
 
@@ -256,7 +301,7 @@
 	Origin string `json:"origin"`
 }
 
-// Handler for associating an existing privateID with an origin.
+// Handler for associating an existing principal with an origin.
 func (ctx WSPR) handleAssocAccount(w http.ResponseWriter, r *http.Request) {
 	if r.Method != "POST" {
 		http.Error(w, "Method not allowed.", http.StatusMethodNotAllowed)
@@ -269,8 +314,63 @@
 		http.Error(w, fmt.Sprintf("Error parsing body: %v", err), http.StatusBadRequest)
 	}
 
+	// Shortcup for old security model.
+	if ctx.useOldModel {
+		ctx.handleAssocAccountOldModel(data, w)
+		return
+	}
+
 	// Store the origin.
 	// TODO(nlacasse, bjornick): determine what the caveats should be.
+	if err := ctx.principalManager.AddOrigin(data.Origin, data.Name, nil); err != nil {
+		http.Error(w, fmt.Sprintf("Error associating account: %v", err), http.StatusBadRequest)
+		return
+	}
+
+	// Success.
+	fmt.Fprintf(w, "")
+}
+
+// TODO(ataly, ashankar): Remove this method once the old security model is killed.
+func (ctx WSPR) handleCreateAccountOldModel(blessingsAny vdlutil.Any, w http.ResponseWriter) {
+	blessing, ok := blessingsAny.(security.PublicID)
+	if ok {
+		ctx.logAndSendBadReqErr(w, "Error creating PublicID from wire data")
+		return
+	}
+
+	// Derive a new identity from the runtime's identity and the blessing.
+	identity, err := ctx.rt.Identity().Derive(blessing)
+	if err != nil {
+		ctx.logAndSendBadReqErr(w, fmt.Sprintf("Error deriving identity: %v", err))
+		return
+	}
+
+	for _, name := range blessing.Names() {
+		// Store identity in identity manager.
+		if err := ctx.idManager.AddAccount(name, identity); err != nil {
+			ctx.logAndSendBadReqErr(w, fmt.Sprintf("Error storing identity: %v", err))
+			return
+		}
+	}
+
+	// Return the names to the client.
+	out := createAccountOutput{
+		Names: blessing.Names(),
+	}
+	outJson, err := json.Marshal(out)
+	if err != nil {
+		ctx.logAndSendBadReqErr(w, fmt.Sprintf("Error mashalling names: %v", err))
+		return
+	}
+
+	// Success.
+	w.Header().Set("Content-Type", "application/json")
+	fmt.Fprintf(w, string(outJson))
+}
+
+// TODO(ataly, ashankar): Remove this method once the old security model is killed.
+func (ctx WSPR) handleAssocAccountOldModel(data assocAccountInput, w http.ResponseWriter) {
 	if err := ctx.idManager.AddOrigin(data.Origin, data.Name, nil); err != nil {
 		http.Error(w, fmt.Sprintf("Error associating account: %v", err), http.StatusBadRequest)
 		return
diff --git a/services/wsprd/wspr/wspr_test.go b/services/wsprd/wspr/wspr_test.go
index 9993cc1..4572c61 100644
--- a/services/wsprd/wspr/wspr_test.go
+++ b/services/wsprd/wspr/wspr_test.go
@@ -7,8 +7,8 @@
 	"net/http"
 	"net/http/httptest"
 	"testing"
-	"time"
 
+	"veyron.io/veyron/veyron2"
 	"veyron.io/veyron/veyron2/context"
 	"veyron.io/veyron/veyron2/ipc"
 	"veyron.io/veyron/veyron2/security"
@@ -17,43 +17,30 @@
 	"veyron.io/veyron/veyron/profiles"
 )
 
+const topLevelName = "mock-blesser"
+
 // BEGIN MOCK BLESSER SERVICE
 // TODO(nlacasse): Is there a better way to mock this?!
 type mockBlesserService struct {
-	id    security.PrivateID
+	p     security.Principal
 	count int
 }
 
-func newMockBlesserService(id security.PrivateID) *mockBlesserService {
+func newMockBlesserService(p security.Principal) *mockBlesserService {
 	return &mockBlesserService{
-		id:    id,
+		p:     p,
 		count: 0,
 	}
 }
 
-func (m *mockBlesserService) BlessUsingAccessToken(c context.T, accessToken string, co ...ipc.CallOpt) (vdlutil.Any, string, error) {
+func (m *mockBlesserService) BlessUsingAccessToken(c context.T, accessToken string, co ...ipc.CallOpt) (vdlutil.Any, []string, error) {
 	m.count = m.count + 1
-	name := fmt.Sprintf("mock-blessing-%v", m.count)
-	blessing, err := m.id.Bless(m.id.PublicID(), name, 5*time.Minute, nil)
+	name := fmt.Sprintf("%s%s%d", topLevelName, security.ChainSeparator, m.count)
+	blessing, err := m.p.BlessSelf(name)
 	if err != nil {
-		return nil, "", err
+		return nil, nil, err
 	}
-	return blessing, name, nil
-}
-
-// This is never used.  Only needed for mock.
-func (m *mockBlesserService) GetMethodTags(c context.T, s string, co ...ipc.CallOpt) ([]interface{}, error) {
-	return nil, nil
-}
-
-// This is never used.  Only needed for mock.
-func (m *mockBlesserService) Signature(c context.T, co ...ipc.CallOpt) (ipc.ServiceSignature, error) {
-	return ipc.ServiceSignature{}, nil
-}
-
-// This is never used.  Only needed for mock.
-func (m *mockBlesserService) UnresolveStep(c context.T, co ...ipc.CallOpt) ([]string, error) {
-	return []string{}, nil
+	return security.MarshalBlessings(blessing), []string{name}, nil
 }
 
 // END MOCK BLESSER SERVICE
@@ -61,10 +48,8 @@
 func setup(t *testing.T) (*WSPR, func()) {
 	spec := *profiles.LocalListenSpec
 	spec.Proxy = "/mock/proxy"
-	wspr := NewWSPR(0, spec, "/mock/identd")
-	providerId := wspr.rt.Identity()
-
-	wspr.blesserService = newMockBlesserService(providerId)
+	wspr := NewWSPR(0, spec, "/mock/identd", veyron2.ForceNewSecurityModel{})
+	wspr.blesser = newMockBlesserService(wspr.rt.Principal())
 	return wspr, func() {
 		wspr.Shutdown()
 	}
@@ -98,10 +83,9 @@
 		t.Fatalf("Expected handleCreateAccount to return 200 OK, instead got %v", resp1)
 	}
 
-	// Verify that idManager has the new account
-	topLevelName := wspr.rt.Identity().PublicID().Names()[0]
-	expectedAccountName := topLevelName + "/mock-blessing-1"
-	gotAccounts := wspr.idManager.AccountsMatching(security.BlessingPattern(expectedAccountName))
+	// Verify that principalManager has the new account
+	expectedAccountName := fmt.Sprintf("%s%s%d", topLevelName, security.ChainSeparator, 1)
+	gotAccounts := wspr.principalManager.AccountsMatching(security.BlessingPattern(expectedAccountName))
 	if len(gotAccounts) != 1 {
 		t.Fatalf("Expected to have 1 account with name %v, but got %v: %v", expectedAccountName, len(gotAccounts), gotAccounts)
 	}
@@ -126,8 +110,8 @@
 		t.Fatalf("Expected handleCreateAccount to return 200 OK, instead got %v", resp2)
 	}
 
-	// Verify that idManager has both accounts
-	gotAccounts = wspr.idManager.AccountsMatching(security.BlessingPattern(fmt.Sprintf("%s%s%v", topLevelName, security.ChainSeparator, security.AllPrincipals)))
+	// Verify that principalManager has both accounts
+	gotAccounts = wspr.principalManager.AccountsMatching(security.BlessingPattern(fmt.Sprintf("%s%s%v", topLevelName, security.ChainSeparator, security.AllPrincipals)))
 	if len(gotAccounts) != 2 {
 		t.Fatalf("Expected to have 2 accounts, but got %v: %v", len(gotAccounts), gotAccounts)
 	}
@@ -137,15 +121,14 @@
 	wspr, teardown := setup(t)
 	defer teardown()
 
-	// First create an accounts.
+	// First create an account.
 	accountName := "mock-account"
-	identityName := "mock-id"
-	privateID, err := wspr.rt.NewIdentity(identityName)
+	blessing, err := wspr.rt.Principal().BlessSelf(accountName)
 	if err != nil {
-		t.Fatalf("wspr.rt.NewIdentity(%v) failed: %v", identityName, err)
+		t.Fatalf("wspr.rt.Principal.BlessSelf(%v) failed: %v", accountName, err)
 	}
-	if err := wspr.idManager.AddAccount(accountName, privateID); err != nil {
-		t.Fatalf("wspr.idManager.AddAccount(%v, %v) failed; %v", accountName, privateID, err)
+	if err := wspr.principalManager.AddAccount(accountName, blessing); err != nil {
+		t.Fatalf("wspr.principalManager.AddAccount(%v, %v) failed; %v", accountName, blessing, err)
 	}
 
 	// Associate with that account
@@ -175,14 +158,14 @@
 		t.Fatalf("Expected handleAssocAccount to return 200 OK, instead got %v", resp)
 	}
 
-	// Verify that idManager has the correct identity for the origin
-	gotID, err := wspr.idManager.Identity(origin)
+	// Verify that principalManager has the correct principal for the origin
+	got, err := wspr.principalManager.Principal(origin)
 	if err != nil {
-		t.Fatalf("wspr.idManager.Identity(%v) failed: %v", origin, err)
+		t.Fatalf("wspr.principalManager.Principal(%v) failed: %v", origin, err)
 	}
 
-	if gotID == nil {
-		t.Fatalf("Expected wspr.idManager.Identity(%v) to return an valid identity, but got %v", origin, gotID)
+	if got == nil {
+		t.Fatalf("Expected wspr.principalManager.Principal(%v) to return a valid principal, but got %v", origin, got)
 	}
 }
 
@@ -218,13 +201,13 @@
 		t.Fatalf("Expected handleAssocAccount to return 400 error, but got %v", resp)
 	}
 
-	// Verify that idManager has no identities for the origin
-	gotID, err := wspr.idManager.Identity(origin)
+	// Verify that principalManager creates no principal for the origin
+	got, err := wspr.principalManager.Principal(origin)
 	if err == nil {
-		t.Fatalf("Expected wspr.idManager.Identity(%v) to fail, but got: %v", origin, gotID)
+		t.Fatalf("Expected wspr.principalManager.Principal(%v) to fail, but got: %v", origin, got)
 	}
 
-	if gotID != nil {
-		t.Fatalf("Expected wspr.idManager.Identity(%v) not to return an identity, but got %v", origin, gotID)
+	if got != nil {
+		t.Fatalf("Expected wspr.principalManager.Principal(%v) not to return a principal, but got %v", origin, got)
 	}
 }