github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/pkg/authorization/authz_unix_test.go (about) 1 // +build !windows 2 3 // TODO Windows: This uses a Unix socket for testing. This might be possible 4 // to port to Windows using a named pipe instead. 5 6 package authorization // import "github.com/docker/docker/pkg/authorization" 7 8 import ( 9 "bytes" 10 "encoding/json" 11 "io/ioutil" 12 "net" 13 "net/http" 14 "net/http/httptest" 15 "os" 16 "path" 17 "reflect" 18 "strings" 19 "testing" 20 21 "github.com/docker/docker/pkg/plugins" 22 "github.com/docker/go-connections/tlsconfig" 23 "github.com/gorilla/mux" 24 ) 25 26 const ( 27 pluginAddress = "authz-test-plugin.sock" 28 ) 29 30 func TestAuthZRequestPluginError(t *testing.T) { 31 server := authZPluginTestServer{t: t} 32 server.start() 33 defer server.stop() 34 35 authZPlugin := createTestPlugin(t) 36 37 request := Request{ 38 User: "user", 39 RequestBody: []byte("sample body"), 40 RequestURI: "www.authz.com/auth", 41 RequestMethod: "GET", 42 RequestHeaders: map[string]string{"header": "value"}, 43 } 44 server.replayResponse = Response{ 45 Err: "an error", 46 } 47 48 actualResponse, err := authZPlugin.AuthZRequest(&request) 49 if err != nil { 50 t.Fatalf("Failed to authorize request %v", err) 51 } 52 53 if !reflect.DeepEqual(server.replayResponse, *actualResponse) { 54 t.Fatal("Response must be equal") 55 } 56 if !reflect.DeepEqual(request, server.recordedRequest) { 57 t.Fatal("Requests must be equal") 58 } 59 } 60 61 func TestAuthZRequestPlugin(t *testing.T) { 62 server := authZPluginTestServer{t: t} 63 server.start() 64 defer server.stop() 65 66 authZPlugin := createTestPlugin(t) 67 68 request := Request{ 69 User: "user", 70 RequestBody: []byte("sample body"), 71 RequestURI: "www.authz.com/auth", 72 RequestMethod: "GET", 73 RequestHeaders: map[string]string{"header": "value"}, 74 } 75 server.replayResponse = Response{ 76 Allow: true, 77 Msg: "Sample message", 78 } 79 80 actualResponse, err := authZPlugin.AuthZRequest(&request) 81 if err != nil { 82 t.Fatalf("Failed to authorize request %v", err) 83 } 84 85 if !reflect.DeepEqual(server.replayResponse, *actualResponse) { 86 t.Fatal("Response must be equal") 87 } 88 if !reflect.DeepEqual(request, server.recordedRequest) { 89 t.Fatal("Requests must be equal") 90 } 91 } 92 93 func TestAuthZResponsePlugin(t *testing.T) { 94 server := authZPluginTestServer{t: t} 95 server.start() 96 defer server.stop() 97 98 authZPlugin := createTestPlugin(t) 99 100 request := Request{ 101 User: "user", 102 RequestURI: "something.com/auth", 103 RequestBody: []byte("sample body"), 104 } 105 server.replayResponse = Response{ 106 Allow: true, 107 Msg: "Sample message", 108 } 109 110 actualResponse, err := authZPlugin.AuthZResponse(&request) 111 if err != nil { 112 t.Fatalf("Failed to authorize request %v", err) 113 } 114 115 if !reflect.DeepEqual(server.replayResponse, *actualResponse) { 116 t.Fatal("Response must be equal") 117 } 118 if !reflect.DeepEqual(request, server.recordedRequest) { 119 t.Fatal("Requests must be equal") 120 } 121 } 122 123 func TestResponseModifier(t *testing.T) { 124 r := httptest.NewRecorder() 125 m := NewResponseModifier(r) 126 m.Header().Set("h1", "v1") 127 m.Write([]byte("body")) 128 m.WriteHeader(http.StatusInternalServerError) 129 130 m.FlushAll() 131 if r.Header().Get("h1") != "v1" { 132 t.Fatalf("Header value must exists %s", r.Header().Get("h1")) 133 } 134 if !reflect.DeepEqual(r.Body.Bytes(), []byte("body")) { 135 t.Fatalf("Body value must exists %s", r.Body.Bytes()) 136 } 137 if r.Code != http.StatusInternalServerError { 138 t.Fatalf("Status code must be correct %d", r.Code) 139 } 140 } 141 142 func TestDrainBody(t *testing.T) { 143 tests := []struct { 144 length int // length is the message length send to drainBody 145 expectedBodyLength int // expectedBodyLength is the expected body length after drainBody is called 146 }{ 147 {10, 10}, // Small message size 148 {maxBodySize - 1, maxBodySize - 1}, // Max message size 149 {maxBodySize * 2, 0}, // Large message size (skip copying body) 150 151 } 152 153 for _, test := range tests { 154 msg := strings.Repeat("a", test.length) 155 body, closer, err := drainBody(ioutil.NopCloser(bytes.NewReader([]byte(msg)))) 156 if err != nil { 157 t.Fatal(err) 158 } 159 if len(body) != test.expectedBodyLength { 160 t.Fatalf("Body must be copied, actual length: '%d'", len(body)) 161 } 162 if closer == nil { 163 t.Fatal("Closer must not be nil") 164 } 165 modified, err := ioutil.ReadAll(closer) 166 if err != nil { 167 t.Fatalf("Error must not be nil: '%v'", err) 168 } 169 if len(modified) != len(msg) { 170 t.Fatalf("Result should not be truncated. Original length: '%d', new length: '%d'", len(msg), len(modified)) 171 } 172 } 173 } 174 175 func TestSendBody(t *testing.T) { 176 var ( 177 testcases = []struct { 178 url string 179 contentType string 180 expected bool 181 }{ 182 { 183 contentType: "application/json", 184 expected: true, 185 }, 186 { 187 contentType: "Application/json", 188 expected: true, 189 }, 190 { 191 contentType: "application/JSON", 192 expected: true, 193 }, 194 { 195 contentType: "APPLICATION/JSON", 196 expected: true, 197 }, 198 { 199 contentType: "application/json; charset=utf-8", 200 expected: true, 201 }, 202 { 203 contentType: "application/json;charset=utf-8", 204 expected: true, 205 }, 206 { 207 contentType: "application/json; charset=UTF8", 208 expected: true, 209 }, 210 { 211 contentType: "application/json;charset=UTF8", 212 expected: true, 213 }, 214 { 215 contentType: "text/html", 216 expected: false, 217 }, 218 { 219 contentType: "", 220 expected: false, 221 }, 222 { 223 url: "nothing.com/auth", 224 contentType: "", 225 expected: false, 226 }, 227 { 228 url: "nothing.com/auth", 229 contentType: "application/json;charset=UTF8", 230 expected: false, 231 }, 232 { 233 url: "nothing.com/auth?p1=test", 234 contentType: "application/json;charset=UTF8", 235 expected: false, 236 }, 237 { 238 url: "nothing.com/test?p1=/auth", 239 contentType: "application/json;charset=UTF8", 240 expected: true, 241 }, 242 { 243 url: "nothing.com/something/auth", 244 contentType: "application/json;charset=UTF8", 245 expected: true, 246 }, 247 { 248 url: "nothing.com/auth/test", 249 contentType: "application/json;charset=UTF8", 250 expected: false, 251 }, 252 { 253 url: "nothing.com/v1.24/auth/test", 254 contentType: "application/json;charset=UTF8", 255 expected: false, 256 }, 257 { 258 url: "nothing.com/v1/auth/test", 259 contentType: "application/json;charset=UTF8", 260 expected: false, 261 }, 262 { 263 url: "www.nothing.com/v1.24/auth/test", 264 contentType: "application/json;charset=UTF8", 265 expected: false, 266 }, 267 { 268 url: "https://www.nothing.com/v1.24/auth/test", 269 contentType: "application/json;charset=UTF8", 270 expected: false, 271 }, 272 { 273 url: "http://nothing.com/v1.24/auth/test", 274 contentType: "application/json;charset=UTF8", 275 expected: false, 276 }, 277 { 278 url: "www.nothing.com/test?p1=/auth", 279 contentType: "application/json;charset=UTF8", 280 expected: true, 281 }, 282 { 283 url: "http://www.nothing.com/test?p1=/auth", 284 contentType: "application/json;charset=UTF8", 285 expected: true, 286 }, 287 { 288 url: "www.nothing.com/something/auth", 289 contentType: "application/json;charset=UTF8", 290 expected: true, 291 }, 292 { 293 url: "https://www.nothing.com/something/auth", 294 contentType: "application/json;charset=UTF8", 295 expected: true, 296 }, 297 } 298 ) 299 300 for _, testcase := range testcases { 301 header := http.Header{} 302 header.Set("Content-Type", testcase.contentType) 303 if testcase.url == "" { 304 testcase.url = "nothing.com" 305 } 306 307 if b := sendBody(testcase.url, header); b != testcase.expected { 308 t.Fatalf("sendBody failed: url: %s, content-type: %s; Expected: %t, Actual: %t", testcase.url, testcase.contentType, testcase.expected, b) 309 } 310 } 311 } 312 313 func TestResponseModifierOverride(t *testing.T) { 314 r := httptest.NewRecorder() 315 m := NewResponseModifier(r) 316 m.Header().Set("h1", "v1") 317 m.Write([]byte("body")) 318 m.WriteHeader(http.StatusInternalServerError) 319 320 overrideHeader := make(http.Header) 321 overrideHeader.Add("h1", "v2") 322 overrideHeaderBytes, err := json.Marshal(overrideHeader) 323 if err != nil { 324 t.Fatalf("override header failed %v", err) 325 } 326 327 m.OverrideHeader(overrideHeaderBytes) 328 m.OverrideBody([]byte("override body")) 329 m.OverrideStatusCode(http.StatusNotFound) 330 m.FlushAll() 331 if r.Header().Get("h1") != "v2" { 332 t.Fatalf("Header value must exists %s", r.Header().Get("h1")) 333 } 334 if !reflect.DeepEqual(r.Body.Bytes(), []byte("override body")) { 335 t.Fatalf("Body value must exists %s", r.Body.Bytes()) 336 } 337 if r.Code != http.StatusNotFound { 338 t.Fatalf("Status code must be correct %d", r.Code) 339 } 340 } 341 342 // createTestPlugin creates a new sample authorization plugin 343 func createTestPlugin(t *testing.T) *authorizationPlugin { 344 pwd, err := os.Getwd() 345 if err != nil { 346 t.Fatal(err) 347 } 348 349 client, err := plugins.NewClient("unix:///"+path.Join(pwd, pluginAddress), &tlsconfig.Options{InsecureSkipVerify: true}) 350 if err != nil { 351 t.Fatalf("Failed to create client %v", err) 352 } 353 354 return &authorizationPlugin{name: "plugin", plugin: client} 355 } 356 357 // AuthZPluginTestServer is a simple server that implements the authZ plugin interface 358 type authZPluginTestServer struct { 359 listener net.Listener 360 t *testing.T 361 // request stores the request sent from the daemon to the plugin 362 recordedRequest Request 363 // response stores the response sent from the plugin to the daemon 364 replayResponse Response 365 server *httptest.Server 366 } 367 368 // start starts the test server that implements the plugin 369 func (t *authZPluginTestServer) start() { 370 r := mux.NewRouter() 371 l, err := net.Listen("unix", pluginAddress) 372 if err != nil { 373 t.t.Fatal(err) 374 } 375 t.listener = l 376 r.HandleFunc("/Plugin.Activate", t.activate) 377 r.HandleFunc("/"+AuthZApiRequest, t.auth) 378 r.HandleFunc("/"+AuthZApiResponse, t.auth) 379 t.server = &httptest.Server{ 380 Listener: l, 381 Config: &http.Server{ 382 Handler: r, 383 Addr: pluginAddress, 384 }, 385 } 386 t.server.Start() 387 } 388 389 // stop stops the test server that implements the plugin 390 func (t *authZPluginTestServer) stop() { 391 t.server.Close() 392 os.Remove(pluginAddress) 393 if t.listener != nil { 394 t.listener.Close() 395 } 396 } 397 398 // auth is a used to record/replay the authentication api messages 399 func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) { 400 t.recordedRequest = Request{} 401 body, err := ioutil.ReadAll(r.Body) 402 if err != nil { 403 t.t.Fatal(err) 404 } 405 r.Body.Close() 406 json.Unmarshal(body, &t.recordedRequest) 407 b, err := json.Marshal(t.replayResponse) 408 if err != nil { 409 t.t.Fatal(err) 410 } 411 w.Write(b) 412 } 413 414 func (t *authZPluginTestServer) activate(w http.ResponseWriter, r *http.Request) { 415 b, err := json.Marshal(plugins.Manifest{Implements: []string{AuthZApiImplements}}) 416 if err != nil { 417 t.t.Fatal(err) 418 } 419 w.Write(b) 420 }