Files
mattermost/api/oauth_test.go

873 lines
31 KiB
Go
Raw Normal View History

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
2017-04-03 14:37:58 -03:00
"encoding/base64"
"io"
"io/ioutil"
"net/http"
"net/url"
2017-04-03 14:37:58 -03:00
"strings"
"testing"
"github.com/mattermost/platform/app"
"github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
)
2017-04-03 14:37:58 -03:00
func TestOAuthRegisterApp(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
2017-04-03 14:37:58 -03:00
Client := th.BasicClient
2017-04-03 14:37:58 -03:00
oauthApp := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
2017-04-03 14:37:58 -03:00
if _, err := Client.RegisterApp(oauthApp); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
2017-04-03 14:37:58 -03:00
// calling the endpoint without an app
if _, err := Client.DoApiPost("/oauth/register", ""); err == nil {
t.Fatal("should have failed")
}
Client.Logout()
2017-04-03 14:37:58 -03:00
if _, err := Client.RegisterApp(oauthApp); err == nil {
t.Fatal("not logged in - should have failed")
}
th.LoginSystemAdmin()
2017-04-03 14:37:58 -03:00
Client = th.SystemAdminClient
2017-04-03 14:37:58 -03:00
if result, err := Client.RegisterApp(oauthApp); err != nil {
t.Fatal(err)
} else {
rapp := result.Data.(*model.OAuthApp)
if len(rapp.Id) != 26 {
t.Fatal("clientid didn't return properly")
}
if len(rapp.ClientSecret) != 26 {
t.Fatal("client secret didn't return properly")
}
}
2017-04-03 14:37:58 -03:00
oauthApp = &model.OAuthApp{Name: "", Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
if _, err := Client.RegisterApp(oauthApp); err == nil {
t.Fatal("missing name - should have failed")
}
2017-04-03 14:37:58 -03:00
oauthApp = &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
if _, err := Client.RegisterApp(oauthApp); err == nil {
t.Fatal("missing homepage - should have failed")
}
2017-04-03 14:37:58 -03:00
oauthApp = &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{}}
if _, err := Client.RegisterApp(oauthApp); err == nil {
t.Fatal("missing callback url - should have failed")
}
2017-04-03 14:37:58 -03:00
user := &model.User{Email: strings.ToLower("test+"+model.NewId()) + "@simulator.amazonses.com", Password: "hello1", Username: "n" + model.NewId(), EmailVerified: true}
ruser := Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
app.UpdateUserRoles(ruser.Id, "")
Client.Logout()
Client.Login(user.Email, user.Password)
oauthApp = &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
if _, err := Client.RegisterApp(oauthApp); err == nil {
t.Fatal("should have failed. not enough permissions")
}
}
2017-04-03 14:37:58 -03:00
func TestOAuthAllow(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
PLT-2057 User as a first class object (#2648) * Adding TeamMember to system * Fixing all unit tests on the backend * Fixing merge conflicts * Fixing merge conflict * Adding javascript unit tests * Adding TeamMember to system * Fixing all unit tests on the backend * Fixing merge conflicts * Fixing merge conflict * Adding javascript unit tests * Adding client side unit test * Cleaning up the clint side tests * Fixing msg * Adding more client side unit tests * Adding more using tests * Adding last bit of client side unit tests and adding make cmd * Fixing bad merge * Fixing libraries * Updating to new client side API * Fixing borken unit test * Fixing unit tests * ugg...trying to beat gofmt * ugg...trying to beat gofmt * Cleaning up remainder of the server side routes * Adding inital load api * Increased coverage of webhook unit tests (#2660) * Adding loading ... to root html * Fixing bad merge * Removing explicit content type so superagent will guess corectly (#2685) * Fixing merge and unit tests * Adding create team UI * Fixing signup flows * Adding LDAP unit tests and enterprise unit test helper (#2702) * Add the ability to reset MFA from the commandline (#2706) * Fixing compliance unit tests * Fixing client side tests * Adding open server to system console * Moving websocket connection * Fixing unit test * Fixing unit tests * Fixing unit tests * Adding nickname and more LDAP unit tests (#2717) * Adding join open teams * Cleaning up all TODOs in the code * Fixing web sockets * Removing unused webockets file * PLT-2533 Add the ability to reset a user's MFA from the system console (#2715) * Add the ability to reset a user's MFA from the system console * Add client side unit test for adminResetMfa * Reorganizing authentication to fix LDAP error message (#2723) * Fixing failing unit test * Initial upgrade db code * Adding upgrade script * Fixing upgrade script after running on core * Update OAuth and Claim routes to work with user model changes (#2739) * Fixing perminant deletion. Adding ability to delete all user and the entire database (#2740) * Fixing team invite ldap login call (#2741) * Fixing bluebar and some img stuff * Fix all the different file upload web utils (#2743) * Fixing invalid session redirect (#2744) * Redirect on bad channel name (#2746) * Fixing a bunch of issue and removing dead code * Patch to fix error message on leave channel (#2747) * Setting EnableOpenServer to false by default * Fixing config * Fixing upgrade * Fixing reported bugs * Bug fixes for PLT-2057 * PLT-2563 Redo password recovery to use a database table (#2745) * Redo password recovery to use a database table * Update reset password audits * Split out admin and user reset password APIs to be separate * Delete password recovery when user is permanently deleted * Consolidate password resetting into a single function * Removed private channels as an option for outgoing webhooks (#2752) * PLT-2577/PLT-2552 Fixes for backstage (#2753) * Added URL to incoming webhook list * Fixed client functions for adding/removing integrations * Disallowed slash commands without trigger words * Fixed clientside handling of errors on AddCommand page * Minor auth cleanup (#2758) * Changed EditPostModal to just close if you save without making any changes (#2759) * Renamed client -> Client in async_client.jsx and fixed eslint warnings (#2756) * Fixed url in channel info modal (#2755) * Fixing reported issues * Moving to version 3 of the apis * Fixing command unit tests (#2760) * Adding team admins * Fixing DM issue * Fixing eslint error * Properly set EditPostModal's originalText state in all cases (#2762) * Update client config check to assume features is defined if server is licensed (#2772) * Fixing url link * Fixing issue with websocket crashing when sending messages to different teams
2016-04-21 22:37:01 -07:00
Client := th.BasicClient
AdminClient := th.SystemAdminClient
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
2017-04-03 14:37:58 -03:00
oauthApp := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
state := "123"
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
2017-04-03 14:37:58 -03:00
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", state); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
2017-04-03 14:37:58 -03:00
if result, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", state); err != nil {
t.Fatal(err)
} else {
redirect := result.Data.(map[string]string)["redirect"]
if len(redirect) == 0 {
t.Fatal("redirect url should be set")
}
ru, _ := url.Parse(redirect)
if ru == nil {
t.Fatal("redirect url unparseable")
} else {
if len(ru.Query().Get("code")) == 0 {
t.Fatal("authorization code not returned")
}
if ru.Query().Get("state") != state {
t.Fatal("returned state doesn't match")
}
}
}
2017-04-03 14:37:58 -03:00
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "", "all", state); err == nil {
t.Fatal("should have failed - no redirect_url given")
}
2017-04-03 14:37:58 -03:00
if _, err := Client.AllowOAuth("", oauthApp.Id, "", "", state); err == nil {
t.Fatal("should have failed - no response type given")
}
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "", "", state); err == nil {
t.Fatal("should have failed - no redirect_url given")
}
2017-04-03 14:37:58 -03:00
if result, err := Client.AllowOAuth("junk", oauthApp.Id, oauthApp.CallbackUrls[0], "all", state); err != nil {
t.Fatal(err)
} else {
redirect := result.Data.(map[string]string)["redirect"]
if len(redirect) == 0 {
t.Fatal("redirect url should be set")
}
ru, _ := url.Parse(redirect)
if ru == nil {
t.Fatal("redirect url unparseable")
} else {
if ru.Query().Get("error") != "unsupported_response_type" {
t.Fatal("wrong error returned")
}
if ru.Query().Get("state") != state {
t.Fatal("returned state doesn't match")
}
}
}
2017-04-03 14:37:58 -03:00
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, "", oauthApp.CallbackUrls[0], "all", state); err == nil {
t.Fatal("should have failed - empty client id")
}
2017-04-03 14:37:58 -03:00
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, "junk", oauthApp.CallbackUrls[0], "all", state); err == nil {
t.Fatal("should have failed - bad client id")
}
2017-04-03 14:37:58 -03:00
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "https://somewhereelse.com", "", state); err == nil {
t.Fatal("should have failed - redirect uri host does not match app host")
}
}
2017-04-03 14:37:58 -03:00
func TestOAuthGetAppsByUser(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
Client := th.BasicClient
AdminClient := th.SystemAdminClient
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
if _, err := Client.GetOAuthAppsByUser(); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
2017-04-10 14:47:38 -04:00
if _, err := Client.GetOAuthAppsByUser(); err == nil {
t.Fatal("Should have failed.")
}
*utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false
2016-09-13 12:42:48 -04:00
utils.SetDefaultRolesBasedOnConfig()
if result, err := Client.GetOAuthAppsByUser(); err != nil {
t.Fatal(err)
} else {
apps := result.Data.([]*model.OAuthApp)
if len(apps) != 0 {
t.Fatal("incorrect number of apps should have been 0")
}
}
2017-04-03 14:37:58 -03:00
oauthApp := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
if result, err := Client.GetOAuthAppsByUser(); err != nil {
t.Fatal(err)
} else {
apps := result.Data.([]*model.OAuthApp)
if len(apps) != 1 {
t.Fatal("incorrect number of apps should have been 1")
}
}
2017-04-03 14:37:58 -03:00
oauthApp = &model.OAuthApp{Name: "TestApp4" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = AdminClient.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
if result, err := AdminClient.GetOAuthAppsByUser(); err != nil {
t.Fatal(err)
} else {
apps := result.Data.([]*model.OAuthApp)
if len(apps) < 4 {
t.Fatal("incorrect number of apps should have been 4 or more")
}
}
2017-04-03 14:37:58 -03:00
user := &model.User{Email: strings.ToLower("test+"+model.NewId()) + "@simulator.amazonses.com", Password: "hello1", Username: "n" + model.NewId(), EmailVerified: true}
ruser := Client.Must(AdminClient.CreateUser(user, "")).Data.(*model.User)
app.UpdateUserRoles(ruser.Id, "")
Client.Logout()
Client.Login(user.Email, user.Password)
if _, err := Client.GetOAuthAppsByUser(); err == nil {
t.Fatal("should have failed. not enough permissions")
}
}
2017-04-03 14:37:58 -03:00
func TestOAuthGetAppInfo(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
Client := th.BasicClient
AdminClient := th.SystemAdminClient
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
if _, err := Client.GetOAuthAppInfo("fakeId"); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
2017-04-03 14:37:58 -03:00
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
2017-04-03 14:37:58 -03:00
if _, err := Client.GetOAuthAppInfo(model.NewId()); err == nil {
t.Fatal("Should have failed")
}
2017-04-03 14:37:58 -03:00
if _, err := Client.GetOAuthAppInfo(oauthApp.Id); err != nil {
t.Fatal(err)
}
}
2017-04-03 14:37:58 -03:00
func TestOAuthGetAuthorizedApps(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
Client := th.BasicClient
AdminClient := th.SystemAdminClient
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
if _, err := Client.GetOAuthAuthorizedApps(); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
2017-04-03 14:37:58 -03:00
}
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
2017-04-03 14:37:58 -03:00
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "https://nowhere.com", "user", ""); err != nil {
t.Fatal(err)
}
if result, err := Client.GetOAuthAuthorizedApps(); err != nil {
t.Fatal(err)
} else {
apps := result.Data.([]*model.OAuthApp)
if len(apps) != 1 {
t.Fatal("incorrect number of apps should have been 1")
}
}
}
2017-04-03 14:37:58 -03:00
func TestOAuthDeauthorizeApp(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
Client := th.BasicClient
AdminClient := th.SystemAdminClient
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
if err := Client.OAuthDeauthorizeApp(model.NewId()); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
2017-04-03 14:37:58 -03:00
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
2017-04-03 14:37:58 -03:00
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
2017-04-03 14:37:58 -03:00
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, "https://nowhere.com", "user", ""); err != nil {
t.Fatal(err)
}
2017-04-03 14:37:58 -03:00
if err := Client.OAuthDeauthorizeApp(""); err == nil {
t.Fatal("Should have failed - no id provided")
}
a1 := model.AccessData{}
a1.ClientId = oauthApp.Id
a1.UserId = th.BasicUser.Id
a1.Token = model.NewId()
a1.RefreshToken = model.NewId()
a1.ExpiresAt = model.GetMillis()
a1.RedirectUri = "http://example.com"
<-app.Srv.Store.OAuth().SaveAccessData(&a1)
if err := Client.OAuthDeauthorizeApp(oauthApp.Id); err != nil {
t.Fatal(err)
}
if result, err := Client.GetOAuthAuthorizedApps(); err != nil {
t.Fatal(err)
} else {
apps := result.Data.([]*model.OAuthApp)
if len(apps) != 0 {
t.Fatal("incorrect number of apps should have been 0")
}
}
}
2017-04-03 14:37:58 -03:00
func TestOAuthRegenerateAppSecret(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
Client := th.BasicClient
AdminClient := th.SystemAdminClient
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
if _, err := AdminClient.RegenerateOAuthAppSecret(model.NewId()); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
2017-04-03 14:37:58 -03:00
oauthApp := &model.OAuthApp{Name: "TestApp6" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
if _, err := AdminClient.RegenerateOAuthAppSecret(model.NewId()); err == nil {
t.Fatal("Should have failed - invalid app id")
}
2017-04-03 14:37:58 -03:00
if _, err := Client.RegenerateOAuthAppSecret(oauthApp.Id); err == nil {
t.Fatal("Should have failed - only admin or the user who registered the app are allowed to perform this action")
}
2017-04-03 14:37:58 -03:00
if regenApp, err := AdminClient.RegenerateOAuthAppSecret(oauthApp.Id); err != nil {
t.Fatal(err)
} else {
app2 := regenApp.Data.(*model.OAuthApp)
2017-04-03 14:37:58 -03:00
if app2.Id != oauthApp.Id {
t.Fatal("Should have been the same app Id")
}
2017-04-03 14:37:58 -03:00
if app2.ClientSecret == oauthApp.ClientSecret {
t.Fatal("Should have been diferent client Secrets")
}
}
}
func TestOAuthDeleteApp(t *testing.T) {
th := Setup().InitBasic().InitSystemAdmin()
Client := th.BasicClient
AdminClient := th.SystemAdminClient
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
if _, err := Client.DeleteOAuthApp("fakeId"); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
*utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false
2016-09-13 12:42:48 -04:00
utils.SetDefaultRolesBasedOnConfig()
2017-04-03 14:37:58 -03:00
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
if _, err := Client.DeleteOAuthApp(oauthApp.Id); err != nil {
t.Fatal(err)
}
oauthApp = &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
if _, err := AdminClient.DeleteOAuthApp(""); err == nil {
t.Fatal("Should have failed - id not provided")
}
if _, err := AdminClient.DeleteOAuthApp(model.NewId()); err == nil {
t.Fatal("Should have failed - invalid id")
}
if _, err := AdminClient.DeleteOAuthApp(oauthApp.Id); err != nil {
t.Fatal(err)
}
oauthApp = &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = AdminClient.Must(AdminClient.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
if _, err := Client.DeleteOAuthApp(oauthApp.Id); err == nil {
t.Fatal("Should have failed - only admin or the user who registered the app are allowed to perform this action")
}
user := &model.User{Email: strings.ToLower("test+"+model.NewId()) + "@simulator.amazonses.com", Password: "hello1", Username: "n" + model.NewId(), EmailVerified: true}
ruser := Client.Must(AdminClient.CreateUser(user, "")).Data.(*model.User)
app.UpdateUserRoles(ruser.Id, "")
Client.Logout()
Client.Login(user.Email, user.Password)
if _, err := Client.DeleteOAuthApp(oauthApp.Id); err == nil {
t.Fatal("Should have failed - not enough permissions")
}
}
func TestOAuthAuthorize(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
th := Setup().InitBasic()
Client := th.BasicClient
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = false
if !utils.Cfg.ServiceSettings.EnableOAuthServiceProvider {
if r, err := HttpGet(Client.Url+"/oauth/authorize", Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - oauth providing turned off")
closeBody(r)
}
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
if r, err := HttpGet(Client.Url+"/oauth/authorize", Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - scope not provided")
closeBody(r)
}
if r, err := HttpGet(Client.Url+"/oauth/authorize?client_id=bad&&redirect_uri=http://example.com&response_type="+model.AUTHCODE_RESPONSE_TYPE, Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - scope not provided")
closeBody(r)
}
// register an app to authorize it
oauthApp := &model.OAuthApp{Name: "TestApp" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
if r, err := HttpGet(Client.Url+"/oauth/authorize?client_id="+oauthApp.Id+"&&redirect_uri=http://example.com&response_type="+model.AUTHCODE_RESPONSE_TYPE, Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - user not logged")
closeBody(r)
}
authToken := Client.AuthType + " " + Client.AuthToken
if r, err := HttpGet(Client.Url+"/oauth/authorize?client_id="+oauthApp.Id+"&redirect_uri=http://example.com&response_type="+model.AUTHCODE_RESPONSE_TYPE, Client.HttpClient, authToken, true); err != nil {
2017-04-03 14:37:58 -03:00
t.Fatal(err)
closeBody(r)
}
// lets authorize the app
if _, err := Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "user", ""); err != nil {
t.Fatal(err)
}
if r, err := HttpGet(Client.Url+"/oauth/authorize?client_id="+oauthApp.Id+"&&redirect_uri="+oauthApp.CallbackUrls[0]+"&response_type="+model.AUTHCODE_RESPONSE_TYPE,
Client.HttpClient, authToken, true); err != nil {
// it will return an error as there is no connection to https://nowhere.com
if r != nil {
closeBody(r)
}
}
}
func TestOAuthAccessToken(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
th := Setup().InitBasic()
Client := th.BasicClient
enableOAuth := utils.Cfg.ServiceSettings.EnableOAuthServiceProvider
adminOnly := *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations
defer func() {
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = enableOAuth
*utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = adminOnly
utils.SetDefaultRolesBasedOnConfig()
}()
2017-04-03 14:37:58 -03:00
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
*utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false
utils.SetDefaultRolesBasedOnConfig()
2017-04-03 14:37:58 -03:00
oauthApp := &model.OAuthApp{Name: "TestApp5" + model.NewId(), Homepage: "https://nowhere.com", Description: "test", CallbackUrls: []string{"https://nowhere.com"}}
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
utils.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]}}
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - oauth providing turned off")
}
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
redirect := Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
rurl, _ := url.Parse(redirect)
Client.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]}}
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - bad grant type")
}
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
data.Set("client_id", "")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - missing client id")
}
data.Set("client_id", "junk")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - bad client id")
}
data.Set("client_id", oauthApp.Id)
data.Set("client_secret", "")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - missing client secret")
}
data.Set("client_secret", "junk")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - bad client secret")
}
data.Set("client_secret", oauthApp.ClientSecret)
data.Set("code", "")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - missing code")
}
data.Set("code", "junk")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - bad code")
}
data.Set("code", rurl.Query().Get("code"))
data.Set("redirect_uri", "junk")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - non-matching redirect uri")
}
2017-04-03 14:37:58 -03:00
// reset data for successful request
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
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])
2017-04-03 14:37:58 -03:00
token := ""
refreshToken := ""
if result, err := Client.GetAccessToken(data); err != nil {
t.Fatal(err)
2017-04-03 14:37:58 -03:00
} else {
rsp := result.Data.(*model.AccessResponse)
if len(rsp.AccessToken) == 0 {
t.Fatal("access token not returned")
} else if len(rsp.RefreshToken) == 0 {
t.Fatal("refresh token not returned")
2017-04-03 14:37:58 -03:00
} else {
token = rsp.AccessToken
refreshToken = rsp.RefreshToken
}
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
t.Fatal("access token type incorrect")
}
}
if result, err := Client.DoApiGet("/teams/"+th.BasicTeam.Id+"/users/0/100?access_token="+token, "", ""); err != nil {
t.Fatal(err)
} else {
userMap := model.UserMapFromJson(result.Body)
if len(userMap) == 0 {
t.Fatal("user map empty - did not get results correctly")
}
}
if _, err := Client.DoApiGet("/teams/"+th.BasicTeam.Id+"/users/0/100", "", ""); err == nil {
t.Fatal("should have failed - no access token provided")
}
if _, err := Client.DoApiGet("/teams/"+th.BasicTeam.Id+"/users/0/100?access_token=junk", "", ""); err == nil {
t.Fatal("should have failed - bad access token provided")
}
2017-04-03 14:37:58 -03:00
Client.SetOAuthToken(token)
if result, err := Client.DoApiGet("/teams/"+th.BasicTeam.Id+"/users/0/100", "", ""); err != nil {
t.Fatal(err)
} else {
userMap := model.UserMapFromJson(result.Body)
if len(userMap) == 0 {
t.Fatal("user map empty - did not get results correctly")
}
}
2017-04-03 14:37:58 -03:00
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("should have failed - tried to reuse auth code")
}
data.Set("grant_type", model.REFRESH_TOKEN_GRANT_TYPE)
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")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("Should have failed - refresh token empty")
}
2017-04-03 14:37:58 -03:00
data.Set("refresh_token", refreshToken)
if result, err := Client.GetAccessToken(data); err != nil {
t.Fatal(err)
} else {
rsp := result.Data.(*model.AccessResponse)
if len(rsp.AccessToken) == 0 {
t.Fatal("access token not returned")
} else if len(rsp.RefreshToken) == 0 {
t.Fatal("refresh token not returned")
} else if rsp.RefreshToken == refreshToken {
t.Fatal("refresh token did not update")
}
if rsp.TokenType != model.ACCESS_TOKEN_TYPE {
t.Fatal("access token type incorrect")
}
}
2017-04-03 14:37:58 -03:00
authData := &model.AuthData{ClientId: oauthApp.Id, RedirectUri: oauthApp.CallbackUrls[0], UserId: th.BasicUser.Id, Code: model.NewId(), ExpiresIn: -1}
<-app.Srv.Store.OAuth().SaveAuthData(authData)
data.Set("grant_type", model.ACCESS_TOKEN_GRANT_TYPE)
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")
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("Should have failed - code is expired")
}
authData = &model.AuthData{ClientId: oauthApp.Id, RedirectUri: oauthApp.CallbackUrls[0], UserId: th.BasicUser.Id, Code: model.NewId(), ExpiresIn: model.AUTHCODE_EXPIRE_TIME}
<-app.Srv.Store.OAuth().SaveAuthData(authData)
data.Set("code", authData.Code)
if _, err := Client.GetAccessToken(data); err == nil {
t.Fatal("Should have failed - code with invalid hash comparission")
}
Client.ClearOAuthToken()
}
func TestOAuthComplete(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
th := Setup().InitBasic()
Client := th.BasicClient
if r, err := HttpGet(Client.Url+"/login/gitlab/complete", Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - no code provided")
closeBody(r)
}
if r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123", Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - gitlab disabled")
closeBody(r)
}
utils.Cfg.GitLabSettings.Enable = true
if r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123&state=!#$#F@#Yˆ&~ñ", Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - gitlab disabled")
closeBody(r)
}
utils.Cfg.GitLabSettings.AuthEndpoint = Client.Url + "/oauth/authorize"
utils.Cfg.GitLabSettings.Id = model.NewId()
stateProps := map[string]string{}
stateProps["action"] = model.OAUTH_ACTION_LOGIN
stateProps["team_id"] = th.BasicTeam.Id
stateProps["redirect_to"] = utils.Cfg.GitLabSettings.AuthEndpoint
state := base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
if r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - bad state")
closeBody(r)
}
stateProps["hash"] = utils.HashSha256(utils.Cfg.GitLabSettings.Id)
2017-04-03 14:37:58 -03:00
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
if r, err := HttpGet(Client.Url+"/login/gitlab/complete?code=123&state="+url.QueryEscape(state), Client.HttpClient, "", true); err == nil {
t.Fatal("should have failed - no connection")
closeBody(r)
}
// We are going to use mattermost as the provider emulating gitlab
utils.Cfg.ServiceSettings.EnableOAuthServiceProvider = true
oauthApp := &model.OAuthApp{
Name: "TestApp5" + model.NewId(),
Homepage: "https://nowhere.com",
Description: "test",
CallbackUrls: []string{
Client.Url + "/signup/" + model.SERVICE_GITLAB + "/complete",
Client.Url + "/login/" + model.SERVICE_GITLAB + "/complete",
},
IsTrusted: true,
}
oauthApp = Client.Must(Client.RegisterApp(oauthApp)).Data.(*model.OAuthApp)
utils.Cfg.GitLabSettings.Id = oauthApp.Id
utils.Cfg.GitLabSettings.Secret = oauthApp.ClientSecret
utils.Cfg.GitLabSettings.AuthEndpoint = Client.Url + "/oauth/authorize"
utils.Cfg.GitLabSettings.TokenEndpoint = Client.Url + "/oauth/access_token"
utils.Cfg.GitLabSettings.UserApiEndpoint = Client.ApiUrl + "/users/me"
provider := &MattermostTestProvider{}
redirect := Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
rurl, _ := url.Parse(redirect)
code := rurl.Query().Get("code")
stateProps["action"] = model.OAUTH_ACTION_EMAIL_TO_SSO
delete(stateProps, "team_id")
stateProps["redirect_to"] = utils.Cfg.GitLabSettings.AuthEndpoint
stateProps["hash"] = utils.HashSha256(utils.Cfg.GitLabSettings.Id)
2017-04-03 14:37:58 -03:00
stateProps["redirect_to"] = "/oauth/authorize"
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
closeBody(r)
}
einterfaces.RegisterOauthProvider(model.SERVICE_GITLAB, provider)
redirect = Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
rurl, _ = url.Parse(redirect)
code = rurl.Query().Get("code")
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
closeBody(r)
}
if result := <-app.Srv.Store.User().UpdateAuthData(
th.BasicUser.Id, model.SERVICE_GITLAB, &th.BasicUser.Email, th.BasicUser.Email, true); result.Err != nil {
t.Fatal(result.Err)
}
redirect = Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
rurl, _ = url.Parse(redirect)
code = rurl.Query().Get("code")
stateProps["action"] = model.OAUTH_ACTION_LOGIN
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
closeBody(r)
}
redirect = Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
rurl, _ = url.Parse(redirect)
code = rurl.Query().Get("code")
delete(stateProps, "action")
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
closeBody(r)
}
redirect = Client.Must(Client.AllowOAuth(model.AUTHCODE_RESPONSE_TYPE, oauthApp.Id, oauthApp.CallbackUrls[0], "all", "123")).Data.(map[string]string)["redirect"]
rurl, _ = url.Parse(redirect)
code = rurl.Query().Get("code")
stateProps["action"] = model.OAUTH_ACTION_SIGNUP
state = base64.StdEncoding.EncodeToString([]byte(model.MapToJson(stateProps)))
if r, err := HttpGet(Client.Url+"/login/"+model.SERVICE_GITLAB+"/complete?code="+url.QueryEscape(code)+"&state="+url.QueryEscape(state), Client.HttpClient, "", false); err == nil {
closeBody(r)
}
}
func HttpGet(url string, httpClient *http.Client, authToken string, followRedirect bool) (*http.Response, *model.AppError) {
rq, _ := http.NewRequest("GET", url, nil)
rq.Close = true
if len(authToken) > 0 {
rq.Header.Set(model.HEADER_AUTH, 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, model.NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error())
} 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.Body != nil {
ioutil.ReadAll(r.Body)
r.Body.Close()
}
}
type MattermostTestProvider struct {
}
func (m *MattermostTestProvider) GetIdentifier() string {
return model.SERVICE_GITLAB
}
func (m *MattermostTestProvider) GetUserFromJson(data io.Reader) *model.User {
return model.UserFromJson(data)
}
func (m *MattermostTestProvider) GetAuthDataFromJson(data io.Reader) string {
authData := model.UserFromJson(data)
return authData.Email
}