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  }