veyron/services/identity: Add revocation timestamps and buttons to identity server UI.
This change is dependent on https://veyron-review.googlesource.com/#/c/4183
This is what the UI looks like for a failed revocation: https://screenshot.googleplex.com/bMKno7uO1s.png.
Revoked identities display the timestamp at which they were revoked.
Change-Id: I7f0274849468e5c26110473fbfb11561186276e8
diff --git a/services/identity/googleoauth/handler.go b/services/identity/googleoauth/handler.go
index 2ac5641..77292c0 100644
--- a/services/identity/googleoauth/handler.go
+++ b/services/identity/googleoauth/handler.go
@@ -138,6 +138,7 @@
Start, End time.Time
Blessed security.PublicID
RevocationCaveatID string
+ RevocationTime time.Time
}
tmplargs := struct {
Log chan tmplentry
@@ -171,13 +172,14 @@
}
if blessEntry.RevocationCaveat != nil {
tmplentry.RevocationCaveatID = base64.URLEncoding.EncodeToString([]byte(blessEntry.RevocationCaveat.ID()))
+ if revocationTime := h.revocationManager.GetRevocationTime(blessEntry.RevocationCaveat.ID()); revocationTime != nil {
+ tmplentry.RevocationTime = *revocationTime
+ }
// TODO(suharshs): Add a timeout that removes old entries to reduce storage space.
// TODO(suharshs): Make this map from CSRFToken to Email address, have
// revocation manager have map from caveatID to Email address in DirectoryStore.
h.tokenRevocationCaveatMap[tokenCaveatIDKey{csrf, tmplentry.RevocationCaveatID}] = true
}
- // TODO(suharshs): Make the UI depend on where the caveatID exists and if it hasn't been revoked.
- // Use the revocation manager IsRevoked function.
ch <- tmplentry
}
}(tmplargs.Log)
diff --git a/services/identity/googleoauth/template.go b/services/identity/googleoauth/template.go
index be8b525..5c2da76 100644
--- a/services/identity/googleoauth/template.go
+++ b/services/identity/googleoauth/template.go
@@ -7,7 +7,6 @@
"html/template"
)
-// TODO(suharshs): Add an if statement to only show the revoke buttons for non-revoked ids.
var tmpl = template.Must(template.New("auditor").Funcs(tmplFuncMap()).Parse(`<!doctype html>
<html>
<head>
@@ -15,9 +14,11 @@
<title>Blessings for {{.Email}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.7.0/moment.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js"></script>
+<script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script>
function setTimeText(elem) {
var timestamp = elem.data("unixtime");
@@ -50,16 +51,26 @@
"CSRFToken": "{{.CSRFToken}}"
})
}).done(function(data) {
- // TODO(suharshs): Have a fail message, add a strikethrough on the revoked caveats.
- console.log(data)
- revokeButton.remove()
- }).fail(function(jqXHR, textStatus){
- console.log(jqXHR)
- console.log("The request failed :( :", textStatus)
+ if (!data.success) {
+ failMessage(revokeButton);
+ return;
+ }
+ revokeButton.replaceWith("<div>Just Revoked!</div>");
+ }).fail(function(xhr, textStatus){
+ failMessage(revokeButton);
+ console.error('Bad request: %s', status, xhr)
});
});
});
+function failMessage(revokeButton) {
+ revokeButton.parent().parent().fadeIn(function(){
+ $(this).addClass("bg-danger");
+ });
+ toastr.options.closeButton = true;
+ toastr.error('Unable to revoke identity!', 'Error!')
+}
+
</script>
</head>
<body>
@@ -84,7 +95,15 @@
<td><div class="unixtime" data-unixtime={{.Start.Unix}}>{{.Start.String}}</div></td>
<td><div class="unixtime" data-unixtime={{.End.Unix}}>{{.End.String}}</div></td>
<td>{{publicKeyHash .Blessee.PublicKey}}</td>
-<td><button class="revoke" value="{{.RevocationCaveatID}}" type="button">Revoke</button></td>
+<td>
+{{if .RevocationCaveatID}}
+ {{ if .RevocationTime.IsZero }}
+ <button class="revoke" value="{{.RevocationCaveatID}}">Revoke</button>
+ {{ else }}
+ <div class="unixtime" data-unixtime={{.RevocationTime.Unix}}>{{.RevocationTime.String}}</div>
+ {{ end }}
+{{ end }}
+</td>
</tr>
{{else}}
<tr>
diff --git a/services/identity/revocation/revocation_manager.go b/services/identity/revocation/revocation_manager.go
index b988696..139012a 100644
--- a/services/identity/revocation/revocation_manager.go
+++ b/services/identity/revocation/revocation_manager.go
@@ -6,6 +6,7 @@
"encoding/hex"
"fmt"
"path/filepath"
+ "strconv"
"sync"
"time"
@@ -47,16 +48,26 @@
if err != nil {
return err
}
- return revocationMap.Put(token, string(time.Now().Unix()))
+ return revocationMap.Put(token, strconv.FormatInt(time.Now().Unix(), 10))
}
-// Returns true if the provided caveat has been revoked.
-func (r *RevocationManager) IsRevoked(caveatID security.ThirdPartyCaveatID) bool {
+// GetRevocationTimestamp returns the timestamp at which a caveat was revoked.
+// If the caveat wasn't revoked returns nil
+func (r *RevocationManager) GetRevocationTime(caveatID security.ThirdPartyCaveatID) *time.Time {
token, err := r.caveatMap.Get(hex.EncodeToString([]byte(caveatID)))
- if err == nil {
- return revocationMap.Exists(token)
+ if err != nil {
+ return nil
}
- return false
+ timestamp, err := revocationMap.Get(token)
+ if err != nil {
+ return nil
+ }
+ unix_int, err := strconv.ParseInt(timestamp, 10, 64)
+ if err != nil {
+ return nil
+ }
+ revocationTime := time.Unix(unix_int, 0)
+ return &revocationTime
}
type revocationCaveat [16]byte