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

Use a form (not ajax) for invalidating codes #390

Merged
merged 8 commits into from
Aug 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/adminapi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func realMain(ctx context.Context) error {

codeStatusController := codestatus.NewAPI(ctx, config, db, h)
r.Handle("/api/checkcodestatus", codeStatusController.HandleCheckCodeStatus()).Methods("POST")
r.Handle("/api/expirecode", codeStatusController.HandleExpire()).Methods("POST")
r.Handle("/api/expirecode", codeStatusController.HandleExpireAPI()).Methods("POST")

srv, err := server.New(config.Port)
if err != nil {
Expand Down
22 changes: 12 additions & 10 deletions cmd/server/assets/codestatus/_codescripts.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<script type="text/javascript">
// element is expected to be a jquery element or dom query selector, ts is
// the number of seconds since epoch, UTC.
function countdown(element, ts) {
function countdown(element, ts, expiredCallback) {
if (typeof (ts) === 'undefined') {
return;
}
Expand Down Expand Up @@ -49,28 +49,30 @@
};

// Fire once so the time is displayed immediately.
setTimeOrExpired($element, formattedTime())
setTimeOrExpired($element, formattedTime(), expiredCallback);

// Set timer.
const fn = setInterval(function() {
let time = formattedTime();
if (!time) {
clearInterval(fn);
}
setTimeOrExpired($element, time)
setTimeOrExpired($element, time, expiredCallback);
}, 1000);

return fn
return fn;
}

function setTimeOrExpired(element, time) {
const countdownExpired = '<strong>EXPIRED</strong>';

function setTimeOrExpired(element, time, expiredCallback) {
if (!time) {
element.html('<strong>EXPIRED</strong>');
return
if (typeof expiredCallback === 'function') {
expiredCallback();
}
return element.html(countdownExpired);
}

let text = `Expires in ${time}`.trim();
element.html(text);
return element.html(`Expires in ${time}`.trim());
}
</script>
{{end}}
20 changes: 14 additions & 6 deletions cmd/server/assets/codestatus/show.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,32 @@ <h5 class="mb-1">Status</h5>
<h5 class="mb-1">Expiry</h5>
<span id="code-expires-at" class="sm text-danger"></span>
</div>
<div class="card-body">
<form action="/code/{{.code.UUID}}/expire" method="POST">
<input type="hidden" name="_method" value="PATCH">
{{ .csrfField }}
<button type="submit" id="invalidate" class="btn btn-danger btn-sm">Invalidate code now</button>
</form>
</div>
</div>

<div>
<a href="/code/status">&larr; Enter another code</a>
</div>
<a href="/code/status" class="card-link">&larr; Enter another code</a>
</main>

{{template "scripts" .}}
{{template "codescripts" .}}

<script type="text/javascript">
let $codeExpiresAt;
let $buttonInvalidate = $('button#invalidate');
let expires = {{ .code.Expires }};

$(function() {
$codeExpiresAt = $('#code-expires-at');
let $codeExpiresAt = $('#code-expires-at');
// Start countdown
countdown($codeExpiresAt, expires);
countdown($codeExpiresAt, expires, function() {
// Disable the submit if already expired.
$buttonInvalidate.prop('disabled', true);
});
});
</script>
</body>
Expand Down
1 change: 1 addition & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ func realMain(ctx context.Context) error {
codeStatusController := codestatus.NewServer(ctx, config, db, h)
sub.Handle("/status", codeStatusController.HandleIndex()).Methods("GET")
sub.Handle("/show", codeStatusController.HandleShow()).Methods("POST")
sub.Handle("/{uuid}/expire", codeStatusController.HandleExpirePage()).Methods("PATCH")
}

// apikeys
Expand Down
37 changes: 36 additions & 1 deletion pkg/controller/codestatus/expire.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ import (

"github.com/google/exposure-notifications-verification-server/pkg/api"
"github.com/google/exposure-notifications-verification-server/pkg/controller"
"github.com/gorilla/mux"
)

func (c *Controller) HandleExpire() http.Handler {
func (c *Controller) HandleExpireAPI() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var request api.ExpireCodeRequest
if err := controller.BindJSON(w, r, &request); err != nil {
Expand Down Expand Up @@ -51,3 +52,37 @@ func (c *Controller) HandleExpire() http.Handler {
})
})
}

func (c *Controller) HandleExpirePage() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
vars := mux.Vars(r)

session := controller.SessionFromContext(ctx)
if session == nil {
controller.MissingSession(w, r, c.h)
return
}
flash := controller.Flash(session)

// Retrieve once to check permissions.
code, _, apiErr := c.CheckCodeStatus(r, vars["uuid"])
if apiErr != nil {
flash.Error("Failed to expire code: %v.", apiErr.Error)
c.renderStatus(ctx, w, code)
return
}

expiredCode, err := c.db.ExpireCode(vars["uuid"])
if err != nil {
flash.Error("Failed to process form: %v.", err)
expiredCode = code
} else {
flash.Alert("Expired code.")
}

retCode := Code{}
c.responseCode(ctx, r, expiredCode, &retCode)
c.renderShow(ctx, w, retCode)
})
}
41 changes: 23 additions & 18 deletions pkg/controller/codestatus/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ func (c *Controller) HandleShow() http.Handler {
c.renderStatus(ctx, w, &code)
return
}
retCode.UUID = form.UUID

code, _, apiErr := c.CheckCodeStatus(r, form.UUID)
if apiErr != nil {
Expand All @@ -67,28 +66,34 @@ func (c *Controller) HandleShow() http.Handler {
c.renderStatus(ctx, w, &code)
return
}
retCode.TestType = strings.Title(code.TestType)

if code.IssuingUserID != 0 {
retCode.IssuerType = "Issuing user"
retCode.Issuer = c.getUserName(ctx, r, code.IssuingUserID)
} else if code.IssuingAppID != 0 {
retCode.IssuerType = "Issuing app"
retCode.Issuer = c.getAuthAppName(ctx, r, code.IssuingAppID)
}

if code.Claimed {
retCode.Status = "Claimed by user"
} else {
retCode.Status = "Not yet claimed"
}
if !code.IsExpired() {
retCode.Expires = code.ExpiresAt.UTC().Unix()
}
c.responseCode(ctx, r, code, &retCode)
c.renderShow(ctx, w, retCode)
})
}

func (c *Controller) responseCode(ctx context.Context, r *http.Request, code *database.VerificationCode, retCode *Code) {
retCode.UUID = code.UUID
retCode.TestType = strings.Title(code.TestType)

if code.IssuingUserID != 0 {
retCode.IssuerType = "Issuing user"
retCode.Issuer = c.getUserName(ctx, r, code.IssuingUserID)
} else if code.IssuingAppID != 0 {
retCode.IssuerType = "Issuing app"
retCode.Issuer = c.getAuthAppName(ctx, r, code.IssuingAppID)
}

if code.Claimed {
retCode.Status = "Claimed by user"
} else {
retCode.Status = "Not yet claimed"
}
if !code.IsExpired() {
retCode.Expires = code.ExpiresAt.UTC().Unix()
}
}

func (c *Controller) getUserName(ctx context.Context, r *http.Request, id uint) (userName string) {
userName = "Unknown user"
_, user, err := c.getAuthorizationFromContext(r)
Expand Down