[MM-29845] Add a new handler to allow authentication via CWS API Key (#16319)

* Add a new handler to allow authentication via CWS API Key

* Make error better

* Add tests and cases for new handler functions

* Move some code around

* Add test for GetCloudSession function

* unset the env after test completion

* Remove white space

* Change Info to Warn

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Nick Misasi
2020-11-23 14:34:10 -05:00
коммит произвёл GitHub
родитель 27462bbfc9
Коммит 3099128bbd
12 изменённых файлов: 144 добавлений и 4 удалений

Просмотреть файл

@@ -540,6 +540,7 @@ type AppIface interface {
GetChannelsForSchemePage(scheme *model.Scheme, page int, perPage int) (model.ChannelList, *model.AppError)
GetChannelsForUser(teamId string, userId string, includeDeleted bool, lastDeleteAt int) (*model.ChannelList, *model.AppError)
GetChannelsUserNotIn(teamId string, userId string, offset int, limit int) (*model.ChannelList, *model.AppError)
GetCloudSession(token string) (*model.Session, *model.AppError)
GetClusterId() string
GetClusterStatus() []*model.ClusterInfo
GetCommand(commandId string) (*model.Command, *model.AppError)

Просмотреть файл

@@ -19,6 +19,7 @@ const (
TokenLocationHeader
TokenLocationCookie
TokenLocationQueryString
TokenLocationCloudHeader
)
func (tl TokenLocation) String() string {
@@ -31,6 +32,8 @@ func (tl TokenLocation) String() string {
return "Cookie"
case TokenLocationQueryString:
return "QueryString"
case TokenLocationCloudHeader:
return "CloudHeader"
default:
return "Unknown"
}
@@ -281,5 +284,9 @@ func ParseAuthTokenFromRequest(r *http.Request) (string, TokenLocation) {
return token, TokenLocationQueryString
}
if token := r.Header.Get(model.HEADER_CLOUD_TOKEN); token != "" {
return token, TokenLocationCloudHeader
}
return "", TokenLocationNotFound
}

Просмотреть файл

@@ -26,6 +26,7 @@ func TestParseAuthTokenFromRequest(t *testing.T) {
{"BEARER mytoken", "", "", "mytoken", TokenLocationHeader},
{"", "mytoken", "", "mytoken", TokenLocationCookie},
{"", "", "mytoken", "mytoken", TokenLocationQueryString},
{"mytoken", "", "", "mytoken", TokenLocationCloudHeader},
}
for testnum, tc := range cases {
@@ -34,10 +35,12 @@ func TestParseAuthTokenFromRequest(t *testing.T) {
pathname += "?access_token=" + tc.query
}
req := httptest.NewRequest("GET", pathname, nil)
if tc.header != "" {
switch tc.expectedLocation {
case TokenLocationHeader:
req.Header.Add(model.HEADER_AUTH, tc.header)
}
if tc.cookie != "" {
case TokenLocationCloudHeader:
req.Header.Add(model.HEADER_CLOUD_TOKEN, tc.header)
case TokenLocationCookie:
req.AddCookie(&http.Cookie{
Name: model.SESSION_COOKIE_TOKEN,
Value: tc.cookie,

Просмотреть файл

@@ -4862,6 +4862,28 @@ func (a *OpenTracingAppLayer) GetChannelsUserNotIn(teamId string, userId string,
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetCloudSession(token string) (*model.Session, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetCloudSession")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetCloudSession(token)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetClusterId() string {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetClusterId")

Просмотреть файл

@@ -7,6 +7,7 @@ import (
"errors"
"math"
"net/http"
"os"
"time"
"github.com/mattermost/mattermost-server/v5/audit"
@@ -34,6 +35,21 @@ func (a *App) CreateSession(session *model.Session) (*model.Session, *model.AppE
return session, nil
}
func (a *App) GetCloudSession(token string) (*model.Session, *model.AppError) {
apiKey := os.Getenv("MM_CLOUD_API_KEY")
if apiKey != "" && apiKey == token {
// Need a bare-bones session object for later checks
session := &model.Session{
Token: token,
IsOAuth: false,
}
session.AddProp(model.SESSION_PROP_TYPE, model.SESSION_TYPE_CLOUD_KEY)
return session, nil
}
return nil, model.NewAppError("GetCloudSession", "api.context.invalid_token.error", map[string]interface{}{"Token": token, "Error": ""}, "The provided token is invalid", http.StatusUnauthorized)
}
func (a *App) GetSession(token string) (*model.Session, *model.AppError) {
metrics := a.Metrics()

Просмотреть файл

@@ -5,6 +5,7 @@ package app
import (
"fmt"
"os"
"testing"
"time"
@@ -404,3 +405,35 @@ func TestApp_SetSessionExpireInDays(t *testing.T) {
})
}
}
func TestGetCloudSession(t *testing.T) {
th := Setup(t)
defer func() {
os.Unsetenv("MM_CLOUD_API_KEY")
th.TearDown()
}()
t.Run("Matching environment variable and token should return non-nil session", func(t *testing.T) {
os.Setenv("MM_CLOUD_API_KEY", "mytoken")
session, err := th.App.GetCloudSession("mytoken")
require.Nil(t, err)
require.NotNil(t, session)
require.Equal(t, "mytoken", session.Token)
})
t.Run("Empty environment variable should return error", func(t *testing.T) {
os.Setenv("MM_CLOUD_API_KEY", "")
session, err := th.App.GetCloudSession("mytoken")
require.Nil(t, session)
require.NotNil(t, err)
require.Equal(t, "api.context.invalid_token.error", err.Id)
})
t.Run("Mismatched env variable and token should return error", func(t *testing.T) {
os.Setenv("MM_CLOUD_API_KEY", "mytoken")
session, err := th.App.GetCloudSession("myincorrecttoken")
require.Nil(t, session)
require.NotNil(t, err)
require.Equal(t, "api.context.invalid_token.error", err.Id)
})
}