github.com/vnforks/kid@v5.11.1+incompatible/app/oauth_test.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "encoding/base64" 8 "encoding/json" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "testing" 13 14 "github.com/mattermost/mattermost-server/model" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func TestGetOAuthAccessTokenForImplicitFlow(t *testing.T) { 20 th := Setup(t).InitBasic() 21 defer th.TearDown() 22 23 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true }) 24 25 oapp := &model.OAuthApp{ 26 Name: "fakeoauthapp" + model.NewRandomString(10), 27 CreatorId: th.BasicUser2.Id, 28 Homepage: "https://nowhere.com", 29 Description: "test", 30 CallbackUrls: []string{"https://nowhere.com"}, 31 } 32 33 oapp, err := th.App.CreateOAuthApp(oapp) 34 require.Nil(t, err) 35 36 authRequest := &model.AuthorizeRequest{ 37 ResponseType: model.IMPLICIT_RESPONSE_TYPE, 38 ClientId: oapp.Id, 39 RedirectUri: oapp.CallbackUrls[0], 40 Scope: "", 41 State: "123", 42 } 43 44 session, err := th.App.GetOAuthAccessTokenForImplicitFlow(th.BasicUser.Id, authRequest) 45 assert.Nil(t, err) 46 assert.NotNil(t, session) 47 48 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = false }) 49 50 session, err = th.App.GetOAuthAccessTokenForImplicitFlow(th.BasicUser.Id, authRequest) 51 assert.NotNil(t, err, "should fail - oauth2 disabled") 52 assert.Nil(t, session) 53 54 th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true }) 55 authRequest.ClientId = "junk" 56 57 session, err = th.App.GetOAuthAccessTokenForImplicitFlow(th.BasicUser.Id, authRequest) 58 assert.NotNil(t, err, "should fail - bad client id") 59 assert.Nil(t, session) 60 61 authRequest.ClientId = oapp.Id 62 63 session, err = th.App.GetOAuthAccessTokenForImplicitFlow("junk", authRequest) 64 assert.NotNil(t, err, "should fail - bad user id") 65 assert.Nil(t, session) 66 } 67 68 func TestOAuthRevokeAccessToken(t *testing.T) { 69 th := Setup(t) 70 defer th.TearDown() 71 72 if err := th.App.RevokeAccessToken(model.NewRandomString(16)); err == nil { 73 t.Fatal("Should have failed bad token") 74 } 75 76 session := &model.Session{} 77 session.CreateAt = model.GetMillis() 78 session.UserId = model.NewId() 79 session.Token = model.NewId() 80 session.Roles = model.SYSTEM_USER_ROLE_ID 81 session.SetExpireInDays(1) 82 83 session, _ = th.App.CreateSession(session) 84 if err := th.App.RevokeAccessToken(session.Token); err == nil { 85 t.Fatal("Should have failed does not have an access token") 86 } 87 88 accessData := &model.AccessData{} 89 accessData.Token = session.Token 90 accessData.UserId = session.UserId 91 accessData.RedirectUri = "http://example.com" 92 accessData.ClientId = model.NewId() 93 accessData.ExpiresAt = session.ExpiresAt 94 95 if result := <-th.App.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil { 96 t.Fatal(result.Err) 97 } 98 99 if err := th.App.RevokeAccessToken(accessData.Token); err != nil { 100 t.Fatal(err) 101 } 102 } 103 104 func TestOAuthDeleteApp(t *testing.T) { 105 th := Setup(t) 106 defer th.TearDown() 107 108 *th.App.Config().ServiceSettings.EnableOAuthServiceProvider = true 109 110 a1 := &model.OAuthApp{} 111 a1.CreatorId = model.NewId() 112 a1.Name = "TestApp" + model.NewId() 113 a1.CallbackUrls = []string{"https://nowhere.com"} 114 a1.Homepage = "https://nowhere.com" 115 116 var err *model.AppError 117 a1, err = th.App.CreateOAuthApp(a1) 118 if err != nil { 119 t.Fatal(err) 120 } 121 122 session := &model.Session{} 123 session.CreateAt = model.GetMillis() 124 session.UserId = model.NewId() 125 session.Token = model.NewId() 126 session.Roles = model.SYSTEM_USER_ROLE_ID 127 session.IsOAuth = true 128 session.SetExpireInDays(1) 129 130 session, _ = th.App.CreateSession(session) 131 132 accessData := &model.AccessData{} 133 accessData.Token = session.Token 134 accessData.UserId = session.UserId 135 accessData.RedirectUri = "http://example.com" 136 accessData.ClientId = a1.Id 137 accessData.ExpiresAt = session.ExpiresAt 138 139 if result := <-th.App.Srv.Store.OAuth().SaveAccessData(accessData); result.Err != nil { 140 t.Fatal(result.Err) 141 } 142 143 if err := th.App.DeleteOAuthApp(a1.Id); err != nil { 144 t.Fatal(err) 145 } 146 147 if _, err := th.App.GetSession(session.Token); err == nil { 148 t.Fatal("should not get session from cache or db") 149 } 150 } 151 152 func TestAuthorizeOAuthUser(t *testing.T) { 153 setup := func(enable, tokenEndpoint, userEndpoint bool, serverURL string) *TestHelper { 154 th := Setup(t) 155 156 th.App.UpdateConfig(func(cfg *model.Config) { 157 *cfg.GitLabSettings.Enable = enable 158 159 if tokenEndpoint { 160 *cfg.GitLabSettings.TokenEndpoint = serverURL + "/token" 161 } else { 162 *cfg.GitLabSettings.TokenEndpoint = "" 163 } 164 165 if userEndpoint { 166 *cfg.GitLabSettings.UserApiEndpoint = serverURL + "/user" 167 } else { 168 *cfg.GitLabSettings.UserApiEndpoint = "" 169 } 170 }) 171 172 return th 173 } 174 175 makeState := func(token *model.Token) string { 176 return base64.StdEncoding.EncodeToString([]byte(model.MapToJson(map[string]string{ 177 "token": token.Token, 178 }))) 179 } 180 181 makeToken := func(th *TestHelper, cookie string) *model.Token { 182 token, _ := th.App.CreateOAuthStateToken(generateOAuthStateTokenExtra("", "", cookie)) 183 return token 184 } 185 186 makeRequest := func(t *testing.T, cookie string) *http.Request { 187 request, _ := http.NewRequest(http.MethodGet, "https://mattermost.example.com", nil) 188 189 if cookie != "" { 190 request.AddCookie(&http.Cookie{ 191 Name: COOKIE_OAUTH, 192 Value: cookie, 193 }) 194 } 195 196 return request 197 } 198 199 t.Run("not enabled", func(t *testing.T) { 200 th := setup(false, true, true, "") 201 defer th.TearDown() 202 203 _, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", "", "") 204 require.NotNil(t, err) 205 assert.Equal(t, "api.user.authorize_oauth_user.unsupported.app_error", err.Id) 206 }) 207 208 t.Run("with an improperly encoded state", func(t *testing.T) { 209 th := setup(true, true, true, "") 210 defer th.TearDown() 211 212 state := "!" 213 214 _, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "") 215 require.NotNil(t, err) 216 assert.Equal(t, "api.user.authorize_oauth_user.invalid_state.app_error", err.Id) 217 }) 218 219 t.Run("without a stored token", func(t *testing.T) { 220 th := setup(true, true, true, "") 221 defer th.TearDown() 222 223 state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(map[string]string{ 224 "token": model.NewId(), 225 }))) 226 227 _, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "") 228 require.NotNil(t, err) 229 assert.Equal(t, "api.oauth.invalid_state_token.app_error", err.Id) 230 assert.NotEqual(t, "", err.DetailedError) 231 }) 232 233 t.Run("with a stored token of the wrong type", func(t *testing.T) { 234 th := setup(true, true, true, "") 235 defer th.TearDown() 236 237 token := model.NewToken("invalid", "") 238 result := <-th.App.Srv.Store.Token().Save(token) 239 require.Nil(t, result.Err) 240 241 state := makeState(token) 242 243 _, _, _, err := th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "") 244 require.NotNil(t, err) 245 assert.Equal(t, "api.oauth.invalid_state_token.app_error", err.Id) 246 assert.Equal(t, "", err.DetailedError) 247 }) 248 249 t.Run("with email missing when changing login types", func(t *testing.T) { 250 th := setup(true, true, true, "") 251 defer th.TearDown() 252 253 email := "" 254 action := model.OAUTH_ACTION_EMAIL_TO_SSO 255 cookie := model.NewId() 256 257 token, err := th.App.CreateOAuthStateToken(generateOAuthStateTokenExtra(email, action, cookie)) 258 require.Nil(t, err) 259 260 state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(map[string]string{ 261 "action": action, 262 "email": email, 263 "token": token.Token, 264 }))) 265 266 _, _, _, err = th.App.AuthorizeOAuthUser(nil, nil, model.SERVICE_GITLAB, "", state, "") 267 require.NotNil(t, err) 268 assert.Equal(t, "api.user.authorize_oauth_user.invalid_state.app_error", err.Id) 269 }) 270 271 t.Run("without an OAuth cookie", func(t *testing.T) { 272 th := setup(true, true, true, "") 273 defer th.TearDown() 274 275 cookie := model.NewId() 276 request := makeRequest(t, "") 277 state := makeState(makeToken(th, cookie)) 278 279 _, _, _, err := th.App.AuthorizeOAuthUser(nil, request, model.SERVICE_GITLAB, "", state, "") 280 require.NotNil(t, err) 281 assert.Equal(t, "api.user.authorize_oauth_user.invalid_state.app_error", err.Id) 282 }) 283 284 t.Run("with an invalid token", func(t *testing.T) { 285 th := setup(true, true, true, "") 286 defer th.TearDown() 287 288 cookie := model.NewId() 289 290 token, err := th.App.CreateOAuthStateToken(model.NewId()) 291 require.Nil(t, err) 292 293 request := makeRequest(t, cookie) 294 state := makeState(token) 295 296 _, _, _, err = th.App.AuthorizeOAuthUser(nil, request, model.SERVICE_GITLAB, "", state, "") 297 require.NotNil(t, err) 298 assert.Equal(t, "api.user.authorize_oauth_user.invalid_state.app_error", err.Id) 299 }) 300 301 t.Run("with an incorrect token endpoint", func(t *testing.T) { 302 th := setup(true, false, true, "") 303 defer th.TearDown() 304 305 cookie := model.NewId() 306 request := makeRequest(t, cookie) 307 state := makeState(makeToken(th, cookie)) 308 309 _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 310 require.NotNil(t, err) 311 assert.Equal(t, "api.user.authorize_oauth_user.token_failed.app_error", err.Id) 312 }) 313 314 t.Run("with an error token response", func(t *testing.T) { 315 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 316 w.WriteHeader(http.StatusTeapot) 317 })) 318 defer server.Close() 319 320 th := setup(true, true, true, server.URL) 321 defer th.TearDown() 322 323 cookie := model.NewId() 324 request := makeRequest(t, cookie) 325 state := makeState(makeToken(th, cookie)) 326 327 _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 328 require.NotNil(t, err) 329 assert.Equal(t, "api.user.authorize_oauth_user.bad_response.app_error", err.Id) 330 }) 331 332 t.Run("with an invalid token response", func(t *testing.T) { 333 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 334 w.Write([]byte("invalid")) 335 })) 336 defer server.Close() 337 338 th := setup(true, true, true, server.URL) 339 defer th.TearDown() 340 341 cookie := model.NewId() 342 request := makeRequest(t, cookie) 343 state := makeState(makeToken(th, cookie)) 344 345 _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 346 require.NotNil(t, err) 347 assert.Equal(t, "api.user.authorize_oauth_user.bad_response.app_error", err.Id) 348 }) 349 350 t.Run("with an invalid token type", func(t *testing.T) { 351 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 352 json.NewEncoder(w).Encode(&model.AccessResponse{ 353 AccessToken: model.NewId(), 354 TokenType: "", 355 }) 356 })) 357 defer server.Close() 358 359 th := setup(true, true, true, server.URL) 360 defer th.TearDown() 361 362 cookie := model.NewId() 363 request := makeRequest(t, cookie) 364 state := makeState(makeToken(th, cookie)) 365 366 _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 367 require.NotNil(t, err) 368 assert.Equal(t, "api.user.authorize_oauth_user.bad_token.app_error", err.Id) 369 }) 370 371 t.Run("with an empty token response", func(t *testing.T) { 372 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 373 json.NewEncoder(w).Encode(&model.AccessResponse{ 374 AccessToken: "", 375 TokenType: model.ACCESS_TOKEN_TYPE, 376 }) 377 })) 378 defer server.Close() 379 380 th := setup(true, true, true, server.URL) 381 defer th.TearDown() 382 383 cookie := model.NewId() 384 request := makeRequest(t, cookie) 385 state := makeState(makeToken(th, cookie)) 386 387 _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 388 require.NotNil(t, err) 389 assert.Equal(t, "api.user.authorize_oauth_user.missing.app_error", err.Id) 390 }) 391 392 t.Run("with an incorrect user endpoint", func(t *testing.T) { 393 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 394 json.NewEncoder(w).Encode(&model.AccessResponse{ 395 AccessToken: model.NewId(), 396 TokenType: model.ACCESS_TOKEN_TYPE, 397 }) 398 })) 399 defer server.Close() 400 401 th := setup(true, true, false, server.URL) 402 defer th.TearDown() 403 404 cookie := model.NewId() 405 request := makeRequest(t, cookie) 406 state := makeState(makeToken(th, cookie)) 407 408 _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 409 require.NotNil(t, err) 410 assert.Equal(t, "api.user.authorize_oauth_user.service.app_error", err.Id) 411 }) 412 413 t.Run("with an error user response", func(t *testing.T) { 414 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 415 switch r.URL.Path { 416 case "/token": 417 t.Log("hit token") 418 json.NewEncoder(w).Encode(&model.AccessResponse{ 419 AccessToken: model.NewId(), 420 TokenType: model.ACCESS_TOKEN_TYPE, 421 }) 422 case "/user": 423 t.Log("hit user") 424 w.WriteHeader(http.StatusTeapot) 425 } 426 })) 427 defer server.Close() 428 429 th := setup(true, true, true, server.URL) 430 defer th.TearDown() 431 432 cookie := model.NewId() 433 request := makeRequest(t, cookie) 434 state := makeState(makeToken(th, cookie)) 435 436 _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 437 require.NotNil(t, err) 438 assert.Equal(t, "api.user.authorize_oauth_user.response.app_error", err.Id) 439 }) 440 441 t.Run("with an error user response due to GitLab TOS", func(t *testing.T) { 442 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 443 switch r.URL.Path { 444 case "/token": 445 t.Log("hit token") 446 json.NewEncoder(w).Encode(&model.AccessResponse{ 447 AccessToken: model.NewId(), 448 TokenType: model.ACCESS_TOKEN_TYPE, 449 }) 450 case "/user": 451 t.Log("hit user") 452 w.WriteHeader(http.StatusForbidden) 453 w.Write([]byte("Terms of Service")) 454 } 455 })) 456 defer server.Close() 457 458 th := setup(true, true, true, server.URL) 459 defer th.TearDown() 460 461 cookie := model.NewId() 462 request := makeRequest(t, cookie) 463 state := makeState(makeToken(th, cookie)) 464 465 _, _, _, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 466 require.NotNil(t, err) 467 assert.Equal(t, "oauth.gitlab.tos.error", err.Id) 468 }) 469 470 t.Run("enabled and properly configured", func(t *testing.T) { 471 userData := "Hello, World!" 472 473 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 474 switch r.URL.Path { 475 case "/token": 476 json.NewEncoder(w).Encode(&model.AccessResponse{ 477 AccessToken: model.NewId(), 478 TokenType: model.ACCESS_TOKEN_TYPE, 479 }) 480 case "/user": 481 w.WriteHeader(http.StatusOK) 482 w.Write([]byte(userData)) 483 } 484 })) 485 defer server.Close() 486 487 th := setup(true, true, true, server.URL) 488 defer th.TearDown() 489 490 cookie := model.NewId() 491 request := makeRequest(t, cookie) 492 493 stateProps := map[string]string{ 494 "team_id": model.NewId(), 495 "token": makeToken(th, cookie).Token, 496 } 497 state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps))) 498 499 body, receivedTeamId, receivedStateProps, err := th.App.AuthorizeOAuthUser(&httptest.ResponseRecorder{}, request, model.SERVICE_GITLAB, "", state, "") 500 501 require.NotNil(t, body) 502 bodyBytes, bodyErr := ioutil.ReadAll(body) 503 require.Nil(t, bodyErr) 504 assert.Equal(t, userData, string(bodyBytes)) 505 506 assert.Equal(t, stateProps["team_id"], receivedTeamId) 507 assert.Equal(t, stateProps, receivedStateProps) 508 assert.Nil(t, err) 509 }) 510 }