Skip to content
This repository was archived by the owner on Jul 12, 2023. It is now read-only.

Commit 42c1599

Browse files
authored
List recent codes on code-status page (#774)
1 parent 3482b2e commit 42c1599

File tree

7 files changed

+154
-10
lines changed

7 files changed

+154
-10
lines changed

cmd/server/assets/codestatus/index.html

+27-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ <h1>Verification code status</h1>
2424
<div class="card mb-3 shadow-sm">
2525
<div class="card-header">Code status</div>
2626
<div class="card-body">
27-
<form method="POST" action="show" class="floating-form">
27+
<form method="POST" id="code-form" action="show" class="floating-form">
2828
{{ .csrfField }}
2929
<div class="form-group">
3030
<div class="form-label-group ">
@@ -43,17 +43,36 @@ <h1>Verification code status</h1>
4343
</div>
4444
</div>
4545

46-
<button id="submit" class="btn btn-primary btn-block">Check status</button>
46+
<button class="btn btn-primary btn-block">Check status</button>
4747
</form>
4848
</div>
4949
</div>
50+
51+
<div class="card mb-3 shadow-sm">
52+
<div class="card-header">Recently issued codes</div>
53+
<div class="card-body">
54+
<div class="list-group">
55+
{{range $code := .recentCodes}}
56+
<a class="list-group-item list-group-item-action" onclick="clickCode('{{$code.UUID}}')">
57+
{{$code.UUID}}
58+
<small class="form-text text-muted">
59+
Created at: {{$code.CreatedAt}}
60+
</small>
61+
</a>
62+
{{end}}
63+
</div>
64+
</div>
65+
</div>
5066
</main>
5167

5268
{{template "scripts" .}}
5369
<script type="text/javascript">
70+
let $uuid
71+
let $form
5472

5573
$(function() {
56-
let $uuid = $('#uuid');
74+
$uuid = $('#uuid');
75+
$form = $('#code-form');
5776
let allowedChars = new RegExp("[0-9a-fA-F]");
5877
let dashLocations = [8, 13, 18, 23];
5978

@@ -73,6 +92,11 @@ <h1>Verification code status</h1>
7392
$uuid.val(r);
7493
});
7594
});
95+
96+
function clickCode(uuid) {
97+
$uuid.val(uuid);
98+
$form.submit();
99+
}
76100
</script>
77101
</body>
78102

pkg/controller/codestatus/expire.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ func (c *Controller) HandleExpirePage() http.Handler {
5858
ctx := r.Context()
5959
vars := mux.Vars(r)
6060

61+
realm := controller.RealmFromContext(ctx)
62+
if realm == nil {
63+
controller.MissingRealm(w, r, c.h)
64+
return
65+
}
66+
67+
currentUser := controller.UserFromContext(ctx)
68+
if currentUser == nil {
69+
controller.MissingUser(w, r, c.h)
70+
return
71+
}
72+
6173
session := controller.SessionFromContext(ctx)
6274
if session == nil {
6375
controller.MissingSession(w, r, c.h)
@@ -69,7 +81,9 @@ func (c *Controller) HandleExpirePage() http.Handler {
6981
code, _, apiErr := c.CheckCodeStatus(r, vars["uuid"])
7082
if apiErr != nil {
7183
flash.Error("Failed to expire code: %v.", apiErr.Error)
72-
c.renderStatus(ctx, w, code)
84+
if err := c.renderStatus(ctx, w, realm, currentUser, code); err != nil {
85+
controller.InternalError(w, r, c.h, err)
86+
}
7387
return
7488
}
7589

pkg/controller/codestatus/index.go

+27-3
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,39 @@ func (c *Controller) HandleIndex() http.Handler {
2828
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2929
ctx := r.Context()
3030

31-
// TODO(whaught): load a list of recent codes to show
31+
realm := controller.RealmFromContext(ctx)
32+
if realm == nil {
33+
controller.MissingRealm(w, r, c.h)
34+
return
35+
}
36+
37+
currentUser := controller.UserFromContext(ctx)
38+
if currentUser == nil {
39+
controller.MissingUser(w, r, c.h)
40+
return
41+
}
3242

3343
var code database.VerificationCode
34-
c.renderStatus(ctx, w, &code)
44+
if err := c.renderStatus(ctx, w, realm, currentUser, &code); err != nil {
45+
controller.InternalError(w, r, c.h, err)
46+
}
3547
})
3648
}
3749

38-
func (c *Controller) renderStatus(ctx context.Context, w http.ResponseWriter, code *database.VerificationCode) {
50+
func (c *Controller) renderStatus(
51+
ctx context.Context,
52+
w http.ResponseWriter,
53+
realm *database.Realm,
54+
user *database.User,
55+
code *database.VerificationCode) error {
56+
recentCodes, err := c.db.ListRecentCodes(realm, user)
57+
if err != nil {
58+
return err
59+
}
60+
3961
m := controller.TemplateMapFromContext(ctx)
4062
m["code"] = code
63+
m["recentCodes"] = recentCodes
4164
c.h.RenderHTML(w, "code/status", m)
65+
return nil
4266
}

pkg/controller/codestatus/show.go

+18-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ func (c *Controller) HandleShow() http.Handler {
3333
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3434
ctx := r.Context()
3535

36+
realm := controller.RealmFromContext(ctx)
37+
if realm == nil {
38+
controller.MissingRealm(w, r, c.h)
39+
return
40+
}
41+
42+
currentUser := controller.UserFromContext(ctx)
43+
if currentUser == nil {
44+
controller.MissingUser(w, r, c.h)
45+
return
46+
}
47+
3648
session := controller.SessionFromContext(ctx)
3749
if session == nil {
3850
controller.MissingSession(w, r, c.h)
@@ -53,7 +65,9 @@ func (c *Controller) HandleShow() http.Handler {
5365
var code database.VerificationCode
5466
code.AddError("uuid", "cannot be blank")
5567

56-
c.renderStatus(ctx, w, &code)
68+
if err := c.renderStatus(ctx, w, realm, currentUser, &code); err != nil {
69+
controller.InternalError(w, r, c.h, err)
70+
}
5771
return
5872
}
5973

@@ -63,7 +77,9 @@ func (c *Controller) HandleShow() http.Handler {
6377
code.UUID = form.UUID
6478
code.AddError("uuid", apiErr.Error)
6579

66-
c.renderStatus(ctx, w, &code)
80+
if err := c.renderStatus(ctx, w, realm, currentUser, &code); err != nil {
81+
controller.InternalError(w, r, c.h, err)
82+
}
6783
return
6884
}
6985

pkg/database/token.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func (db *Database) ClaimToken(realmID uint, tokenID string, subject *Subject) e
145145
})
146146
}
147147

148-
// VerifyCodeAndIssueToken takes a previously issed verification code and exchanges
148+
// VerifyCodeAndIssueToken takes a previously issued verification code and exchanges
149149
// it for a long term token. The verification code must not have expired and must
150150
// not have been previously used. Both acctions are done in a single database
151151
// transaction.

pkg/database/vercode.go

+27
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,33 @@ func (db *Database) FindVerificationCodeByUUID(uuid string) (*VerificationCode,
227227
return &vc, nil
228228
}
229229

230+
// ListRecentCodes shows the last 5 recently issued codes for a given issuing user.
231+
// The code and longCode are removed, this is only intended to show metadata.
232+
func (db *Database) ListRecentCodes(realm *Realm, user *User) ([]*VerificationCode, error) {
233+
var codes []*VerificationCode
234+
if err := db.db.
235+
Model(&VerificationCode{}).
236+
Where("realm_id = ? AND issuing_user_id = ?", realm.ID, user.ID).
237+
Order("created_at DESC").
238+
Limit(5).
239+
Find(&codes).
240+
Error; err != nil {
241+
return nil, err
242+
}
243+
244+
// We're only showing meta details, not the encrypted codes.
245+
for _, t := range codes {
246+
if t.Code != "" {
247+
t.Code = "short"
248+
}
249+
if t.LongCode != "" {
250+
t.LongCode = "long"
251+
}
252+
}
253+
254+
return codes, nil
255+
}
256+
230257
// ExpireCode saves a verification code as expired.
231258
func (db *Database) ExpireCode(uuid string) (*VerificationCode, error) {
232259
var vc VerificationCode

pkg/database/vercode_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,45 @@ func TestVerificationCode_FindVerificationCodeByUUID(t *testing.T) {
105105
}
106106
}
107107

108+
func TestVerificationCode_ListRecentCodes(t *testing.T) {
109+
t.Parallel()
110+
111+
db := NewTestDatabase(t)
112+
var realmID uint = 123
113+
var userID uint = 456
114+
115+
vc := &VerificationCode{
116+
RealmID: realmID,
117+
IssuingUserID: userID,
118+
Code: "123456",
119+
LongCode: "defghijk329024",
120+
TestType: "confirmed",
121+
ExpiresAt: time.Now().Add(time.Hour),
122+
LongExpiresAt: time.Now().Add(2 * time.Hour),
123+
}
124+
125+
if err := db.SaveVerificationCode(vc, time.Hour); err != nil {
126+
t.Fatal(err)
127+
}
128+
129+
uuid := vc.UUID
130+
if uuid == "" {
131+
t.Fatal("expected uuid")
132+
}
133+
134+
{
135+
r := &Realm{Model: gorm.Model{ID: realmID}}
136+
u := &User{Model: gorm.Model{ID: userID}}
137+
got, err := db.ListRecentCodes(r, u)
138+
if err != nil {
139+
t.Fatal(err)
140+
}
141+
if got, want := got[0].ID, vc.ID; got != want {
142+
t.Errorf("expected %#v to be %#v", got, want)
143+
}
144+
}
145+
}
146+
108147
func TestVerificationCode_ExpireVerificationCode(t *testing.T) {
109148
t.Parallel()
110149

0 commit comments

Comments
 (0)