This repository has been archived by the owner on Mar 1, 2023. It is now read-only.
/
apiv1.go
69 lines (63 loc) · 2.03 KB
/
apiv1.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package apiv1
import (
"crypto/sha256"
"encoding/base64"
"io/ioutil"
"net/http"
"darlinggo.co/api"
yall "yall.in"
"lockbox.dev/clients"
"lockbox.dev/hmac"
)
// APIv1 holds all the information that we want to
// be available for all the functions in the API,
// things like our logging, metrics, and other
// telemetry.
type APIv1 struct {
Storer clients.Storer
Log *yall.Logger
Signer hmac.Signer
}
// VerifyRequest calculates the HMAC signature of `r` and compares it to
// the passed Authorization header, while also checking the claimed SHA256
// hash of the content matches the body of the request. It either returns
// the body of the request, or a Response indicating the error in the request.
// If Response is not nil, it is meant to be returned, short-circuiting the
// request. If Response is nil, the returned string can safely be assumed to
// be an authenticated request body.
func (a APIv1) VerifyRequest(r *http.Request) (string, *Response) {
var payload string
if r.Method == "POST" || r.Method == "PUT" {
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
a.Log.WithError(err).Error("error reading request")
return "", &Response{
Errors: api.ActOfGodError,
Status: http.StatusInternalServerError,
}
}
payload = string(body)
}
hash := base64.StdEncoding.EncodeToString(sha256.New().Sum([]byte(payload)))
err := a.Signer.AuthenticateRequest(r, hash)
if err != nil {
a.Log.WithError(err).Debug("failed to authenticate request")
return "", &Response{
Errors: []api.RequestError{{
Header: "Authorization",
Slug: api.RequestErrAccessDenied,
}},
Status: http.StatusUnauthorized,
}
}
return payload, nil
}
// Response is used to encode JSON responses; it is
// the global response format for all API responses.
type Response struct {
Clients []Client `json:"clients,omitempty"`
RedirectURIs []RedirectURI `json:"redirectURIs,omitempty"`
Errors []api.RequestError `json:"errors,omitempty"`
Status int `json:"-"`
}