github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/api/websocket_test.go (about) 1 // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package api 5 6 import ( 7 "fmt" 8 //"encoding/json" 9 //"net/http" 10 "net/http" 11 "testing" 12 "time" 13 14 "github.com/gorilla/websocket" 15 "github.com/mattermost/mattermost-server/model" 16 ) 17 18 /*func TestWebSocketAuthentication(t *testing.T) { 19 th := Setup().InitBasic() 20 WebSocketClient, err := th.CreateWebSocketClient() 21 if err != nil { 22 t.Fatal(err) 23 } 24 WebSocketClient.Listen() 25 26 time.Sleep(300 * time.Millisecond) 27 if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK { 28 t.Fatal("should have responded OK to authentication challenge") 29 } 30 31 WebSocketClient.SendMessage("ping", nil) 32 time.Sleep(300 * time.Millisecond) 33 if resp := <-WebSocketClient.ResponseChannel; resp.Data["text"].(string) != "pong" { 34 t.Fatal("wrong response") 35 } 36 37 WebSocketClient.Close() 38 39 authToken := WebSocketClient.AuthToken 40 WebSocketClient.AuthToken = "junk" 41 if err := WebSocketClient.Connect(); err != nil { 42 t.Fatal(err) 43 } 44 WebSocketClient.Listen() 45 46 if resp := <-WebSocketClient.ResponseChannel; resp != nil { 47 t.Fatal("should have closed") 48 } 49 50 if conn, _, err := websocket.DefaultDialer.Dial(WebSocketClient.ApiUrl+"/users/websocket", nil); err != nil { 51 t.Fatal("should have connected") 52 } else { 53 req := &model.WebSocketRequest{} 54 req.Seq = 1 55 req.Action = "ping" 56 conn.WriteJSON(req) 57 58 closedAutomatically := false 59 hitNotAuthedError := false 60 61 go func() { 62 time.Sleep(10 * time.Second) 63 conn.Close() 64 65 if !closedAutomatically { 66 t.Fatal("should have closed automatically in 5 seconds") 67 } 68 }() 69 70 for { 71 if _, rawMsg, err := conn.ReadMessage(); err != nil { 72 closedAutomatically = true 73 conn.Close() 74 break 75 } else { 76 var response model.WebSocketResponse 77 if err := json.Unmarshal(rawMsg, &response); err != nil && !response.IsValid() { 78 t.Fatal("should not have failed") 79 } else { 80 if response.Error == nil || response.Error.Id != "api.web_socket_router.not_authenticated.app_error" { 81 t.Log(response.Error.Id) 82 t.Fatal("wrong error") 83 continue 84 } 85 86 hitNotAuthedError = true 87 } 88 } 89 } 90 91 if !hitNotAuthedError { 92 t.Fatal("should have received a not authenticated response") 93 } 94 } 95 96 header := http.Header{} 97 header.Set(model.HEADER_AUTH, "BEARER "+authToken) 98 if conn, _, err := websocket.DefaultDialer.Dial(WebSocketClient.ApiUrl+"/users/websocket", header); err != nil { 99 t.Fatal("should have connected") 100 } else { 101 if _, rawMsg, err := conn.ReadMessage(); err != nil { 102 t.Fatal("should not have closed automatically") 103 } else { 104 var event model.WebSocketEvent 105 if err := json.Unmarshal(rawMsg, &event); err != nil && !event.IsValid() { 106 t.Fatal("should not have failed") 107 } else if event.Event != model.WEBSOCKET_EVENT_HELLO { 108 t.Log(event.ToJson()) 109 t.Fatal("should have helloed") 110 } 111 } 112 113 conn.Close() 114 } 115 }*/ 116 117 func TestWebSocket(t *testing.T) { 118 th := Setup().InitBasic() 119 defer th.TearDown() 120 121 WebSocketClient, err := th.CreateWebSocketClient() 122 if err != nil { 123 t.Fatal(err) 124 } 125 defer WebSocketClient.Close() 126 127 time.Sleep(300 * time.Millisecond) 128 129 // Test closing and reconnecting 130 WebSocketClient.Close() 131 if err := WebSocketClient.Connect(); err != nil { 132 t.Fatal(err) 133 } 134 135 WebSocketClient.Listen() 136 137 time.Sleep(300 * time.Millisecond) 138 if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK { 139 t.Fatal("should have responded OK to authentication challenge") 140 } 141 142 WebSocketClient.SendMessage("ping", nil) 143 time.Sleep(300 * time.Millisecond) 144 if resp := <-WebSocketClient.ResponseChannel; resp.Data["text"].(string) != "pong" { 145 t.Fatal("wrong response") 146 } 147 148 WebSocketClient.SendMessage("", nil) 149 time.Sleep(300 * time.Millisecond) 150 if resp := <-WebSocketClient.ResponseChannel; resp.Error.Id != "api.web_socket_router.no_action.app_error" { 151 t.Fatal("should have been no action response") 152 } 153 154 WebSocketClient.SendMessage("junk", nil) 155 time.Sleep(300 * time.Millisecond) 156 if resp := <-WebSocketClient.ResponseChannel; resp.Error.Id != "api.web_socket_router.bad_action.app_error" { 157 t.Fatal("should have been bad action response") 158 } 159 160 req := &model.WebSocketRequest{} 161 req.Seq = 0 162 req.Action = "ping" 163 WebSocketClient.Conn.WriteJSON(req) 164 time.Sleep(300 * time.Millisecond) 165 if resp := <-WebSocketClient.ResponseChannel; resp.Error.Id != "api.web_socket_router.bad_seq.app_error" { 166 t.Fatal("should have been bad action response") 167 } 168 169 WebSocketClient.UserTyping("", "") 170 time.Sleep(300 * time.Millisecond) 171 if resp := <-WebSocketClient.ResponseChannel; resp.Error.Id != "api.websocket_handler.invalid_param.app_error" { 172 t.Fatal("should have been invalid param response") 173 } else { 174 if resp.Error.DetailedError != "" { 175 t.Fatal("detailed error not cleared") 176 } 177 } 178 } 179 180 func TestWebSocketEvent(t *testing.T) { 181 th := Setup().InitBasic() 182 defer th.TearDown() 183 184 WebSocketClient, err := th.CreateWebSocketClient() 185 if err != nil { 186 t.Fatal(err) 187 } 188 defer WebSocketClient.Close() 189 190 WebSocketClient.Listen() 191 192 time.Sleep(300 * time.Millisecond) 193 if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK { 194 t.Fatal("should have responded OK to authentication challenge") 195 } 196 197 omitUser := make(map[string]bool, 1) 198 omitUser["somerandomid"] = true 199 evt1 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", th.BasicChannel.Id, "", omitUser) 200 evt1.Add("user_id", "somerandomid") 201 th.App.Publish(evt1) 202 203 time.Sleep(300 * time.Millisecond) 204 205 stop := make(chan bool) 206 eventHit := false 207 208 go func() { 209 for { 210 select { 211 case resp := <-WebSocketClient.EventChannel: 212 if resp.Event == model.WEBSOCKET_EVENT_TYPING && resp.Data["user_id"].(string) == "somerandomid" { 213 eventHit = true 214 } 215 case <-stop: 216 return 217 } 218 } 219 }() 220 221 time.Sleep(400 * time.Millisecond) 222 223 stop <- true 224 225 if !eventHit { 226 t.Fatal("did not receive typing event") 227 } 228 229 evt2 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", "somerandomid", "", nil) 230 th.App.Publish(evt2) 231 time.Sleep(300 * time.Millisecond) 232 233 eventHit = false 234 235 go func() { 236 for { 237 select { 238 case resp := <-WebSocketClient.EventChannel: 239 if resp.Event == model.WEBSOCKET_EVENT_TYPING { 240 eventHit = true 241 } 242 case <-stop: 243 return 244 } 245 } 246 }() 247 248 time.Sleep(400 * time.Millisecond) 249 250 stop <- true 251 252 if eventHit { 253 t.Fatal("got typing event for bad channel id") 254 } 255 } 256 257 func TestCreateDirectChannelWithSocket(t *testing.T) { 258 th := Setup().InitBasic() 259 defer th.TearDown() 260 261 Client := th.BasicClient 262 user2 := th.BasicUser2 263 264 users := make([]*model.User, 0) 265 users = append(users, user2) 266 267 for i := 0; i < 10; i++ { 268 users = append(users, th.CreateUser(Client)) 269 } 270 271 WebSocketClient, err := th.CreateWebSocketClient() 272 if err != nil { 273 t.Fatal(err) 274 } 275 defer WebSocketClient.Close() 276 WebSocketClient.Listen() 277 278 time.Sleep(300 * time.Millisecond) 279 if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK { 280 t.Fatal("should have responded OK to authentication challenge") 281 } 282 283 wsr := <-WebSocketClient.EventChannel 284 if wsr.Event != model.WEBSOCKET_EVENT_HELLO { 285 t.Fatal("missing hello") 286 } 287 288 stop := make(chan bool) 289 count := 0 290 291 go func() { 292 for { 293 select { 294 case wsr := <-WebSocketClient.EventChannel: 295 if wsr.Event == model.WEBSOCKET_EVENT_DIRECT_ADDED { 296 count = count + 1 297 } 298 299 case <-stop: 300 return 301 } 302 } 303 }() 304 305 for _, user := range users { 306 time.Sleep(100 * time.Millisecond) 307 if _, err := Client.CreateDirectChannel(user.Id); err != nil { 308 t.Fatal("failed to create DM channel") 309 } 310 } 311 312 time.Sleep(5000 * time.Millisecond) 313 314 stop <- true 315 316 if count != len(users) { 317 t.Fatal("We didn't get the proper amount of direct_added messages") 318 } 319 320 } 321 322 func TestWebsocketOriginSecurity(t *testing.T) { 323 th := Setup().InitBasic() 324 defer th.TearDown() 325 326 url := fmt.Sprintf("ws://localhost:%v", th.App.Srv.ListenAddr.Port) 327 328 // Should fail because origin doesn't match 329 _, _, err := websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ 330 "Origin": []string{"http://www.evil.com"}, 331 }) 332 if err == nil { 333 t.Fatal("Should have errored because Origin does not match host! SECURITY ISSUE!") 334 } 335 336 // We are not a browser so we can spoof this just fine 337 _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ 338 "Origin": []string{fmt.Sprintf("http://localhost:%v", th.App.Srv.ListenAddr.Port)}, 339 }) 340 if err != nil { 341 t.Fatal(err) 342 } 343 344 // Should succeed now because open CORS 345 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "*" }) 346 _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ 347 "Origin": []string{"http://www.evil.com"}, 348 }) 349 if err != nil { 350 t.Fatal(err) 351 } 352 353 // Should succeed now because matching CORS 354 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.evil.com" }) 355 _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ 356 "Origin": []string{"http://www.evil.com"}, 357 }) 358 if err != nil { 359 t.Fatal(err) 360 } 361 362 // Should fail because non-matching CORS 363 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.good.com" }) 364 _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ 365 "Origin": []string{"http://www.evil.com"}, 366 }) 367 if err == nil { 368 t.Fatal("Should have errored because Origin contain AllowCorsFrom") 369 } 370 371 // Should fail because non-matching CORS 372 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "http://www.good.com" }) 373 _, _, err = websocket.DefaultDialer.Dial(url+model.API_URL_SUFFIX_V3+"/users/websocket", http.Header{ 374 "Origin": []string{"http://www.good.co"}, 375 }) 376 if err == nil { 377 t.Fatal("Should have errored because Origin does not match host! SECURITY ISSUE!") 378 } 379 380 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.AllowCorsFrom = "" }) 381 }