blob: be8b52536b7d8bd0ae294edf825f2bde07dbc7d8 [file] [log] [blame]
Asim Shankare31900e2014-08-14 12:39:00 -07001package googleoauth
2
3import (
4 "crypto/ecdsa"
5 "crypto/md5"
6 "crypto/x509"
7 "html/template"
8)
9
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070010// TODO(suharshs): Add an if statement to only show the revoke buttons for non-revoked ids.
Asim Shankare31900e2014-08-14 12:39:00 -070011var tmpl = template.Must(template.New("auditor").Funcs(tmplFuncMap()).Parse(`<!doctype html>
12<html>
13<head>
14<meta charset="UTF-8">
15<title>Blessings for {{.Email}}</title>
16<meta name="viewport" content="width=device-width, initial-scale=1.0">
17<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
18<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.7.0/moment.min.js"></script>
19<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
20<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.0/jquery-ui.min.js"></script>
21<script>
22function setTimeText(elem) {
23 var timestamp = elem.data("unixtime");
24 var m = moment(timestamp*1000.0);
25 var style = elem.data("style");
26 if (style === "absolute") {
27 elem.html("<a href='#'>" + m.format("dd, MMM Do YYYY, h:mm:ss a") + "</a>");
28 elem.data("style", "fromNow");
29 } else {
30 elem.html("<a href='#'>" + m.fromNow() + "</a>");
31 elem.data("style", "absolute");
32 }
33}
34
35$(document).ready(function() {
36 $(".unixtime").each(function() {
37 // clicking the timestamp should toggle the display format.
38 $(this).click(function() { setTimeText($(this)); });
39 setTimeText($(this));
40 });
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070041
42 // Setup the revoke buttons click events.
43 $(".revoke").click(function() {
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070044 var revokeButton = $(this);
45 $.ajax({
Suharsh Sivakumar5ee0ee62014-09-04 13:16:34 -070046 url: "/google/revoke",
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070047 type: "POST",
Suharsh Sivakumar5ee0ee62014-09-04 13:16:34 -070048 data: JSON.stringify({
49 "CaveatID": revokeButton.val(),
50 "CSRFToken": "{{.CSRFToken}}"
51 })
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070052 }).done(function(data) {
53 // TODO(suharshs): Have a fail message, add a strikethrough on the revoked caveats.
54 console.log(data)
55 revokeButton.remove()
Suharsh Sivakumar5ee0ee62014-09-04 13:16:34 -070056 }).fail(function(jqXHR, textStatus){
57 console.log(jqXHR)
58 console.log("The request failed :( :", textStatus)
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070059 });
60 });
Asim Shankare31900e2014-08-14 12:39:00 -070061});
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070062
Asim Shankare31900e2014-08-14 12:39:00 -070063</script>
64</head>
65<body>
66<div class="container">
67<h3>Blessing log for {{.Email}}</h3>
68<table class="table table-bordered table-hover table-responsive">
69<thead>
70<tr>
71 <th>Blessing sought as</th>
72 <th>Blessed as</th>
73 <th>Issued</th>
74 <th>Expires</th>
75 <th>PublicKey</th>
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070076 <th>Revoked</th>
Asim Shankare31900e2014-08-14 12:39:00 -070077 </tr>
78</thead>
79<tbody>
80{{range .Log}}
81<tr>
82<td>{{.Blessee}}</td>
83<td>{{.Blessed}}</td>
84<td><div class="unixtime" data-unixtime={{.Start.Unix}}>{{.Start.String}}</div></td>
85<td><div class="unixtime" data-unixtime={{.End.Unix}}>{{.End.String}}</div></td>
86<td>{{publicKeyHash .Blessee.PublicKey}}</td>
Suharsh Sivakumarfb5cbb72014-08-27 13:14:22 -070087<td><button class="revoke" value="{{.RevocationCaveatID}}" type="button">Revoke</button></td>
Asim Shankare31900e2014-08-14 12:39:00 -070088</tr>
89{{else}}
90<tr>
91<td colspan=5>No blessings issued</td>
92</tr>
93{{end}}
94</tbody>
95</table>
96<hr/>
97</div>
98</body>
99</html>`))
100
101func tmplFuncMap() template.FuncMap {
102 m := make(template.FuncMap)
103 m["publicKeyHash"] = publicKeyHash
104 return m
105}
106
107// publicKeyHash returns a human-readable representation of a public key.
108// The returned representation is similar to what SSH uses when prompting
109// about new hosts (it's the hash of the key).
110//
111// TODO(ashankar): Might be nice for this representation to be used in other
112// places like the "identity" command line tool.
113func publicKeyHash(key *ecdsa.PublicKey) string {
114 const hextable = "0123456789abcdef"
115 bytes, err := x509.MarshalPKIXPublicKey(key)
116 if err != nil {
117 return err.Error()
118 }
119 hash := md5.Sum(bytes)
120 var repr [md5.Size * 3]byte
121 for i, v := range hash {
122 repr[i*3] = hextable[v>>4]
123 repr[i*3+1] = hextable[v&0x0f]
124 repr[i*3+2] = ':'
125 }
126 return string(repr[:len(repr)-1])
127}