veyron/services/identity: Better error handling and displaying to user
for viewing blessings from audit logs.

Change-Id: I83116fa1a470ed5c62979243e728c6d8898b5fe6
diff --git a/services/identity/auditor/blessing_auditor.go b/services/identity/auditor/blessing_auditor.go
index a76bc89..28f7b62 100644
--- a/services/identity/auditor/blessing_auditor.go
+++ b/services/identity/auditor/blessing_auditor.go
@@ -10,7 +10,6 @@
 	vsecurity "veyron.io/veyron/veyron/security"
 	"veyron.io/veyron/veyron/security/audit"
 	"veyron.io/veyron/veyron2/security"
-	"veyron.io/veyron/veyron2/vlog"
 	"veyron.io/veyron/veyron2/vom"
 )
 
@@ -27,6 +26,7 @@
 	Timestamp          time.Time // Time when the blesings were created.
 	RevocationCaveatID string
 	Blessings          security.Blessings
+	DecodeError        error
 }
 
 // NewSQLBlessingAuditor returns an auditor for wrapping a principal with, and a BlessingLogReader
@@ -70,12 +70,7 @@
 	defer close(dst)
 	dbch := r.db.Query(email)
 	for dbentry := range dbch {
-		var entry BlessingEntry
-		if err := entry.fromDatabaseEntry(dbentry); err != nil {
-			vlog.Errorf("Corrupt database data? %#v, %v", dbentry, err)
-			continue
-		}
-		dst <- entry
+		dst <- newBlessingEntry(dbentry)
 	}
 }
 
@@ -115,22 +110,29 @@
 	return d, nil
 }
 
-func (b *BlessingEntry) fromDatabaseEntry(dbentry databaseEntry) error {
-	b.Email = dbentry.email
-	b.Timestamp = dbentry.timestamp
+func newBlessingEntry(dbentry databaseEntry) BlessingEntry {
+	if dbentry.decodeErr != nil {
+		return BlessingEntry{DecodeError: dbentry.decodeErr}
+	}
+	b := BlessingEntry{
+		Email:     dbentry.email,
+		Timestamp: dbentry.timestamp,
+	}
 	var wireBlessings security.WireBlessings
 	var err error
-	if err := vom.NewDecoder(bytes.NewBuffer(dbentry.blessings)).Decode(&wireBlessings); err != nil {
-		return err
+	if err = vom.NewDecoder(bytes.NewBuffer(dbentry.blessings)).Decode(&wireBlessings); err != nil {
+		return BlessingEntry{DecodeError: fmt.Errorf("failed to decode blessings: %s", err)}
 	}
 	if b.Blessings, err = security.NewBlessings(wireBlessings); err != nil {
-		return err
+		return BlessingEntry{DecodeError: fmt.Errorf("failed to construct blessings: %s", err)}
 	}
-	if err := vom.NewDecoder(bytes.NewBuffer(dbentry.caveats)).Decode(&b.Caveats); err != nil {
-		return err
+	if err = vom.NewDecoder(bytes.NewBuffer(dbentry.caveats)).Decode(&b.Caveats); err != nil {
+		return BlessingEntry{DecodeError: fmt.Errorf("failed to decode caveats: %s", err)}
 	}
-	b.RevocationCaveatID, err = revocationCaveatID(b.Caveats)
-	return err
+	if b.RevocationCaveatID, err = revocationCaveatID(b.Caveats); err != nil {
+		return BlessingEntry{DecodeError: fmt.Errorf("error getting revocationCaveatID: %s", err)}
+	}
+	return b
 }
 
 func revocationCaveatID(caveats []security.Caveat) (string, error) {
diff --git a/services/identity/auditor/sql_database.go b/services/identity/auditor/sql_database.go
index 7b601b6..405f97f 100644
--- a/services/identity/auditor/sql_database.go
+++ b/services/identity/auditor/sql_database.go
@@ -25,6 +25,7 @@
 	email, revocationCaveatID string
 	caveats, blessings        []byte
 	timestamp                 time.Time
+	decodeErr                 error
 }
 
 // newSQLDatabase returns a SQL implementation of the database interface.
@@ -72,13 +73,14 @@
 	rows, err := s.queryStmt.Query(email)
 	if err != nil {
 		vlog.Errorf("query failed %v", err)
+		dst <- databaseEntry{decodeErr: fmt.Errorf("Failed to query for all audits: %v", err)}
 		return
 	}
 	for rows.Next() {
 		var dbentry databaseEntry
 		if err = rows.Scan(&dbentry.email, &dbentry.caveats, &dbentry.revocationCaveatID, &dbentry.timestamp, &dbentry.blessings); err != nil {
 			vlog.Errorf("scan of row failed %v", err)
-			return
+			dbentry.decodeErr = fmt.Errorf("failed to read sql row, %s", err)
 		}
 		dst <- dbentry
 	}
diff --git a/services/identity/googleoauth/handler.go b/services/identity/googleoauth/handler.go
index 255c9b1..109f3fd 100644
--- a/services/identity/googleoauth/handler.go
+++ b/services/identity/googleoauth/handler.go
@@ -178,6 +178,7 @@
 		RevocationTime time.Time
 		Blessed        security.Blessings
 		Token          string
+		Error          error
 	}
 	tmplargs := struct {
 		Log                chan tmplentry
@@ -201,6 +202,7 @@
 		defer close(ch)
 		for entry := range entrych {
 			tmplEntry := tmplentry{
+				Error:     entry.DecodeError,
 				Timestamp: entry.Timestamp,
 				Caveats:   entry.Caveats,
 				Blessed:   entry.Blessings,
@@ -212,6 +214,7 @@
 					caveatID := base64.URLEncoding.EncodeToString([]byte(entry.RevocationCaveatID))
 					if tmplEntry.Token, err = h.csrfCop.NewToken(w, r, clientIDCookie, caveatID); err != nil {
 						vlog.Errorf("Failed to create CSRF token[%v] for request %#v", err, r)
+						tmplEntry.Error = fmt.Errorf("server error: unable to create revocation token")
 					}
 				}
 			}
diff --git a/services/identity/googleoauth/template.go b/services/identity/googleoauth/template.go
index 6bac1f7..2bd72bd 100644
--- a/services/identity/googleoauth/template.go
+++ b/services/identity/googleoauth/template.go
@@ -72,7 +72,7 @@
 <h3>Blessing log for {{.Email}}</h3>
 <table class="table table-bordered table-hover table-responsive">
 <thead>
-<tr>
+  <tr>
   <th>Blessed as</th>
   <th>Public Key</th>
   <th>Issued</th>
@@ -82,27 +82,33 @@
 </thead>
 <tbody>
 {{range .Log}}
-<tr>
-<td>{{.Blessed}}</td>
-<td>{{.Blessed.PublicKey}}</td>
-<td><div class="unixtime" data-unixtime={{.Timestamp.Unix}}>{{.Timestamp.String}}</div></td>
-<td>
-{{range .Caveats}}
-  {{.}}</br>
-{{end}}
-</td>
-<td>
-  {{ if .Token }}
-  <button class="revoke" value="{{.Token}}">Revoke</button>
-  {{ else if not .RevocationTime.IsZero }}
-    <div class="unixtime" data-unixtime={{.RevocationTime.Unix}}>{{.RevocationTime.String}}</div>
-  {{ end }}
-</td>
-</tr>
+  {{if .Error}}
+    <tr class="bg-danger">
+      <td colspan="5">Failed to read audit log: Error: {{.Error}}</td>
+    </tr>
+  {{else}}
+    <tr>
+    <td>{{.Blessed}}</td>
+    <td>{{.Blessed.PublicKey}}</td>
+    <td><div class="unixtime" data-unixtime={{.Timestamp.Unix}}>{{.Timestamp.String}}</div></td>
+    <td>
+    {{range .Caveats}}
+      {{.}}</br>
+    {{end}}
+    </td>
+    <td>
+      {{ if .Token }}
+      <button class="revoke" value="{{.Token}}">Revoke</button>
+      {{ else if not .RevocationTime.IsZero }}
+        <div class="unixtime" data-unixtime={{.RevocationTime.Unix}}>{{.RevocationTime.String}}</div>
+      {{ end }}
+    </td>
+    </tr>
+  {{end}}
 {{else}}
-<tr>
-<td colspan=5>No blessings issued</td>
-</tr>
+  <tr>
+  <td colspan=5>No blessings issued</td>
+  </tr>
 {{end}}
 </tbody>
 </table>