Files
mostlymatter/web/oauth_test.go
Lev 03f43d7ae5 Convert URLs to fully-qualified for HTTP redirects (#19020)
* Convert URLs to fully-qualified for HTTP redirects

* forgot the tests
2021-11-23 08:51:25 -08:00

823 строки
28 KiB
Go
Исходник Ответственный История

Этот файл содержит неоднозначные символы Юникода
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package web
import (
"context"
"encoding/base64"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/i18n"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/mattermost/mattermost-server/v6/utils"
)
func TestOAuthComplete_AccessDenied(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
c := &Context{
App: th.App,
Params: &Params{
Service: "TestService",
},
}
responseWriter := httptest.NewRecorder()
request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/signup/TestService/complete?error=access_denied", nil)
completeOAuth(c, responseWriter, request)
response := responseWriter.Result()
assert.Equal(t, http.StatusTemporaryRedirect, response.StatusCode)
location, _ := url.Parse(response.Header.Get("Location"))
assert.Equal(t, "oauth_access_denied", location.Query().Get("type"))
assert.Equal(t, "TestService", location.Query().Get("service"))
}
func TestAuthorizeOAuthApp(t *testing.T) {
th := Setup(t).InitBasic()
th.Login(apiClient, th.SystemAdminUser)
defer th.TearDown()
enableOAuth := *th.App.Config().ServiceSettings.EnableOAuthServiceProvider
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth })
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
oapp := &model.OAuthApp{
Name: GenerateTestAppName(),
Homepage: "https://nowhere.com",
Description: "test",
CallbackUrls: []string{"https://nowhere.com"},
CreatorId: th.SystemAdminUser.Id,
}
rapp, appErr := th.App.CreateOAuthApp(oapp)
require.Nil(t, appErr)
authRequest := &model.AuthorizeRequest{
ResponseType: model.AuthCodeResponseType,
ClientId: rapp.Id,
RedirectURI: rapp.CallbackUrls[0],
Scope: "",
State: "123",
}
// Test auth code flow
ruri, _, err := apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
require.NotEmpty(t, ruri, "redirect url should be set")
ru, _ := url.Parse(ruri)
require.NotNil(t, ru, "redirect url unparseable")
require.NotEmpty(t, ru.Query().Get("code"), "authorization code not returned")
require.Equal(t, ru.Query().Get("state"), authRequest.State, "returned state doesn't match")
// Test implicit flow
authRequest.ResponseType = model.ImplicitResponseType
ruri, _, err = apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
require.False(t, ruri == "", "redirect url should be set")
ru, _ = url.Parse(ruri)
require.NotNil(t, ru, "redirect url unparseable")
values, err := url.ParseQuery(ru.Fragment)
require.NoError(t, err)
assert.False(t, values.Get("access_token") == "", "access_token not returned")
assert.Equal(t, authRequest.State, values.Get("state"), "returned state doesn't match")
oldToken := apiClient.AuthToken
apiClient.AuthToken = values.Get("access_token")
_, resp, err := apiClient.AuthorizeOAuthApp(authRequest)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
apiClient.AuthToken = oldToken
authRequest.RedirectURI = ""
_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
authRequest.RedirectURI = "http://somewhereelse.com"
_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
authRequest.RedirectURI = rapp.CallbackUrls[0]
authRequest.ResponseType = ""
_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
authRequest.ResponseType = model.AuthCodeResponseType
authRequest.ClientId = ""
_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
authRequest.ClientId = model.NewId()
_, resp, err = apiClient.AuthorizeOAuthApp(authRequest)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
}
func TestDeauthorizeOAuthApp(t *testing.T) {
th := Setup(t).InitBasic()
th.Login(apiClient, th.SystemAdminUser)
defer th.TearDown()
enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth })
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
oapp := &model.OAuthApp{
Name: GenerateTestAppName(),
Homepage: "https://nowhere.com",
Description: "test",
CallbackUrls: []string{"https://nowhere.com"},
CreatorId: th.SystemAdminUser.Id,
}
rapp, appErr := th.App.CreateOAuthApp(oapp)
require.Nil(t, appErr)
authRequest := &model.AuthorizeRequest{
ResponseType: model.AuthCodeResponseType,
ClientId: rapp.Id,
RedirectURI: rapp.CallbackUrls[0],
Scope: "",
State: "123",
}
_, _, err := apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
_, err = apiClient.DeauthorizeOAuthApp(rapp.Id)
require.NoError(t, err)
resp, err := apiClient.DeauthorizeOAuthApp("junk")
require.Error(t, err)
CheckBadRequestStatus(t, resp)
_, err = apiClient.DeauthorizeOAuthApp(model.NewId())
require.NoError(t, err)
th.Logout(apiClient)
resp, err = apiClient.DeauthorizeOAuthApp(rapp.Id)
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
}
func TestOAuthAccessToken(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
th := Setup(t).InitBasic()
th.Login(apiClient, th.SystemAdminUser)
defer th.TearDown()
enableOAuth := th.App.Config().ServiceSettings.EnableOAuthServiceProvider
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth })
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
defaultRolePermissions := th.SaveDefaultRolePermissions()
defer func() {
th.RestoreDefaultRolePermissions(defaultRolePermissions)
}()
th.AddPermissionToRole(model.PermissionManageOAuth.Id, model.TeamUserRoleId)
th.AddPermissionToRole(model.PermissionManageOAuth.Id, model.SystemUserRoleId)
oauthApp := &model.OAuthApp{
Name: "TestApp5" + model.NewId(),
Homepage: "https://nowhere.com",
Description: "test",
CallbackUrls: []string{"https://nowhere.com"},
CreatorId: th.SystemAdminUser.Id,
}
oauthApp, appErr := th.App.CreateOAuthApp(oauthApp)
require.Nil(t, appErr)
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = false })
data := url.Values{"grant_type": []string{"junk"}, "client_id": []string{"12345678901234567890123456"}, "client_secret": []string{"12345678901234567890123456"}, "code": []string{"junk"}, "redirect_uri": []string{oauthApp.CallbackUrls[0]}}
_, _, err := apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - oauth providing turned off")
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
authRequest := &model.AuthorizeRequest{
ResponseType: model.AuthCodeResponseType,
ClientId: oauthApp.Id,
RedirectURI: oauthApp.CallbackUrls[0],
Scope: "all",
State: "123",
}
redirect, _, err := apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
rurl, _ := url.Parse(redirect)
apiClient.Logout()
data = url.Values{"grant_type": []string{"junk"}, "client_id": []string{oauthApp.Id}, "client_secret": []string{oauthApp.ClientSecret}, "code": []string{rurl.Query().Get("code")}, "redirect_uri": []string{oauthApp.CallbackUrls[0]}}
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - bad grant type")
data.Set("grant_type", model.AccessTokenGrantType)
data.Set("client_id", "")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - missing client id")
data.Set("client_id", "junk")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - bad client id")
data.Set("client_id", oauthApp.Id)
data.Set("client_secret", "")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - missing client secret")
data.Set("client_secret", "junk")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - bad client secret")
data.Set("client_secret", oauthApp.ClientSecret)
data.Set("code", "")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - missing code")
data.Set("code", "junk")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - bad code")
data.Set("code", rurl.Query().Get("code"))
data.Set("redirect_uri", "junk")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - non-matching redirect uri")
// reset data for successful request
data.Set("grant_type", model.AccessTokenGrantType)
data.Set("client_id", oauthApp.Id)
data.Set("client_secret", oauthApp.ClientSecret)
data.Set("code", rurl.Query().Get("code"))
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
token := ""
refreshToken := ""
rsp, _, err := apiClient.GetOAuthAccessToken(data)
require.NoError(t, err)
require.NotEmpty(t, rsp.AccessToken, "access token not returned")
require.NotEmpty(t, rsp.RefreshToken, "refresh token not returned")
token, refreshToken = rsp.AccessToken, rsp.RefreshToken
require.Equal(t, rsp.TokenType, model.AccessTokenType, "access token type incorrect")
_, err = apiClient.DoAPIGet("/oauth_test", "")
require.NoError(t, err)
apiClient.SetOAuthToken("")
_, err = apiClient.DoAPIGet("/oauth_test", "")
require.Error(t, err, "should have failed - no access token provided")
apiClient.SetOAuthToken("badtoken")
_, err = apiClient.DoAPIGet("/oauth_test", "")
require.Error(t, err, "should have failed - bad token provided")
apiClient.SetOAuthToken(token)
_, err = apiClient.DoAPIGet("/oauth_test", "")
require.NoError(t, err)
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "should have failed - tried to reuse auth code")
data.Set("grant_type", model.RefreshTokenGrantType)
data.Set("client_id", oauthApp.Id)
data.Set("client_secret", oauthApp.ClientSecret)
data.Set("refresh_token", "")
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
data.Del("code")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "Should have failed - refresh token empty")
data.Set("refresh_token", refreshToken)
rsp, _, err = apiClient.GetOAuthAccessToken(data)
require.NoError(t, err)
require.NotEmpty(t, rsp.AccessToken, "access token not returned")
require.NotEmpty(t, rsp.RefreshToken, "refresh token not returned")
require.NotEqual(t, rsp.RefreshToken, refreshToken, "refresh token did not update")
require.Equal(t, rsp.TokenType, model.AccessTokenType, "access token type incorrect")
apiClient.SetOAuthToken(rsp.AccessToken)
_, err = apiClient.DoAPIGet("/oauth_test", "")
require.NoError(t, err)
data.Set("refresh_token", rsp.RefreshToken)
rsp, _, err = apiClient.GetOAuthAccessToken(data)
require.NoError(t, err)
require.NotEmpty(t, rsp.AccessToken, "access token not returned")
require.NotEmpty(t, rsp.RefreshToken, "refresh token not returned")
require.NotEqual(t, rsp.RefreshToken, refreshToken, "refresh token did not update")
require.Equal(t, rsp.TokenType, model.AccessTokenType, "access token type incorrect")
apiClient.SetOAuthToken(rsp.AccessToken)
_, err = apiClient.DoAPIGet("/oauth_test", "")
require.NoError(t, err)
authData := &model.AuthData{ClientId: oauthApp.Id, RedirectUri: oauthApp.CallbackUrls[0], UserId: th.BasicUser.Id, Code: model.NewId(), ExpiresIn: -1}
_, err = th.App.Srv().Store.OAuth().SaveAuthData(authData)
require.NoError(t, err)
data.Set("grant_type", model.AccessTokenGrantType)
data.Set("client_id", oauthApp.Id)
data.Set("client_secret", oauthApp.ClientSecret)
data.Set("redirect_uri", oauthApp.CallbackUrls[0])
data.Set("code", authData.Code)
data.Del("refresh_token")
_, _, err = apiClient.GetOAuthAccessToken(data)
require.Error(t, err, "Should have failed - code is expired")
apiClient.ClearOAuthToken()
}
func TestMobileLoginWithOAuth(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
c := &Context{
App: th.App,
AppContext: &request.Context{},
Params: &Params{
Service: "gitlab",
},
}
var siteURL = "http://localhost:8065"
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.SiteURL = siteURL })
translationFunc := i18n.GetUserTranslations("en")
c.AppContext.SetT(translationFunc)
c.Logger = th.TestLogger
provider := &MattermostTestProvider{}
einterfaces.RegisterOAuthProvider(model.ServiceGitlab, provider)
t.Run("Should include redirect URL in the output when valid URL Scheme is passed", func(t *testing.T) {
responseWriter := httptest.NewRecorder()
request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/oauth/gitlab/mobile_login?redirect_to="+url.QueryEscape("randomScheme://"), nil)
mobileLoginWithOAuth(c, responseWriter, request)
assert.Contains(t, responseWriter.Body.String(), "randomScheme://")
assert.NotContains(t, responseWriter.Body.String(), siteURL)
})
t.Run("Should not include the redirect URL consisting of javascript protocol", func(t *testing.T) {
responseWriter := httptest.NewRecorder()
request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/oauth/gitlab/mobile_login?redirect_to="+url.QueryEscape("javascript:alert('hello')"), nil)
mobileLoginWithOAuth(c, responseWriter, request)
assert.NotContains(t, responseWriter.Body.String(), "javascript:alert('hello')")
assert.Contains(t, responseWriter.Body.String(), siteURL)
})
t.Run("Should not include the redirect URL consisting of javascript protocol in mixed case", func(t *testing.T) {
responseWriter := httptest.NewRecorder()
request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/oauth/gitlab/mobile_login?redirect_to="+url.QueryEscape("JaVasCript:alert('hello')"), nil)
mobileLoginWithOAuth(c, responseWriter, request)
assert.NotContains(t, responseWriter.Body.String(), "JaVasCript:alert('hello')")
assert.Contains(t, responseWriter.Body.String(), siteURL)
})
}
func TestOAuthComplete(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
th := Setup(t).InitBasic()
th.Login(apiClient, th.SystemAdminUser)
defer th.TearDown()
gitLabSettingsEnable := th.App.Config().GitLabSettings.Enable
gitLabSettingsAuthEndpoint := th.App.Config().GitLabSettings.AuthEndpoint
gitLabSettingsId := th.App.Config().GitLabSettings.Id
gitLabSettingsSecret := th.App.Config().GitLabSettings.Secret
gitLabSettingsTokenEndpoint := th.App.Config().GitLabSettings.TokenEndpoint
gitLabSettingsUserAPIEndpoint := th.App.Config().GitLabSettings.UserAPIEndpoint
enableOAuthServiceProvider := th.App.Config().ServiceSettings.EnableOAuthServiceProvider
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Enable = gitLabSettingsEnable })
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.AuthEndpoint = gitLabSettingsAuthEndpoint })
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Id = gitLabSettingsId })
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.Secret = gitLabSettingsSecret })
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.TokenEndpoint = gitLabSettingsTokenEndpoint })
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GitLabSettings.UserAPIEndpoint = gitLabSettingsUserAPIEndpoint })
th.App.UpdateConfig(func(cfg *model.Config) { cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuthServiceProvider })
}()
r, err := HTTPGet(apiClient.URL+"/login/gitlab/complete?code=123", apiClient.HTTPClient, "", true)
assert.Error(t, err)
closeBody(r)
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Enable = true })
r, err = HTTPGet(apiClient.URL+"/login/gitlab/complete?code=123&state=!#$#F@#Yˆ&~ñ", apiClient.HTTPClient, "", true)
assert.Error(t, err)
closeBody(r)
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.AuthEndpoint = apiClient.URL + "/oauth/authorize" })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Id = model.NewId() })
stateProps := map[string]string{}
stateProps["action"] = model.OAuthActionLogin
stateProps["team_id"] = th.BasicTeam.Id
stateProps["redirect_to"] = *th.App.Config().GitLabSettings.AuthEndpoint
state := base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
r, err = HTTPGet(apiClient.URL+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), apiClient.HTTPClient, "", true)
assert.Error(t, err)
closeBody(r)
stateProps["hash"] = utils.HashSha256(*th.App.Config().GitLabSettings.Id)
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
r, err = HTTPGet(apiClient.URL+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), apiClient.HTTPClient, "", true)
assert.Error(t, err)
closeBody(r)
// We are going to use mattermost as the provider emulating gitlab
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
defaultRolePermissions := th.SaveDefaultRolePermissions()
defer func() {
th.RestoreDefaultRolePermissions(defaultRolePermissions)
}()
th.AddPermissionToRole(model.PermissionManageOAuth.Id, model.TeamUserRoleId)
th.AddPermissionToRole(model.PermissionManageOAuth.Id, model.SystemUserRoleId)
oauthApp := &model.OAuthApp{
Name: "TestApp5" + model.NewId(),
Homepage: "https://nowhere.com",
Description: "test",
CallbackUrls: []string{
apiClient.URL + "/signup/" + model.ServiceGitlab + "/complete",
apiClient.URL + "/login/" + model.ServiceGitlab + "/complete",
},
CreatorId: th.SystemAdminUser.Id,
IsTrusted: true,
}
oauthApp, appErr := th.App.CreateOAuthApp(oauthApp)
require.Nil(t, appErr)
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Id = oauthApp.Id })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Secret = oauthApp.ClientSecret })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.AuthEndpoint = apiClient.URL + "/oauth/authorize" })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.TokenEndpoint = apiClient.URL + "/oauth/access_token" })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.UserAPIEndpoint = apiClient.APIURL + "/users/me" })
provider := &MattermostTestProvider{}
authRequest := &model.AuthorizeRequest{
ResponseType: model.AuthCodeResponseType,
ClientId: oauthApp.Id,
RedirectURI: oauthApp.CallbackUrls[0],
Scope: "all",
State: "123",
}
redirect, _, err := apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
rurl, _ := url.Parse(redirect)
code := rurl.Query().Get("code")
stateProps["action"] = model.OAuthActionEmailToSSO
delete(stateProps, "team_id")
stateProps["redirect_to"] = *th.App.Config().GitLabSettings.AuthEndpoint
stateProps["hash"] = utils.HashSha256(*th.App.Config().GitLabSettings.Id)
stateProps["redirect_to"] = "/oauth/authorize"
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
r, err = HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false)
if err == nil {
closeBody(r)
}
einterfaces.RegisterOAuthProvider(model.ServiceGitlab, provider)
redirect, _, err = apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
rurl, _ = url.Parse(redirect)
code = rurl.Query().Get("code")
r, err = HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false)
if err == nil {
closeBody(r)
}
_, nErr := th.App.Srv().Store.User().UpdateAuthData(
th.BasicUser.Id, model.ServiceGitlab, &th.BasicUser.Email, th.BasicUser.Email, true)
require.NoError(t, nErr)
redirect, _, err = apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
rurl, _ = url.Parse(redirect)
code = rurl.Query().Get("code")
stateProps["action"] = model.OAuthActionLogin
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
if r, err = HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false); err == nil {
closeBody(r)
}
redirect, _, err = apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
rurl, _ = url.Parse(redirect)
code = rurl.Query().Get("code")
delete(stateProps, "action")
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
if r, err = HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false); err == nil {
closeBody(r)
}
redirect, _, err = apiClient.AuthorizeOAuthApp(authRequest)
require.NoError(t, err)
rurl, _ = url.Parse(redirect)
code = rurl.Query().Get("code")
stateProps["action"] = model.OAuthActionSignup
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
if r, err := HTTPGet(apiClient.URL+"/login/"+model.ServiceGitlab+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), apiClient.HTTPClient, "", false); err == nil {
closeBody(r)
}
}
func TestOAuthComplete_ErrorMessages(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
c := &Context{
App: th.App,
AppContext: &request.Context{},
Params: &Params{
Service: "gitlab",
},
}
translationFunc := i18n.GetUserTranslations("en")
c.AppContext.SetT(translationFunc)
c.Logger = mlog.CreateConsoleTestLogger(true, mlog.LvlDebug)
defer c.Logger.Shutdown()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GitLabSettings.Enable = true })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
provider := &MattermostTestProvider{}
einterfaces.RegisterOAuthProvider(model.ServiceGitlab, provider)
responseWriter := httptest.NewRecorder()
// Renders for web & mobile app with webview
request, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/signup/gitlab/complete?code=1234", nil)
completeOAuth(c, responseWriter, request)
assert.Contains(t, responseWriter.Body.String(), "<!-- web error message -->")
// Renders for mobile app with redirect url
stateProps := map[string]string{}
stateProps["action"] = model.OAuthActionMobile
stateProps["redirect_to"] = th.App.Config().NativeAppSettings.AppCustomURLSchemes[0]
state := base64.StdEncoding.EncodeToString([]byte(model.MapToJSON(stateProps)))
request2, _ := http.NewRequest(http.MethodGet, th.App.GetSiteURL()+"/signup/gitlab/complete?code=1234&state="+url.QueryEscape(state), nil)
completeOAuth(c, responseWriter, request2)
assert.Contains(t, responseWriter.Body.String(), "<!-- mobile app message -->")
}
func HTTPGet(url string, httpClient *http.Client, authToken string, followRedirect bool) (*http.Response, error) {
rq, _ := http.NewRequest("GET", url, nil)
rq.Close = true
if authToken != "" {
rq.Header.Set(model.HeaderAuth, authToken)
}
if !followRedirect {
httpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
}
if rp, err := httpClient.Do(rq); err != nil {
return nil, err
} else if rp.StatusCode == 304 {
return rp, nil
} else if rp.StatusCode == 307 {
return rp, nil
} else if rp.StatusCode >= 300 {
defer closeBody(rp)
return rp, model.AppErrorFromJSON(rp.Body)
} else {
return rp, nil
}
}
func closeBody(r *http.Response) {
if r != nil && r.Body != nil {
ioutil.ReadAll(r.Body)
r.Body.Close()
}
}
type MattermostTestProvider struct {
}
func (m *MattermostTestProvider) GetUserFromJSON(data io.Reader, tokenUser *model.User) (*model.User, error) {
var user model.User
if err := json.NewDecoder(data).Decode(&user); err != nil {
return nil, err
}
user.AuthData = &user.Email
return &user, nil
}
func (m *MattermostTestProvider) GetSSOSettings(config *model.Config, service string) (*model.SSOSettings, error) {
return &config.GitLabSettings, nil
}
func (m *MattermostTestProvider) GetUserFromIdToken(token string) (*model.User, error) {
return nil, nil
}
func (m *MattermostTestProvider) IsSameUser(dbUser, oauthUser *model.User) bool {
return dbUser.AuthData == oauthUser.AuthData
}
func GenerateTestAppName() string {
return "fakeoauthapp" + model.NewRandomString(10)
}
func checkHTTPStatus(t *testing.T, resp *model.Response, expectedStatus int) {
t.Helper()
require.NotNilf(t, resp, "Unexpected nil response, expected http status:%v", expectedStatus)
require.Equalf(t, expectedStatus, resp.StatusCode, "Expected http status:%v, got %v", expectedStatus, resp.StatusCode)
}
func CheckForbiddenStatus(t *testing.T, resp *model.Response) {
t.Helper()
checkHTTPStatus(t, resp, http.StatusForbidden)
}
func CheckUnauthorizedStatus(t *testing.T, resp *model.Response) {
t.Helper()
checkHTTPStatus(t, resp, http.StatusUnauthorized)
}
func CheckNotFoundStatus(t *testing.T, resp *model.Response) {
t.Helper()
checkHTTPStatus(t, resp, http.StatusNotFound)
}
func CheckBadRequestStatus(t *testing.T, resp *model.Response) {
t.Helper()
checkHTTPStatus(t, resp, http.StatusBadRequest)
}
func (th *TestHelper) Login(client *model.Client4, user *model.User) {
session := &model.Session{
UserId: user.Id,
Roles: user.GetRawRoles(),
IsOAuth: false,
}
session, _ = th.App.CreateSession(session)
client.AuthToken = session.Token
client.AuthType = model.HeaderBearer
}
func (th *TestHelper) Logout(client *model.Client4) {
client.AuthToken = ""
}
func (th *TestHelper) SaveDefaultRolePermissions() map[string][]string {
results := make(map[string][]string)
for _, roleName := range []string{
"system_user",
"system_admin",
"team_user",
"team_admin",
"channel_user",
"channel_admin",
} {
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
if err1 != nil {
panic(err1)
}
results[roleName] = role.Permissions
}
return results
}
func (th *TestHelper) RestoreDefaultRolePermissions(data map[string][]string) {
for roleName, permissions := range data {
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
if err1 != nil {
panic(err1)
}
if strings.Join(role.Permissions, " ") == strings.Join(permissions, " ") {
continue
}
role.Permissions = permissions
_, err2 := th.App.UpdateRole(role)
if err2 != nil {
panic(err2)
}
}
}
// func (th *TestHelper) RemovePermissionFromRole(permission string, roleName string) {
// utils.DisableDebugLogForTest()
// role, err1 := th.App.GetRoleByName(roleName)
// if err1 != nil {
// utils.EnableDebugLogForTest()
// panic(err1)
// }
// var newPermissions []string
// for _, p := range role.Permissions {
// if p != permission {
// newPermissions = append(newPermissions, p)
// }
// }
// if strings.Join(role.Permissions, " ") == strings.Join(newPermissions, " ") {
// utils.EnableDebugLogForTest()
// return
// }
// role.Permissions = newPermissions
// _, err2 := th.App.UpdateRole(role)
// if err2 != nil {
// utils.EnableDebugLogForTest()
// panic(err2)
// }
// utils.EnableDebugLogForTest()
// }
func (th *TestHelper) AddPermissionToRole(permission string, roleName string) {
role, err1 := th.App.GetRoleByName(context.Background(), roleName)
if err1 != nil {
panic(err1)
}
for _, existingPermission := range role.Permissions {
if existingPermission == permission {
return
}
}
role.Permissions = append(role.Permissions, permission)
_, err2 := th.App.UpdateRole(role)
if err2 != nil {
panic(err2)
}
}
func TestFullyQualifiedRedirectURL(t *testing.T) {
const siteURL = "https://xxx.yyy/mm"
for target, expected := range map[string]string{
"": "https://xxx.yyy/mm",
"/": "https://xxx.yyy/mm/",
"some-path": "https://xxx.yyy/mm/some-path",
"/some-path": "https://xxx.yyy/mm/some-path",
"/some-path/": "https://xxx.yyy/mm/some-path/",
} {
t.Run(target, func(t *testing.T) {
require.Equal(t, expected, fullyQualifiedRedirectURL(siteURL, target))
})
}
}