aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/cashierd/main.go30
-rw-r--r--server/static/js/table.js49
-rw-r--r--server/static/static.go22
-rw-r--r--server/store/mem.go6
-rw-r--r--server/store/store_test.go2
-rw-r--r--server/templates/certs.go24
6 files changed, 105 insertions, 28 deletions
diff --git a/cmd/cashierd/main.go b/cmd/cashierd/main.go
index e71c126..6e52ddf 100644
--- a/cmd/cashierd/main.go
+++ b/cmd/cashierd/main.go
@@ -15,6 +15,7 @@ import (
"net"
"net/http"
"os"
+ "strconv"
"strings"
"golang.org/x/oauth2"
@@ -231,20 +232,24 @@ func listAllCertsHandler(a *appContext, w http.ResponseWriter, r *http.Request)
if !a.isLoggedIn(w, r) {
return a.login(w, r)
}
- certs, err := a.certstore.List()
- if err != nil {
- return http.StatusInternalServerError, err
+ tmpl := template.Must(template.New("certs.html").Parse(templates.Certs))
+ tmpl.Execute(w, map[string]interface{}{
+ csrf.TemplateTag: csrf.TemplateField(r),
+ })
+ return http.StatusOK, nil
+}
+
+func listCertsJSONHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, error) {
+ if !a.isLoggedIn(w, r) {
+ return http.StatusUnauthorized, errors.New(http.StatusText(http.StatusUnauthorized))
}
- page := struct {
- Certs []*store.CertRecord
- CSRF template.HTML
- }{
- Certs: certs,
- CSRF: csrf.TemplateField(r),
+ includeExpired, _ := strconv.ParseBool(r.URL.Query().Get("all"))
+ certs, err := a.certstore.List(includeExpired)
+ j, err := json.Marshal(certs)
+ if err != nil {
+ return http.StatusInternalServerError, errors.New(http.StatusText(http.StatusInternalServerError))
}
-
- tmpl := template.Must(template.New("certs.html").Parse(templates.Certs))
- tmpl.Execute(w, page)
+ w.Write(j)
return http.StatusOK, nil
}
@@ -397,6 +402,7 @@ func main() {
r.Methods("GET").Path("/revoked").Handler(appHandler{ctx, listRevokedCertsHandler})
r.Methods("POST").Path("/admin/revoke").Handler(CSRF(appHandler{ctx, revokeCertHandler}))
r.Methods("GET").Path("/admin/certs").Handler(CSRF(appHandler{ctx, listAllCertsHandler}))
+ r.Methods("GET").Path("/admin/certs.json").Handler(appHandler{ctx, listCertsJSONHandler})
r.PathPrefix("/").Handler(http.FileServer(static.FS(false)))
h := handlers.LoggingHandler(logfile, r)
diff --git a/server/static/js/table.js b/server/static/js/table.js
new file mode 100644
index 0000000..da0da39
--- /dev/null
+++ b/server/static/js/table.js
@@ -0,0 +1,49 @@
+function reqListener() {
+ var recs = JSON.parse(this.responseText);
+ var table = document.querySelector('#cert-table');
+ var tbody = table.querySelector("#list");
+ while (tbody.rows.length > 0) {
+ tbody.deleteRow(0);
+ }
+ issuedList.clear();
+ recs.forEach(function makeTable(el, i, arr) {
+ var row = tbody.insertRow(-1);
+ row.insertCell(0).innerHTML = el.key_id;
+ row.insertCell(1).innerHTML = el.created_at;
+ row.insertCell(2).innerHTML = el.expires;
+ row.insertCell(3).innerHTML = el.principals;
+ row.insertCell(4).innerHTML = el.revoked;
+ // Index keyid and principals.
+ row.cells[0].classList = ["keyid"];
+ row.cells[3].classList = ["principals"];
+ if (el.revoked) {
+ row.insertCell(5).innerHTML = '<input style="margin:0;" type="checkbox" value="'+ el.key_id + '" name="cert_id" id="cert_id" />';
+ }
+ tbody.appendChild(row);
+ });
+ issuedList.reIndex();
+}
+
+function loadCerts(all) {
+ var r = new XMLHttpRequest();
+ var endpoint = '/admin/certs.json';
+ if (all) {
+ endpoint += '?all=true';
+ }
+ r.open('GET', endpoint);
+ r.addEventListener('load', reqListener);
+ r.send()
+}
+
+var SHOW_ALL = false;
+
+function toggleExpired() {
+ var button = document.querySelector("#toggle-certs");
+ SHOW_ALL = !SHOW_ALL;
+ loadCerts(SHOW_ALL);
+ if (SHOW_ALL == false) {
+ button.innerHTML = "Show Expired";
+ } else {
+ button.innerHTML = "Hide Expired";
+ }
+}
diff --git a/server/static/static.go b/server/static/static.go
index 16f1c72..28719ca 100644
--- a/server/static/static.go
+++ b/server/static/static.go
@@ -296,7 +296,7 @@ AAD//7viGWrCLAAA
"/static/js/list.min.js": {
local: "server/static/js/list.min.js",
size: 15785,
- modtime: 1472412760,
+ modtime: 1472414777,
compressed: `
H4sIAAAJbogA/6x7bZPbNpLwX5H4uLRABHGkxMnuksaovI5TT65i++rsvS8UvQUCIMUxRcokNGOfxP3t
V3gjQYrjnezlywzx1mh0N/oNrXl6KqnIq3JGQIIoYvDc9XCQoT085ymY0yiL9Veivu5JPcuxZ6d6GIuv
@@ -389,6 +389,26 @@ FG1iGP5vAAAA//84G4UUqT0AAA==
`,
},
+ "/static/js/table.js": {
+ local: "server/static/js/table.js",
+ size: 1414,
+ modtime: 1473620952,
+ compressed: `
+H4sIAAAJbogA/3xU3W4aPRC95ynm21ysUYghX9qb0k1VRai0Io0UkFopipBZD+Bi7I3t5UcV717Zy/4k
+0N4g1j5n5szxzMxzlTqhFRh8GQnrUKEhbfjdAtgwAwZTCwl8Gz98pxkzFolbCksN2kwrixPcuXb/iHVs
+JhES4DrN16gcfcnR7McoMXXakPgiReOuAiquSTPN95AU5DeM6EIK66KA3S6FRCABTo3eWipRLdwSbqFX
+yIUiFuUo0eGj3pJeYB5aAMLaHLmvj6YSmSHhxhdH59oMWLoklRFrtsKJV0NQdkB0gBlTZgiW6K3XG3IJ
+ZdE4n+vqOoQEf308vkMpSa9NhVJohpP7ESSAkq5wPxX8LPj6BJwaZA75lLmzhP9PCLjLhEF7Fn1zgs6M
+UKnImDxPeHdCMLjRKzyq73bhq+K4gxXuBQemONQBaRUwRSntU++ZppJZ6x8BEniKAil67r/B3bzF1SFL
+sJgDqbWUb3Oi/v1r9fFHobLcgXV7iUm0ZmYh1IdePwK3zzCJ0iWmq5neRbBhMsckii/r54JLiCNQbO2B
+aNxU8AgEb3x0b+NC3aHRiyzLUPG7pZCcGL0t+jH8NjrSYHDR9+Sh1ar6UGrG79A4S5iUjYmEBBRu4ef9
+aOhc9ogvOVpHqoFCxTMtlPcu7jK+FqrrNVr6y2oVJHr7qpBQEy4TiD8xKRNncozL0TFUZ6hI/GUwiTsV
+uJgfyjgfbFC5anPEXnXcaW6TI9Si4qTtK/Qqx8OHH9PPI/8ucyYt9huFO71YSByERuaNXTTLndPq7wsm
+uiiYV6HeYm008vxX/vfntbnlabu0pqYctZVGFflf9VQ0XuotHKVGwTJAafEfjKHg+JrROrT+BAAA//+v
+G1KqhgUAAA==
+`,
+ },
+
"/": {
isDir: true,
local: "server",
diff --git a/server/store/mem.go b/server/store/mem.go
index e63d00a..54aa965 100644
--- a/server/store/mem.go
+++ b/server/store/mem.go
@@ -40,7 +40,7 @@ func (ms *memoryStore) List(includeExpired bool) ([]*CertRecord, error) {
defer ms.Unlock()
for _, value := range ms.certs {
- if !includeExpired && value.Expires.After(time.Now().UTC()) {
+ if !includeExpired && value.Expires.Before(time.Now().UTC()) {
continue
}
records = append(records, value)
@@ -62,7 +62,9 @@ func (ms *memoryStore) GetRevoked() ([]*CertRecord, error) {
var revoked []*CertRecord
all, _ := ms.List(false)
for _, r := range all {
- revoked = append(revoked, r)
+ if r.Revoked {
+ revoked = append(revoked, r)
+ }
}
return revoked, nil
}
diff --git a/server/store/store_test.go b/server/store/store_test.go
index 3552d1c..594da37 100644
--- a/server/store/store_test.go
+++ b/server/store/store_test.go
@@ -49,7 +49,7 @@ func testStore(t *testing.T, db CertStorer) {
if err := db.SetRecord(r); err != nil {
t.Error(err)
}
- if _, err := db.List(); err != nil {
+ if _, err := db.List(true); err != nil {
t.Error(err)
}
diff --git a/server/templates/certs.go b/server/templates/certs.go
index 89ec5a1..0aa2b7c 100644
--- a/server/templates/certs.go
+++ b/server/templates/certs.go
@@ -13,7 +13,7 @@ const Certs = `
<link rel="stylesheet" href="/static/css/skeleton.css">
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
</head>
-<body>
+<body onload="loadCerts()">
<div class="container">
<div class="page-header">
<h2>Issued SSH Certificates</h2>
@@ -21,8 +21,9 @@ const Certs = `
<div id="issued">
<input class="u-full-width search" type="text" placeholder="Search" id="q" />
+ <button class="button-primary" id="toggle-certs" onclick="toggleExpired()">Show Expired</button>
<form action="/admin/revoke" method="post" id="form_revoke">
- {{ .CSRF }}
+ {{ .csrfField }}
<table id="cert-table">
<thead>
<tr>
@@ -34,17 +35,15 @@ const Certs = `
<th>Revoke</th>
</tr>
</thead>
- <tbody class="list">
- {{range .Certs}}
- <tr>
- <td class="keyid">{{.KeyID}}</td>
- <td>{{.CreatedAt}}</td>
- <td>{{.Expires}}</td>
- <td class="principals">{{.Principals}}</td>
- <td>{{.Revoked}}</td>
- <td>{{if not .Revoked}}<input style="margin:0;" type="checkbox" value="{{.KeyID}}" name="cert_id" id="cert_id" />{{end}}</td>
+ <tbody id="list" class="list">
+ <tr>
+ <td class="keyid"></td>
+ <td></td>
+ <td></td>
+ <td class="principals"></td>
+ <td></td>
+ <td></td>
</tr>
- {{ end }}
</tbody>
</table>
</form>
@@ -59,5 +58,6 @@ var options = {
}
var issuedList = new List('issued', options);
</script>
+<script src="/static/js/table.js"></script>
</html>
`