/
example-go-plugin.go
176 lines (154 loc) · 5.6 KB
/
example-go-plugin.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/TykTechnologies/tyk/config"
"github.com/TykTechnologies/tyk/ctx"
"github.com/TykTechnologies/tyk/log"
"github.com/TykTechnologies/tyk/storage"
"github.com/TykTechnologies/tyk/user"
)
var logger = log.Get()
// Writes data in the http.Request object to the Gateway log
func RequestLogger(rw http.ResponseWriter, r *http.Request) {
// call ParseForm to populate some of the form-related fields
r.ParseForm()
logger.Info("Request logger plugin will now log request data...")
logger.Info(" Method: ", r.Method)
logger.Info(" Proto: ", r.Proto)
logger.Info(" ProtoMajor: ", r.ProtoMajor)
logger.Info(" ProtoMinor: ", r.ProtoMinor)
logger.Info(" URL.Host: ", r.URL.Host)
logger.Info(" URL.Path: ", r.URL.Path)
logger.Info(" URL.Scheme: ", r.URL.Scheme)
logger.Info(" URL.Fragment: ", r.URL.Fragment)
logger.Info(" URL.RawQuery: ", r.URL.RawQuery)
logger.Info(" URL.Opaque: ", r.URL.Opaque)
for name, _ := range r.Header {
logger.Info(" Header.Get(\"", name, "\"): ", r.Header.Get(name))
}
logger.Info(" ContentLength: ", r.ContentLength)
logger.Info(" Host: ", r.Host)
logger.Info(" RemoteAddr: ", r.RemoteAddr)
logger.Info(" RequestURI: ", r.RequestURI)
for name, _ := range r.Form {
logger.Info(" Form.Get(\"", name, "\"): ", r.Form.Get(name))
}
for name, _ := range r.PostForm {
logger.Info(" PostForm.Get(\"", name, "\"): ", r.PostForm.Get(name))
}
body, err := ioutil.ReadAll(r.Body)
if err == nil {
sb := string(body)
logger.Info(" Body - read by ioutil.ReadAll(): ", sb)
}
}
func Authenticate(rw http.ResponseWriter, r *http.Request) {
// Get the global config - it's needed in various places
conf := config.Global()
// Create a Redis Controller, which will handle the Redis connection for the storage
rc := storage.NewRedisController(r.Context())
// Create a storage object, which will handle Redis operations using "apikey-" key prefix
store := storage.RedisCluster{KeyPrefix: "apikey-", HashKeys: conf.HashKeys, RedisController: rc}
go rc.ConnectToRedis(r.Context(), nil, &conf)
for i := 0; i < 5; i++ { // max 5 attempts - should only take 2
if rc.Connected() {
logger.Info("Redis Controller connected")
break
}
logger.Warn("Redis Controller not connected, will retry")
time.Sleep(10 * time.Millisecond)
}
if !rc.Connected() {
logger.Error("Could not connect to storage")
rw.WriteHeader(http.StatusInternalServerError)
return
}
if conf.HashKeys {
logger.Info("Key hashing is enabled using ", conf.HashKeyFunction)
} else {
logger.Info("Key hashing is disabled")
}
// Get the Authorization header value
authHeader := r.Header.Get("Authorization")
logger.Info("Authorization header: ", authHeader)
requestedAPI := ctx.GetDefinition(r)
if requestedAPI == nil {
logger.Error("Could not get API Definition")
rw.WriteHeader(http.StatusInternalServerError)
return
}
// Calculate the key lookup value
lookupKey := authHeader
if storage.TokenOrg(authHeader) == requestedAPI.OrgID {
// Standard keys don't any special treatment
logger.Info("Key is an encoded standard key")
// Log some useful info
base64decoded, _ := base64.StdEncoding.DecodeString(authHeader)
logger.Info("Base64 decoded standard key: ", string(base64decoded))
} else {
// Custom keys need to be converted to a JSON key for lookup purposes
logger.Info("Key is a custom key")
jsonKey := fmt.Sprintf(`{"org":"%s","id":"%s","h":"%s"}`, requestedAPI.OrgID, authHeader, conf.HashKeyFunction)
logger.Info("Generated JSON for custom key: ", jsonKey)
lookupKey = base64.StdEncoding.EncodeToString([]byte(jsonKey))
}
// This is the key we will be looking for
logger.Info("Lookup key: ", lookupKey)
// Check if key exists
exists, err := store.Exists(lookupKey)
if err != nil {
logger.Error("Couldn't check if key exists in Redis: ", err)
rw.WriteHeader(http.StatusInternalServerError)
return
}
if !exists {
logger.Info("No session object for key")
rw.WriteHeader(http.StatusUnauthorized)
return
}
// Use key to get session from Redis storage
sessionJson, err := store.GetKey(lookupKey)
if err != nil {
logger.Error("Couldn't get session from Redis: ", err)
rw.WriteHeader(http.StatusInternalServerError)
return
}
// Convert to session object
sessionObject := &user.SessionState{}
err = json.Unmarshal([]byte(sessionJson), &sessionObject)
if err != nil {
logger.Error("Couldn't unmarshal session object: ", err)
}
// Check if session object has access rights for the requested API
// Note: This authorization functionality is provided as an example, for educational purposes. It isn't actually necessary to do this here, as subsequent middleware modules will perform this test.
isAuthorized := false
for k := range sessionObject.AccessRights {
if k == requestedAPI.APIID {
isAuthorized = true
logger.Info("Found access rights for ", requestedAPI.Name)
}
}
if !isAuthorized {
logger.Info("Session does not have access to requested API: ", requestedAPI.Name)
rw.WriteHeader(http.StatusForbidden)
return
}
// Set session state using session object
ctx.SetSession(r, sessionObject, false)
logger.Info("Session created for request")
}
// AddHelloWorldHeader adds custom "Hello: World" header to the request
func AddHelloWorldHeader(rw http.ResponseWriter, r *http.Request) {
r.Header.Add("Hello", "World")
}
// Called once plugin is loaded, this is where we put all initialization work for plugin
// i.e. setting exported functions, setting up connection pool to storage and etc.
func init() {
logger.Info("Initialising Example Go Plugin")
}
func main() {}