github.com/ttys3/engine@v17.12.1-ce-rc2+incompatible/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 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 TestResponseModifierOverride(t *testing.T) { 176 r := httptest.NewRecorder() 177 m := NewResponseModifier(r) 178 m.Header().Set("h1", "v1") 179 m.Write([]byte("body")) 180 m.WriteHeader(http.StatusInternalServerError) 181 182 overrideHeader := make(http.Header) 183 overrideHeader.Add("h1", "v2") 184 overrideHeaderBytes, err := json.Marshal(overrideHeader) 185 if err != nil { 186 t.Fatalf("override header failed %v", err) 187 } 188 189 m.OverrideHeader(overrideHeaderBytes) 190 m.OverrideBody([]byte("override body")) 191 m.OverrideStatusCode(http.StatusNotFound) 192 m.FlushAll() 193 if r.Header().Get("h1") != "v2" { 194 t.Fatalf("Header value must exists %s", r.Header().Get("h1")) 195 } 196 if !reflect.DeepEqual(r.Body.Bytes(), []byte("override body")) { 197 t.Fatalf("Body value must exists %s", r.Body.Bytes()) 198 } 199 if r.Code != http.StatusNotFound { 200 t.Fatalf("Status code must be correct %d", r.Code) 201 } 202 } 203 204 // createTestPlugin creates a new sample authorization plugin 205 func createTestPlugin(t *testing.T) *authorizationPlugin { 206 pwd, err := os.Getwd() 207 if err != nil { 208 t.Fatal(err) 209 } 210 211 client, err := plugins.NewClient("unix:///"+path.Join(pwd, pluginAddress), &tlsconfig.Options{InsecureSkipVerify: true}) 212 if err != nil { 213 t.Fatalf("Failed to create client %v", err) 214 } 215 216 return &authorizationPlugin{name: "plugin", plugin: client} 217 } 218 219 // AuthZPluginTestServer is a simple server that implements the authZ plugin interface 220 type authZPluginTestServer struct { 221 listener net.Listener 222 t *testing.T 223 // request stores the request sent from the daemon to the plugin 224 recordedRequest Request 225 // response stores the response sent from the plugin to the daemon 226 replayResponse Response 227 server *httptest.Server 228 } 229 230 // start starts the test server that implements the plugin 231 func (t *authZPluginTestServer) start() { 232 r := mux.NewRouter() 233 l, err := net.Listen("unix", pluginAddress) 234 if err != nil { 235 t.t.Fatal(err) 236 } 237 t.listener = l 238 r.HandleFunc("/Plugin.Activate", t.activate) 239 r.HandleFunc("/"+AuthZApiRequest, t.auth) 240 r.HandleFunc("/"+AuthZApiResponse, t.auth) 241 t.server = &httptest.Server{ 242 Listener: l, 243 Config: &http.Server{ 244 Handler: r, 245 Addr: pluginAddress, 246 }, 247 } 248 t.server.Start() 249 } 250 251 // stop stops the test server that implements the plugin 252 func (t *authZPluginTestServer) stop() { 253 t.server.Close() 254 os.Remove(pluginAddress) 255 if t.listener != nil { 256 t.listener.Close() 257 } 258 } 259 260 // auth is a used to record/replay the authentication api messages 261 func (t *authZPluginTestServer) auth(w http.ResponseWriter, r *http.Request) { 262 t.recordedRequest = Request{} 263 body, err := ioutil.ReadAll(r.Body) 264 if err != nil { 265 t.t.Fatal(err) 266 } 267 r.Body.Close() 268 json.Unmarshal(body, &t.recordedRequest) 269 b, err := json.Marshal(t.replayResponse) 270 if err != nil { 271 t.t.Fatal(err) 272 } 273 w.Write(b) 274 } 275 276 func (t *authZPluginTestServer) activate(w http.ResponseWriter, r *http.Request) { 277 b, err := json.Marshal(plugins.Manifest{Implements: []string{AuthZApiImplements}}) 278 if err != nil { 279 t.t.Fatal(err) 280 } 281 w.Write(b) 282 }