-
Notifications
You must be signed in to change notification settings - Fork 5
/
handlers.go
154 lines (139 loc) · 4.54 KB
/
handlers.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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
log "github.com/sirupsen/logrus"
)
// sigHandler receives input body must
// contain a base64 encoded file to sign, and the response body contains a base64 encoded
// signed file. The Authorization header of the http request must contain a valid token.
func sigHandler(w http.ResponseWriter, r *http.Request) {
rid := getRequestID(r)
log.WithFields(log.Fields{
"remoteAddressChain": "[" + r.Header.Get("X-Forwarded-For") + "]",
"method": r.Method,
"proto": r.Proto,
"url": r.URL.String(),
"ua": r.UserAgent(),
"rid": rid,
}).Info("request")
// some sanity checking on the request
if r.Method != http.MethodPost {
log.WithFields(log.Fields{"rid": rid}).Error("invalid method")
httpError(w, r, http.StatusMethodNotAllowed, "invalid method")
return
}
if len(r.Header.Get("Authorization")) < 60 {
log.WithFields(log.Fields{"rid": rid}).Error("missing authorization header")
httpError(w, r, http.StatusUnauthorized, "missing authorization header")
return
}
// verify auth token
auth, err := authorize(r.Header.Get("Authorization"))
if err != nil {
log.WithFields(log.Fields{"rid": rid}).Error(err)
httpError(w, r, http.StatusUnauthorized, "not authorized")
return
}
fd, fdHeader, err := r.FormFile("input")
if err != nil {
log.WithFields(log.Fields{"rid": rid}).Error(err)
httpError(w, r, http.StatusBadRequest, "failed to read form data")
return
}
defer fd.Close()
input := make([]byte, fdHeader.Size)
_, err = io.ReadFull(fd, input)
if err != nil {
log.WithFields(log.Fields{"rid": rid}).Error(err)
httpError(w, r, http.StatusBadRequest, "failed to read input")
return
}
inputSha256 := fmt.Sprintf("%x", sha256.Sum256(input))
// prepare an x-forwarded-for by reusing the values received and adding the client IP
clientip := strings.Split(r.RemoteAddr, ":")
xff := strings.Join([]string{
r.Header.Get("X-Forwarded-For"),
strings.Join(clientip[:len(clientip)-1], ":")},
",")
// let's get this file signed!
output, err := callAutograph(auth, input, xff)
if err != nil {
log.WithFields(log.Fields{"rid": rid, "input_sha256": inputSha256}).Error(err)
httpError(w, r, http.StatusBadGateway, "failed to call autograph for signature")
return
}
outputSha256 := fmt.Sprintf("%x", sha256.Sum256(output))
log.WithFields(log.Fields{"rid": rid,
"user": auth.User,
"input_sha256": inputSha256,
"output_sha256": outputSha256,
}).Info("returning signed data")
w.Header().Add("Content-Type", "application/octet-stream")
w.WriteHeader(http.StatusCreated)
w.Write(output)
}
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
httpError(w, r, http.StatusNotFound, "404 page not found")
return
}
func versionHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(jsonVersion)
}
type heartbeat struct {
Status bool `json:"status"`
Checks struct {
CheckAutographHeartbeat bool `json:"check_autograph_heartbeat"`
} `json:"checks"`
Details string `json:"details"`
}
func writeHeartbeatResponse(w http.ResponseWriter, st heartbeat) {
w.Header().Set("Content-Type", "application/json")
if !st.Status {
log.Println(st.Details)
w.WriteHeader(http.StatusServiceUnavailable)
} else {
w.WriteHeader(http.StatusOK)
}
jsonSt, err := json.Marshal(st)
if err != nil {
log.Fatalf("failed to marshal heartbeat status: %v", err)
}
w.Write(jsonSt)
}
// send a GET request to the autograph heartbeat endpoint and
// evaluate its status code before responding
func heartbeatHandler(baseURL string, client heartbeatRequester) http.HandlerFunc {
var (
st heartbeat
heartbeatURL string = baseURL + "__heartbeat__"
)
return func(w http.ResponseWriter, r *http.Request) {
// assume the best, change if we encounter errors
st.Status = true
st.Checks.CheckAutographHeartbeat = true
resp, err := client.Get(heartbeatURL)
if err != nil {
st.Checks.CheckAutographHeartbeat = false
st.Status = false
st.Details = fmt.Sprintf("failed to request autograph heartbeat from %s: %v", heartbeatURL, err)
writeHeartbeatResponse(w, st)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
st.Checks.CheckAutographHeartbeat = false
st.Status = false
st.Details = fmt.Sprintf("upstream autograph returned heartbeat code %d %s", resp.StatusCode, resp.Status)
writeHeartbeatResponse(w, st)
return
}
writeHeartbeatResponse(w, st)
}
}