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

Commit

Permalink
Use a form (not ajax) for invalidating codes (#390)
Browse files Browse the repository at this point in the history
* add form

* submit form, not ajax for invalidate code

* switch pointer around

* form data

* patch / mux.vars

* fix build

* format error
  • Loading branch information
whaught authored Aug 27, 2020
1 parent 0fcdf98 commit 3cb8bbc
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 36 deletions.
2 changes: 1 addition & 1 deletion cmd/adminapi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,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 @@ -255,6 +255,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

0 comments on commit 3cb8bbc

Please sign in to comment.