-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathauthenticator.go
146 lines (131 loc) · 3.9 KB
/
authenticator.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
package main
import (
"bufio"
"bytes"
"crypto"
"crypto/tls"
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
log "github.com/Sirupsen/logrus"
"github.com/elazarl/goproxy"
)
// Panic if called for
func checkError(err error) {
if err != nil {
log.Error(fmt.Sprintf("Panic on error: %s", err))
panic(err)
}
}
type authenticator struct {
Target *url.URL
Verbose bool
client *http.Client
authZ string
strategy strategy
}
type strategy interface {
authenticate() (string, error)
}
func base64URLEncode(bytes []byte) string {
return strings.TrimRight(base64.URLEncoding.EncodeToString(bytes), "=")
}
func newAuthenticator(target *url.URL, creds *authContext, verbose bool, insecure bool) *authenticator {
var client *http.Client
if insecure {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client = &http.Client{Transport: tr}
} else {
client = &http.Client{}
}
var strategy strategy
if len(creds.TokenEndpoint) > 0 {
strategy = &ossStrategy{client: client, creds: creds, Verbose: verbose}
} else {
strategy = &enterpriseStrategy{client: client, creds: creds, Verbose: verbose, hash: crypto.SHA256}
}
return &authenticator{Target: target, strategy: strategy, Verbose: verbose, client: client}
}
// NewAuthenticationHandler creates a new *goproxy.ProxyHttpServer with an automatic
// authentication handler using the authContext provided. URLs are re-written
// to point at the provided 'target'.
func NewAuthenticationHandler(target *url.URL, creds *authContext, verbose bool, insecure bool) *goproxy.ProxyHttpServer {
a := newAuthenticator(target, creds, verbose, insecure)
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if a.Verbose {
log.Debug(fmt.Sprintf("Handling Non-Proxy request for: %s", req.URL.String()))
}
if req.Host == "" {
fmt.Fprintln(w, "Cannot handle requests without Host header, e.g., HTTP 1.0")
return
}
req.URL.Scheme = "http"
req.URL.Host = req.Host
proxy.ServeHTTP(w, req)
})
proxy.OnRequest().DoFunc(
func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
originalURL := req.URL.String()
req.URL.Host = target.Host
req.URL.Scheme = target.Scheme
if len(target.Path) > 0 {
req.URL.Path = target.Path + req.URL.Path
}
if a.Verbose {
log.Infof("Proxying %s --> %s", originalURL, req.URL.String())
}
req.Header.Set("Authorization", a.authZ)
reqBody, err := ioutil.ReadAll(req.Body)
if err != nil {
log.Errorf("Error reading request body: %v", err)
resp, _ := http.ReadResponse(bufio.NewReader(bytes.NewBufferString("Error reading request body")), req)
return req, resp
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(reqBody))
ctx.UserData = reqBody
return req, nil
})
proxy.OnResponse().DoFunc(
func(r *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
if a.Verbose {
if r != nil {
log.Info(fmt.Sprintf("Handling %d response for %v", r.StatusCode, ctx.Req.URL))
} else {
log.Warning("Response is nil!")
}
}
var response *http.Response
if r != nil {
if r.StatusCode == 401 {
authZ, err := a.strategy.authenticate()
if err != nil {
log.Errorf("Authentication failure: %v", err)
response = r
} else {
a.authZ = "token=" + authZ
ctx.Req.Header.Set("Authorization", a.authZ)
ctx.Req.Body = ioutil.NopCloser(bytes.NewBuffer(ctx.UserData.([]byte)))
response, _ = a.client.Do(ctx.Req)
}
} else {
response = r
}
}
if strings.Contains(response.Header.Get("Content-Type"), "text/event-stream") {
if a.Verbose {
log.Info("Adding eventStreamBody wrapper")
}
response.Body = newEventStreamBody(response.Body)
}
return response
})
proxy.Tr.Proxy = nil
return proxy
}