-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathauthorizer.go
150 lines (134 loc) · 3.96 KB
/
authorizer.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
package gofast
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"strings"
)
// NewAuthRequest returns a new *http.Request
// and a *Request with the body buffered
// into new NopReader.
func NewAuthRequest(orgl *http.Request) (r *http.Request, req *Request, err error) {
// new request struct that inherits orgl values
r = &http.Request{}
*r = *orgl
var stdin io.ReadCloser
// clone the raw request content into r.Body and stdin
// if there is any body
if orgl.Body != nil {
var content []byte
content, err = ioutil.ReadAll(orgl.Body)
if err != nil {
return
}
r.Body = ioutil.NopCloser(bytes.NewBuffer(content))
stdin = ioutil.NopCloser(bytes.NewBuffer(content))
}
// generate the request
req = &Request{
Raw: orgl,
Role: RoleAuthorizer,
Params: make(map[string]string),
Stdin: stdin,
Data: nil,
}
return
}
// NewAuthorizer creates an authorizer
func NewAuthorizer(clientFactory ClientFactory, sessionHandler SessionHandler) *Authorizer {
return &Authorizer{
clientFactory,
sessionHandler,
}
}
// Authorizer guard a given http.Handler
//
// Since this is implemented as a generic http.Handler middleware,
// you may use this with other non-gofast library components as
// long as they implements the http.Handler interface.
type Authorizer struct {
clientFactory ClientFactory
sessionHandler SessionHandler
}
// Wrap method is a generic http.Handler middleware. Requests
// to wrapped hander would go through the fastcgi authorizer
// first. If not authorized, the request will not reach wrapped
// hander.
func (ar Authorizer) Wrap(inner http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// generate auth request
innerReq, req, err := NewAuthRequest(r)
if err != nil {
w.Header().Add("Content-Type", "text/html; charset=utf8")
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "%s", err)
return
}
// get client to fastcgi application
c, err := ar.clientFactory()
if err != nil {
w.Header().Add("Content-Type", "text/html; charset=utf8")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "unable to connect to authorizer: %s", err)
return
}
// make request with client
resp, err := ar.sessionHandler(c, req)
if err != nil {
w.Header().Add("Content-Type", "text/html; charset=utf8")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "error with authorizer request: %s", err)
return
}
ew := new(bytes.Buffer)
rw := httptest.NewRecorder() // FIXME: should do this without httptest
if err = resp.WriteTo(rw, ew); err != nil {
log.Printf("cannot write to response pipe: %s", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, http.StatusText(http.StatusInternalServerError))
return
}
// if code is not http.StatusOK (200)
if rw.Code != http.StatusOK {
// copy header map
for k, m := range rw.Header() {
for _, v := range m {
w.Header().Add(k, v)
}
}
w.WriteHeader(rw.Code)
fmt.Fprint(w, rw.Body.String())
// if error stream is not empty
// also write to response
// TODO: add option to suppress this?
if ew.Len() > 0 {
w.Header().Add("Content-Type", "text/html; charset=utf8")
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "error reading authorizer response: %s", err)
log.Printf("gofast: error stream from application process %s",
ew.String())
return
}
return
}
// no problem from authorizer
// pass down variable to the inner handler
// and discard the authorizer stdout and stderr
for k, m := range rw.Header() {
// looking for header with keys "Variable-*"
// strip the prefix and pass to the inner header
if len(k) > 9 && strings.HasPrefix(strings.ToLower(k), "variable-") {
innerKey := k[9:]
for _, v := range m {
log.Printf("k: %s, innerKey: %s, v: %s", k, innerKey, v)
innerReq.Header.Add(innerKey, v)
}
}
}
inner.ServeHTTP(w, innerReq)
})
}