@@ -2,9 +2,16 @@ package keystone
2
2
3
3
import (
4
4
"bytes"
5
+ "crypto/tls"
6
+ "crypto/x509"
5
7
"encoding/json"
6
8
"errors"
9
+ "io/ioutil"
7
10
"net/http"
11
+
12
+ "fmt"
13
+ "github.com/grafana/grafana/pkg/log"
14
+ "github.com/grafana/grafana/pkg/setting"
8
15
)
9
16
10
17
///////////////////////
@@ -18,7 +25,7 @@ type auth_request_struct struct {
18
25
19
26
type auth_struct struct {
20
27
Identity auth_identity_struct `json:"identity"`
21
- Scope string `json:"scope"`
28
+ Scope string `json:"scope,omitempty "`
22
29
}
23
30
24
31
type scoped_auth_token_request_struct struct {
@@ -30,15 +37,13 @@ type scoped_auth_password_request_struct struct {
30
37
}
31
38
32
39
type scoped_auth_token_struct struct {
33
- Nocatalog bool `json:"nocatalog"`
34
- Identity auth_scoped_identity_struct `json:"identity"`
35
- Scope auth_scope_struct `json:"scope"`
40
+ Identity auth_scoped_identity_struct `json:"identity"`
41
+ Scope auth_scope_struct `json:"scope"`
36
42
}
37
43
38
44
type scoped_auth_password_struct struct {
39
- Nocatalog bool `json:"nocatalog"`
40
- Identity auth_identity_struct `json:"identity"`
41
- Scope auth_scope_struct `json:"scope"`
45
+ Identity auth_identity_struct `json:"identity"`
46
+ Scope auth_scope_struct `json:"scope"`
42
47
}
43
48
44
49
type auth_scoped_identity_struct struct {
@@ -127,6 +132,7 @@ type Auth_data struct {
127
132
128
133
func AuthenticateScoped (data * Auth_data ) error {
129
134
if data .UnscopedToken != "" {
135
+ log .Trace ("AuthenticateScoped() with token" )
130
136
var auth_post scoped_auth_token_request_struct
131
137
auth_post .Auth .Identity .Methods = []string {"token" }
132
138
auth_post .Auth .Identity .Token .Id = data .UnscopedToken
@@ -136,7 +142,7 @@ func AuthenticateScoped(data *Auth_data) error {
136
142
return authenticate (data , b )
137
143
} else {
138
144
var auth_post scoped_auth_password_request_struct
139
- auth_post . Auth . Nocatalog = true
145
+ log . Trace ( "AuthenticateScoped() with password" )
140
146
auth_post .Auth .Identity .Methods = []string {"password" }
141
147
auth_post .Auth .Identity .Password .User .Name = data .Username
142
148
auth_post .Auth .Identity .Password .User .Password = data .Password
@@ -149,6 +155,7 @@ func AuthenticateScoped(data *Auth_data) error {
149
155
}
150
156
151
157
func AuthenticateUnscoped (data * Auth_data ) error {
158
+ log .Trace ("AuthenticateUnscoped()" )
152
159
var auth_post auth_request_struct
153
160
auth_post .Auth .Scope = "unscoped"
154
161
auth_post .Auth .Identity .Methods = []string {"password" }
@@ -159,22 +166,43 @@ func AuthenticateUnscoped(data *Auth_data) error {
159
166
160
167
return authenticate (data , b )
161
168
}
162
-
163
169
func authenticate (data * Auth_data , b []byte ) error {
164
- request , err := http .NewRequest ("POST" , data .Server + "/v3/auth/tokens" , bytes .NewBuffer (b ))
170
+ auth_url := data .Server + "/v3/auth/tokens?nocatalog"
171
+
172
+ log .Debug ("Authentication request to URL: %s" , auth_url )
173
+
174
+ log .Debug ("Authentication request body: \n %s" , anonymisePasswordsTokens (data , b ))
175
+
176
+ request , err := http .NewRequest ("POST" , auth_url , bytes .NewBuffer (b ))
165
177
if err != nil {
166
178
return err
167
179
}
168
180
169
- client := & http.Client {}
170
- resp , err := client .Do (request )
181
+ resp , err := GetHttpClient ().Do (request )
171
182
if err != nil {
172
183
return err
173
- } else if resp .StatusCode != 201 {
184
+ }
185
+ defer resp .Body .Close ()
186
+
187
+ if resp .StatusCode != 201 {
174
188
return errors .New ("Keystone authentication failed: " + resp .Status )
175
189
}
176
190
177
- decoder := json .NewDecoder (resp .Body )
191
+ var decoder * json.Decoder
192
+
193
+ if log .IsDebug () {
194
+ buf := new (bytes.Buffer )
195
+ buf .ReadFrom (resp .Body )
196
+ strBody := buf .Bytes ()
197
+
198
+ log .Debug ("Authentication response: \n %s" , strBody )
199
+
200
+ bodyReader := bytes .NewBufferString (fmt .Sprintf ("%s" , strBody ))
201
+ decoder = json .NewDecoder (bodyReader )
202
+ } else {
203
+ decoder = json .NewDecoder (resp .Body )
204
+ }
205
+
178
206
var auth_response auth_response_struct
179
207
err = decoder .Decode (& auth_response )
180
208
if err != nil {
@@ -188,6 +216,20 @@ func authenticate(data *Auth_data, b []byte) error {
188
216
return nil
189
217
}
190
218
219
+ func anonymisePasswordsTokens (data * Auth_data , json []byte ) []byte {
220
+ anonJson := json
221
+ if data .Password != "" {
222
+ anonJson = bytes .Replace (anonJson , []byte ("\" password\" :\" " + data .Password + "\" " ),
223
+ []byte ("\" password\" :\" ********\" " ), - 1 )
224
+ }
225
+ if data .UnscopedToken != "" {
226
+ anonJson = bytes .Replace (anonJson , []byte ("\" token\" :{\" id\" :\" " + data .UnscopedToken + "\" " ),
227
+ []byte ("\" token\" :{\" id\" :\" ****************\" " ), - 1 )
228
+ }
229
+
230
+ return anonJson
231
+ }
232
+
191
233
// Projects Section
192
234
type Projects_data struct {
193
235
Token string
@@ -197,21 +239,39 @@ type Projects_data struct {
197
239
}
198
240
199
241
func GetProjects (data * Projects_data ) error {
242
+ log .Info ("Authentication request to URL: %s" , data .Server + "/v3/auth/projects" )
243
+
200
244
request , err := http .NewRequest ("GET" , data .Server + "/v3/auth/projects" , nil )
201
245
if err != nil {
202
246
return err
203
247
}
204
248
request .Header .Add ("X-Auth-Token" , data .Token )
205
249
206
- client := & http.Client {}
207
- resp , err := client .Do (request )
250
+ resp , err := GetHttpClient ().Do (request )
208
251
if err != nil {
209
252
return err
210
- } else if resp .StatusCode != 200 {
253
+ }
254
+ defer resp .Body .Close ()
255
+
256
+ if resp .StatusCode != 200 {
211
257
return errors .New ("Keystone project-list failed: " + resp .Status )
212
258
}
213
259
214
- decoder := json .NewDecoder (resp .Body )
260
+ var decoder * json.Decoder
261
+
262
+ if log .IsDebug () {
263
+ buf := new (bytes.Buffer )
264
+ buf .ReadFrom (resp .Body )
265
+ strBody := buf .Bytes ()
266
+
267
+ log .Debug ("Projects response: \n %s" , strBody )
268
+
269
+ bodyReader := bytes .NewBufferString (fmt .Sprintf ("%s" , strBody ))
270
+ decoder = json .NewDecoder (bodyReader )
271
+ } else {
272
+ decoder = json .NewDecoder (resp .Body )
273
+ }
274
+
215
275
var project_response project_response_struct
216
276
err = decoder .Decode (& project_response )
217
277
if err != nil {
@@ -224,3 +284,35 @@ func GetProjects(data *Projects_data) error {
224
284
}
225
285
return nil
226
286
}
287
+
288
+ // From https://golang.org/pkg/net/http:
289
+ // "Clients and Transports are safe for concurrent use by multiple goroutines and for efficiency should only be created once and re-used."
290
+ var client * http.Client
291
+
292
+ func GetHttpClient () * http.Client {
293
+ if client != nil {
294
+ return client
295
+ } else {
296
+ var certPool * x509.CertPool
297
+ if pemfile := setting .KeystoneRootCAPEMFile ; pemfile != "" {
298
+ certPool = x509 .NewCertPool ()
299
+ pemFileContent , err := ioutil .ReadFile (pemfile )
300
+ if err != nil {
301
+ panic (err )
302
+ }
303
+ if ! certPool .AppendCertsFromPEM (pemFileContent ) {
304
+ log .Error (3 , "Failed to load any certificates from Root CA PEM file %s" , pemfile )
305
+ } else {
306
+ log .Info ("Successfully loaded certificate(s) from %s" , pemfile )
307
+ }
308
+ }
309
+ tr := & http.Transport {
310
+ TLSClientConfig : & tls.Config {RootCAs : certPool ,
311
+ InsecureSkipVerify : ! setting .KeystoneVerifySSLCert },
312
+ }
313
+ tr .Proxy = http .ProxyFromEnvironment
314
+
315
+ client = & http.Client {Transport : tr }
316
+ return client
317
+ }
318
+ }
0 commit comments