mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
[MM-55028] Added OAuthOutgoingConnection store (#25221)
* OAuthOutgoingConnection model * added store * make generated * add missing license headers * fix receiver name * i18n * i18n sorting * update migrations from master * make migrations-extract * update retrylayer tests * replaced sql query with id pagination * fixed flaky tests * missing columns * missing columns on save/update * typo * improved tests * remove enum from mysql colum * add password credentials to store * renamed migrations * model change suggestions * refactor test functionsn * migration typo * refactor store table names * updated sanitize test * oauthoutgoingconnection -> outgoingoauthconnection * signature change * i18n update * granttype typo * uppercase typo * lowercase store name
This commit is contained in:
@@ -228,6 +228,8 @@ channels/db/migrations/mysql/000114_sharedchannelremotes_drop_nextsyncat_descrip
|
||||
channels/db/migrations/mysql/000114_sharedchannelremotes_drop_nextsyncat_description.up.sql
|
||||
channels/db/migrations/mysql/000115_user_reporting_changes.down.sql
|
||||
channels/db/migrations/mysql/000115_user_reporting_changes.up.sql
|
||||
channels/db/migrations/mysql/000116_create_outgoing_oauth_connections.down.sql
|
||||
channels/db/migrations/mysql/000116_create_outgoing_oauth_connections.up.sql
|
||||
channels/db/migrations/postgres/000001_create_teams.down.sql
|
||||
channels/db/migrations/postgres/000001_create_teams.up.sql
|
||||
channels/db/migrations/postgres/000002_create_team_members.down.sql
|
||||
@@ -456,3 +458,5 @@ channels/db/migrations/postgres/000114_sharedchannelremotes_drop_nextsyncat_desc
|
||||
channels/db/migrations/postgres/000114_sharedchannelremotes_drop_nextsyncat_description.up.sql
|
||||
channels/db/migrations/postgres/000115_user_reporting_changes.down.sql
|
||||
channels/db/migrations/postgres/000115_user_reporting_changes.up.sql
|
||||
channels/db/migrations/postgres/000116_create_outgoing_oauth_connections.down.sql
|
||||
channels/db/migrations/postgres/000116_create_outgoing_oauth_connections.up.sql
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS OutgoingOAuthConnections;
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE IF NOT EXISTS OutgoingOAuthConnections (
|
||||
Id varchar(26),
|
||||
Name varchar(64),
|
||||
CreatorId varchar(26) DEFAULT NULL,
|
||||
CreateAt bigint(20) DEFAULT NULL,
|
||||
UpdateAt bigint(20) DEFAULT NULL,
|
||||
ClientId varchar(255),
|
||||
ClientSecret varchar(255),
|
||||
CredentialsUsername varchar(255),
|
||||
CredentialsPassword varchar(255),
|
||||
OAuthTokenURL text,
|
||||
GrantType varchar(32) DEFAULT 'client_credentials',
|
||||
Audiences TEXT,
|
||||
PRIMARY KEY (Id),
|
||||
KEY idx_OutgoingOAuthConnections_name (Name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP TABLE IF EXISTS outgoingoauthconnections;
|
||||
|
||||
DROP TYPE IF EXISTS outgoingoauthconnections_granttype;
|
||||
@@ -0,0 +1,18 @@
|
||||
CREATE TYPE outgoingoauthconnections_granttype AS ENUM ('client_credentials', 'password');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS outgoingoauthconnections (
|
||||
id varchar(26) PRIMARY KEY,
|
||||
name varchar(64),
|
||||
creatorid VARCHAR(26),
|
||||
createat bigint,
|
||||
updateat bigint,
|
||||
clientid varchar(255),
|
||||
clientsecret varchar(255),
|
||||
credentialsusername varchar(255),
|
||||
credentialspassword varchar(255),
|
||||
oauthtokenurl text,
|
||||
granttype outgoingoauthconnections_granttype DEFAULT 'client_credentials',
|
||||
audiences VARCHAR(1024)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_outgoingoauthconnections_name ON outgoingoauthconnections (name);
|
||||
@@ -37,6 +37,7 @@ type OpenTracingLayer struct {
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
OutgoingOAuthConnectionStore store.OutgoingOAuthConnectionStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
@@ -137,6 +138,10 @@ func (s *OpenTracingLayer) OAuth() store.OAuthStore {
|
||||
return s.OAuthStore
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayer) OutgoingOAuthConnection() store.OutgoingOAuthConnectionStore {
|
||||
return s.OutgoingOAuthConnectionStore
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayer) Plugin() store.PluginStore {
|
||||
return s.PluginStore
|
||||
}
|
||||
@@ -331,6 +336,11 @@ type OpenTracingLayerOAuthStore struct {
|
||||
Root *OpenTracingLayer
|
||||
}
|
||||
|
||||
type OpenTracingLayerOutgoingOAuthConnectionStore struct {
|
||||
store.OutgoingOAuthConnectionStore
|
||||
Root *OpenTracingLayer
|
||||
}
|
||||
|
||||
type OpenTracingLayerPluginStore struct {
|
||||
store.PluginStore
|
||||
Root *OpenTracingLayer
|
||||
@@ -5773,6 +5783,96 @@ func (s *OpenTracingLayerOAuthStore) UpdateApp(app *model.OAuthApp) (*model.OAut
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerOutgoingOAuthConnectionStore) DeleteConnection(c request.CTX, id string) error {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "OutgoingOAuthConnectionStore.DeleteConnection")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
err := s.OutgoingOAuthConnectionStore.DeleteConnection(c, id)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerOutgoingOAuthConnectionStore) GetConnection(c request.CTX, id string) (*model.OutgoingOAuthConnection, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "OutgoingOAuthConnectionStore.GetConnection")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.OutgoingOAuthConnectionStore.GetConnection(c, id)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerOutgoingOAuthConnectionStore) GetConnections(c request.CTX, filters model.OutgoingOAuthConnectionGetConnectionsFilter) ([]*model.OutgoingOAuthConnection, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "OutgoingOAuthConnectionStore.GetConnections")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.OutgoingOAuthConnectionStore.GetConnections(c, filters)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerOutgoingOAuthConnectionStore) SaveConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "OutgoingOAuthConnectionStore.SaveConnection")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.OutgoingOAuthConnectionStore.SaveConnection(c, conn)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerOutgoingOAuthConnectionStore) UpdateConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "OutgoingOAuthConnectionStore.UpdateConnection")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.OutgoingOAuthConnectionStore.UpdateConnection(c, conn)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPluginStore) CompareAndDelete(keyVal *model.PluginKeyValue, oldValue []byte) (bool, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PluginStore.CompareAndDelete")
|
||||
@@ -13086,6 +13186,7 @@ func New(childStore store.Store, ctx context.Context) *OpenTracingLayer {
|
||||
newStore.LinkMetadataStore = &OpenTracingLayerLinkMetadataStore{LinkMetadataStore: childStore.LinkMetadata(), Root: &newStore}
|
||||
newStore.NotifyAdminStore = &OpenTracingLayerNotifyAdminStore{NotifyAdminStore: childStore.NotifyAdmin(), Root: &newStore}
|
||||
newStore.OAuthStore = &OpenTracingLayerOAuthStore{OAuthStore: childStore.OAuth(), Root: &newStore}
|
||||
newStore.OutgoingOAuthConnectionStore = &OpenTracingLayerOutgoingOAuthConnectionStore{OutgoingOAuthConnectionStore: childStore.OutgoingOAuthConnection(), Root: &newStore}
|
||||
newStore.PluginStore = &OpenTracingLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
|
||||
newStore.PostStore = &OpenTracingLayerPostStore{PostStore: childStore.Post(), Root: &newStore}
|
||||
newStore.PostAcknowledgementStore = &OpenTracingLayerPostAcknowledgementStore{PostAcknowledgementStore: childStore.PostAcknowledgement(), Root: &newStore}
|
||||
|
||||
@@ -41,6 +41,7 @@ type RetryLayer struct {
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
OutgoingOAuthConnectionStore store.OutgoingOAuthConnectionStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
@@ -141,6 +142,10 @@ func (s *RetryLayer) OAuth() store.OAuthStore {
|
||||
return s.OAuthStore
|
||||
}
|
||||
|
||||
func (s *RetryLayer) OutgoingOAuthConnection() store.OutgoingOAuthConnectionStore {
|
||||
return s.OutgoingOAuthConnectionStore
|
||||
}
|
||||
|
||||
func (s *RetryLayer) Plugin() store.PluginStore {
|
||||
return s.PluginStore
|
||||
}
|
||||
@@ -335,6 +340,11 @@ type RetryLayerOAuthStore struct {
|
||||
Root *RetryLayer
|
||||
}
|
||||
|
||||
type RetryLayerOutgoingOAuthConnectionStore struct {
|
||||
store.OutgoingOAuthConnectionStore
|
||||
Root *RetryLayer
|
||||
}
|
||||
|
||||
type RetryLayerPluginStore struct {
|
||||
store.PluginStore
|
||||
Root *RetryLayer
|
||||
@@ -6550,6 +6560,111 @@ func (s *RetryLayerOAuthStore) UpdateApp(app *model.OAuthApp) (*model.OAuthApp,
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerOutgoingOAuthConnectionStore) DeleteConnection(c request.CTX, id string) error {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
err := s.OutgoingOAuthConnectionStore.DeleteConnection(c, id)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerOutgoingOAuthConnectionStore) GetConnection(c request.CTX, id string) (*model.OutgoingOAuthConnection, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.OutgoingOAuthConnectionStore.GetConnection(c, id)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerOutgoingOAuthConnectionStore) GetConnections(c request.CTX, filters model.OutgoingOAuthConnectionGetConnectionsFilter) ([]*model.OutgoingOAuthConnection, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.OutgoingOAuthConnectionStore.GetConnections(c, filters)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerOutgoingOAuthConnectionStore) SaveConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.OutgoingOAuthConnectionStore.SaveConnection(c, conn)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerOutgoingOAuthConnectionStore) UpdateConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.OutgoingOAuthConnectionStore.UpdateConnection(c, conn)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPluginStore) CompareAndDelete(keyVal *model.PluginKeyValue, oldValue []byte) (bool, error) {
|
||||
|
||||
tries := 0
|
||||
@@ -14916,6 +15031,7 @@ func New(childStore store.Store) *RetryLayer {
|
||||
newStore.LinkMetadataStore = &RetryLayerLinkMetadataStore{LinkMetadataStore: childStore.LinkMetadata(), Root: &newStore}
|
||||
newStore.NotifyAdminStore = &RetryLayerNotifyAdminStore{NotifyAdminStore: childStore.NotifyAdmin(), Root: &newStore}
|
||||
newStore.OAuthStore = &RetryLayerOAuthStore{OAuthStore: childStore.OAuth(), Root: &newStore}
|
||||
newStore.OutgoingOAuthConnectionStore = &RetryLayerOutgoingOAuthConnectionStore{OutgoingOAuthConnectionStore: childStore.OutgoingOAuthConnection(), Root: &newStore}
|
||||
newStore.PluginStore = &RetryLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
|
||||
newStore.PostStore = &RetryLayerPostStore{PostStore: childStore.Post(), Root: &newStore}
|
||||
newStore.PostAcknowledgementStore = &RetryLayerPostAcknowledgementStore{PostAcknowledgementStore: childStore.PostAcknowledgement(), Root: &newStore}
|
||||
|
||||
@@ -34,6 +34,7 @@ func genStore() *mocks.Store {
|
||||
mock.On("LinkMetadata").Return(&mocks.LinkMetadataStore{})
|
||||
mock.On("SharedChannel").Return(&mocks.SharedChannelStore{})
|
||||
mock.On("OAuth").Return(&mocks.OAuthStore{})
|
||||
mock.On("OutgoingOAuthConnection").Return(&mocks.OutgoingOAuthConnectionStore{})
|
||||
mock.On("Plugin").Return(&mocks.PluginStore{})
|
||||
mock.On("Post").Return(&mocks.PostStore{})
|
||||
mock.On("Thread").Return(&mocks.ThreadStore{})
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type SqlOutgoingOAuthConnectionStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func newSqlOutgoingOAuthConnectionStore(sqlStore *SqlStore) store.OutgoingOAuthConnectionStore {
|
||||
return &SqlOutgoingOAuthConnectionStore{sqlStore}
|
||||
}
|
||||
|
||||
func (s *SqlOutgoingOAuthConnectionStore) SaveConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
if conn.Id != "" {
|
||||
return nil, store.NewErrInvalidInput("OutgoingOAuthConnection", "Id", conn.Id)
|
||||
}
|
||||
|
||||
conn.PreSave()
|
||||
if err := conn.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.GetMasterX().NamedExec(`INSERT INTO OutgoingOAuthConnections
|
||||
(Id, Name, ClientId, ClientSecret, CreateAt, UpdateAt, CreatorId, OAuthTokenURL, GrantType, Audiences)
|
||||
VALUES
|
||||
(:Id, :Name, :ClientId, :ClientSecret, :CreateAt, :UpdateAt, :CreatorId, :OAuthTokenURL, :GrantType, :Audiences)`, conn); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to save OutgoingOAuthConnection")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (s *SqlOutgoingOAuthConnectionStore) UpdateConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
if conn.Id == "" {
|
||||
return nil, store.NewErrInvalidInput("OutgoingOAuthConnection", "Id", conn.Id)
|
||||
}
|
||||
|
||||
conn.PreUpdate()
|
||||
if err := conn.IsValid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := s.GetMasterX().NamedExec(`UPDATE OutgoingOAuthConnections SET
|
||||
Name=:Name, ClientId=:ClientId, ClientSecret=:ClientSecret, UpdateAt=:UpdateAt, OAuthTokenURL=:OAuthTokenURL, GrantType=:GrantType, Audiences=:Audiences
|
||||
WHERE Id=:Id`, conn); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to update OutgoingOAuthConnection")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (s *SqlOutgoingOAuthConnectionStore) GetConnection(c request.CTX, id string) (*model.OutgoingOAuthConnection, error) {
|
||||
conn := &model.OutgoingOAuthConnection{}
|
||||
if err := s.GetReplicaX().Get(conn, `SELECT * FROM OutgoingOAuthConnections WHERE Id=?`, id); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, store.NewErrNotFound("OutgoingOAuthConnection", id)
|
||||
}
|
||||
return nil, errors.Wrap(err, "failed to get OutgoingOAuthConnection")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (s *SqlOutgoingOAuthConnectionStore) GetConnections(c request.CTX, filters model.OutgoingOAuthConnectionGetConnectionsFilter) ([]*model.OutgoingOAuthConnection, error) {
|
||||
filters.SetDefaults()
|
||||
|
||||
conns := []*model.OutgoingOAuthConnection{}
|
||||
query := s.getQueryBuilder().
|
||||
Select("*").
|
||||
From("OutgoingOAuthConnections").
|
||||
OrderBy("Id").
|
||||
Limit(uint64(filters.Limit))
|
||||
|
||||
if filters.OffsetId != "" {
|
||||
query = query.Where("Id > ?", filters.OffsetId)
|
||||
}
|
||||
|
||||
if err := s.GetReplicaX().SelectBuilder(&conns, query); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get OutgoingOAuthConnections")
|
||||
}
|
||||
|
||||
return conns, nil
|
||||
}
|
||||
|
||||
func (s *SqlOutgoingOAuthConnectionStore) DeleteConnection(c request.CTX, id string) error {
|
||||
if _, err := s.GetMasterX().Exec(`DELETE FROM OutgoingOAuthConnections WHERE Id=?`, id); err != nil {
|
||||
return errors.Wrap(err, "failed to delete OutgoingOAuthConnection")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost/server/v8/channels/store/storetest"
|
||||
)
|
||||
|
||||
func TestOutgoingOAuthConnectionStore(t *testing.T) {
|
||||
StoreTest(t, storetest.TestOutgoingOAuthConnectionStore)
|
||||
}
|
||||
@@ -78,6 +78,7 @@ type SqlStoreStores struct {
|
||||
compliance store.ComplianceStore
|
||||
session store.SessionStore
|
||||
oauth store.OAuthStore
|
||||
outgoingOAuthConnection store.OutgoingOAuthConnectionStore
|
||||
system store.SystemStore
|
||||
webhook store.WebhookStore
|
||||
command store.CommandStore
|
||||
@@ -199,6 +200,7 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) (*Sql
|
||||
store.stores.compliance = newSqlComplianceStore(store)
|
||||
store.stores.session = newSqlSessionStore(store)
|
||||
store.stores.oauth = newSqlOAuthStore(store)
|
||||
store.stores.outgoingOAuthConnection = newSqlOutgoingOAuthConnectionStore(store)
|
||||
store.stores.system = newSqlSystemStore(store)
|
||||
store.stores.webhook = newSqlWebhookStore(store, metrics)
|
||||
store.stores.command = newSqlCommandStore(store)
|
||||
@@ -948,6 +950,10 @@ func (ss *SqlStore) OAuth() store.OAuthStore {
|
||||
return ss.stores.oauth
|
||||
}
|
||||
|
||||
func (ss *SqlStore) OutgoingOAuthConnection() store.OutgoingOAuthConnectionStore {
|
||||
return ss.stores.outgoingOAuthConnection
|
||||
}
|
||||
|
||||
func (ss *SqlStore) System() store.SystemStore {
|
||||
return ss.stores.system
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ type Store interface {
|
||||
Compliance() ComplianceStore
|
||||
Session() SessionStore
|
||||
OAuth() OAuthStore
|
||||
OutgoingOAuthConnection() OutgoingOAuthConnectionStore
|
||||
System() SystemStore
|
||||
Webhook() WebhookStore
|
||||
Command() CommandStore
|
||||
@@ -577,6 +578,14 @@ type OAuthStore interface {
|
||||
RemoveAllAccessData() error
|
||||
}
|
||||
|
||||
type OutgoingOAuthConnectionStore interface {
|
||||
SaveConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error)
|
||||
UpdateConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error)
|
||||
GetConnection(c request.CTX, id string) (*model.OutgoingOAuthConnection, error)
|
||||
GetConnections(c request.CTX, filters model.OutgoingOAuthConnectionGetConnectionsFilter) ([]*model.OutgoingOAuthConnection, error)
|
||||
DeleteConnection(c request.CTX, id string) error
|
||||
}
|
||||
|
||||
type SystemStore interface {
|
||||
Save(system *model.System) error
|
||||
SaveOrUpdate(system *model.System) error
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// Code generated by mockery v2.23.2. DO NOT EDIT.
|
||||
|
||||
// Regenerate this file using `make store-mocks`.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
model "github.com/mattermost/mattermost/server/public/model"
|
||||
request "github.com/mattermost/mattermost/server/public/shared/request"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// OutgoingOAuthConnectionStore is an autogenerated mock type for the OutgoingOAuthConnectionStore type
|
||||
type OutgoingOAuthConnectionStore struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// DeleteConnection provides a mock function with given fields: c, id
|
||||
func (_m *OutgoingOAuthConnectionStore) DeleteConnection(c request.CTX, id string) error {
|
||||
ret := _m.Called(c, id)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, string) error); ok {
|
||||
r0 = rf(c, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetConnection provides a mock function with given fields: c, id
|
||||
func (_m *OutgoingOAuthConnectionStore) GetConnection(c request.CTX, id string) (*model.OutgoingOAuthConnection, error) {
|
||||
ret := _m.Called(c, id)
|
||||
|
||||
var r0 *model.OutgoingOAuthConnection
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, string) (*model.OutgoingOAuthConnection, error)); ok {
|
||||
return rf(c, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, string) *model.OutgoingOAuthConnection); ok {
|
||||
r0 = rf(c, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.OutgoingOAuthConnection)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(request.CTX, string) error); ok {
|
||||
r1 = rf(c, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetConnections provides a mock function with given fields: c, filters
|
||||
func (_m *OutgoingOAuthConnectionStore) GetConnections(c request.CTX, filters model.OutgoingOAuthConnectionGetConnectionsFilter) ([]*model.OutgoingOAuthConnection, error) {
|
||||
ret := _m.Called(c, filters)
|
||||
|
||||
var r0 []*model.OutgoingOAuthConnection
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, model.OutgoingOAuthConnectionGetConnectionsFilter) ([]*model.OutgoingOAuthConnection, error)); ok {
|
||||
return rf(c, filters)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, model.OutgoingOAuthConnectionGetConnectionsFilter) []*model.OutgoingOAuthConnection); ok {
|
||||
r0 = rf(c, filters)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.OutgoingOAuthConnection)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(request.CTX, model.OutgoingOAuthConnectionGetConnectionsFilter) error); ok {
|
||||
r1 = rf(c, filters)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SaveConnection provides a mock function with given fields: c, conn
|
||||
func (_m *OutgoingOAuthConnectionStore) SaveConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
ret := _m.Called(c, conn)
|
||||
|
||||
var r0 *model.OutgoingOAuthConnection
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error)); ok {
|
||||
return rf(c, conn)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, *model.OutgoingOAuthConnection) *model.OutgoingOAuthConnection); ok {
|
||||
r0 = rf(c, conn)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.OutgoingOAuthConnection)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(request.CTX, *model.OutgoingOAuthConnection) error); ok {
|
||||
r1 = rf(c, conn)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateConnection provides a mock function with given fields: c, conn
|
||||
func (_m *OutgoingOAuthConnectionStore) UpdateConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
ret := _m.Called(c, conn)
|
||||
|
||||
var r0 *model.OutgoingOAuthConnection
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error)); ok {
|
||||
return rf(c, conn)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(request.CTX, *model.OutgoingOAuthConnection) *model.OutgoingOAuthConnection); ok {
|
||||
r0 = rf(c, conn)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.OutgoingOAuthConnection)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(request.CTX, *model.OutgoingOAuthConnection) error); ok {
|
||||
r1 = rf(c, conn)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewOutgoingOAuthConnectionStore interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewOutgoingOAuthConnectionStore creates a new instance of OutgoingOAuthConnectionStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewOutgoingOAuthConnectionStore(t mockConstructorTestingTNewOutgoingOAuthConnectionStore) *OutgoingOAuthConnectionStore {
|
||||
mock := &OutgoingOAuthConnectionStore{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -492,6 +492,22 @@ func (_m *Store) OAuth() store.OAuthStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// OutgoingOAuthConnection provides a mock function with given fields:
|
||||
func (_m *Store) OutgoingOAuthConnection() store.OutgoingOAuthConnectionStore {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.OutgoingOAuthConnectionStore
|
||||
if rf, ok := ret.Get(0).(func() store.OutgoingOAuthConnectionStore); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.OutgoingOAuthConnectionStore)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Plugin provides a mock function with given fields:
|
||||
func (_m *Store) Plugin() store.PluginStore {
|
||||
ret := _m.Called()
|
||||
|
||||
239
server/channels/store/storetest/outgoing_oauth_connection.go
Normal file
239
server/channels/store/storetest/outgoing_oauth_connection.go
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package storetest
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func newValidOutgoingOAuthConnection() *model.OutgoingOAuthConnection {
|
||||
return &model.OutgoingOAuthConnection{
|
||||
CreatorId: model.NewId(),
|
||||
Name: "Test Connection",
|
||||
ClientId: model.NewId(),
|
||||
ClientSecret: model.NewId(),
|
||||
OAuthTokenURL: "https://nowhere.com/oauth/token",
|
||||
GrantType: model.OutgoingOAuthConnectionGrantTypeClientCredentials,
|
||||
Audiences: []string{"https://nowhere.com"},
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupOutgoingOAuthConnections(t *testing.T, ss store.Store) func() {
|
||||
return func() {
|
||||
// Delete all outgoing connections
|
||||
connections, err := ss.OutgoingOAuthConnection().GetConnections(request.TestContext(t), model.OutgoingOAuthConnectionGetConnectionsFilter{
|
||||
Limit: 100,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
for _, conn := range connections {
|
||||
err := ss.OutgoingOAuthConnection().DeleteConnection(request.TestContext(t), conn.Id)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutgoingOAuthConnectionStore(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
t.Run("SaveConnection", func(t *testing.T) {
|
||||
t.Cleanup(cleanupOutgoingOAuthConnections(t, ss))
|
||||
testSaveOutgoingOAuthConnection(t, ss)
|
||||
})
|
||||
t.Run("UpdateConnection", func(t *testing.T) {
|
||||
t.Cleanup(cleanupOutgoingOAuthConnections(t, ss))
|
||||
testUpdateOutgoingOAuthConnection(t, ss)
|
||||
})
|
||||
t.Run("GetConnection", func(t *testing.T) {
|
||||
t.Cleanup(cleanupOutgoingOAuthConnections(t, ss))
|
||||
testGetOutgoingOAuthConnection(t, ss)
|
||||
})
|
||||
t.Run("GetConnections", func(t *testing.T) {
|
||||
t.Cleanup(cleanupOutgoingOAuthConnections(t, ss))
|
||||
testGetOutgoingOAuthConnections(t, ss)
|
||||
})
|
||||
t.Run("DeleteConnection", func(t *testing.T) {
|
||||
t.Cleanup(cleanupOutgoingOAuthConnections(t, ss))
|
||||
testDeleteOutgoingOAuthConnection(t, ss)
|
||||
})
|
||||
}
|
||||
|
||||
func testSaveOutgoingOAuthConnection(t *testing.T, ss store.Store) {
|
||||
c := request.TestContext(t)
|
||||
|
||||
t.Run("save/get", func(t *testing.T) {
|
||||
// Define test data
|
||||
connection := newValidOutgoingOAuthConnection()
|
||||
|
||||
// Save the connection
|
||||
_, err := ss.OutgoingOAuthConnection().SaveConnection(c, connection)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve the connection
|
||||
storeConn, err := ss.OutgoingOAuthConnection().GetConnection(c, connection.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, connection, storeConn)
|
||||
})
|
||||
|
||||
t.Run("save without id should fail", func(t *testing.T) {
|
||||
connection := &model.OutgoingOAuthConnection{
|
||||
Id: model.NewId(),
|
||||
}
|
||||
|
||||
_, err := ss.OutgoingOAuthConnection().SaveConnection(c, connection)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("save with incorrect grant type should fail", func(t *testing.T) {
|
||||
connection := newValidOutgoingOAuthConnection()
|
||||
connection.GrantType = "incorrect"
|
||||
|
||||
_, err := ss.OutgoingOAuthConnection().SaveConnection(c, connection)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func testUpdateOutgoingOAuthConnection(t *testing.T, ss store.Store) {
|
||||
c := request.TestContext(t)
|
||||
|
||||
t.Run("update/get", func(t *testing.T) {
|
||||
// Define test data
|
||||
connection := newValidOutgoingOAuthConnection()
|
||||
|
||||
// Save the connection
|
||||
_, err := ss.OutgoingOAuthConnection().SaveConnection(c, connection)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update the connection
|
||||
connection.Name = "Updated Name"
|
||||
_, err = ss.OutgoingOAuthConnection().UpdateConnection(c, connection)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve the connection
|
||||
storeConn, err := ss.OutgoingOAuthConnection().GetConnection(c, connection.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, connection, storeConn)
|
||||
})
|
||||
|
||||
t.Run("update non-existing", func(t *testing.T) {
|
||||
connection := newValidOutgoingOAuthConnection()
|
||||
connection.Id = model.NewId()
|
||||
|
||||
_, err := ss.OutgoingOAuthConnection().UpdateConnection(c, connection)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("update without id should fail", func(t *testing.T) {
|
||||
connection := &model.OutgoingOAuthConnection{
|
||||
Id: model.NewId(),
|
||||
}
|
||||
|
||||
_, err := ss.OutgoingOAuthConnection().UpdateConnection(c, connection)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("update should update all fields", func(t *testing.T) {
|
||||
// Define test data
|
||||
connection := newValidOutgoingOAuthConnection()
|
||||
|
||||
// Save the connection
|
||||
_, err := ss.OutgoingOAuthConnection().SaveConnection(c, connection)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update the connection
|
||||
connection.Name = "Updated Name"
|
||||
connection.ClientId = "Updated ClientId"
|
||||
connection.ClientSecret = "Updated ClientSecret"
|
||||
connection.OAuthTokenURL = "https://nowhere.com/updated"
|
||||
// connection.GrantType = "client_credentials" // ignoring since we only allow one for now
|
||||
connection.Audiences = []string{"https://nowhere.com/updated"}
|
||||
_, err = ss.OutgoingOAuthConnection().UpdateConnection(c, connection)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve the connection
|
||||
storeConn, err := ss.OutgoingOAuthConnection().GetConnection(c, connection.Id)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, connection, storeConn)
|
||||
})
|
||||
}
|
||||
|
||||
func testGetOutgoingOAuthConnection(t *testing.T, ss store.Store) {
|
||||
c := request.TestContext(t)
|
||||
|
||||
t.Run("get non-existing", func(t *testing.T) {
|
||||
nonExistingId := model.NewId()
|
||||
var expected *store.ErrNotFound
|
||||
_, err := ss.OutgoingOAuthConnection().GetConnection(c, nonExistingId)
|
||||
require.ErrorAs(t, err, &expected)
|
||||
})
|
||||
}
|
||||
|
||||
func testGetOutgoingOAuthConnections(t *testing.T, ss store.Store) {
|
||||
c := request.TestContext(t)
|
||||
|
||||
// Define test data
|
||||
connection1 := newValidOutgoingOAuthConnection()
|
||||
connection2 := newValidOutgoingOAuthConnection()
|
||||
connection3 := newValidOutgoingOAuthConnection()
|
||||
|
||||
// Save the connections
|
||||
connection1, err := ss.OutgoingOAuthConnection().SaveConnection(c, connection1)
|
||||
require.NoError(t, err)
|
||||
connection2, err = ss.OutgoingOAuthConnection().SaveConnection(c, connection2)
|
||||
require.NoError(t, err)
|
||||
connection3, err = ss.OutgoingOAuthConnection().SaveConnection(c, connection3)
|
||||
require.NoError(t, err)
|
||||
|
||||
connections := []*model.OutgoingOAuthConnection{connection1, connection2, connection3}
|
||||
sort.Slice(connections, func(i, j int) bool {
|
||||
return connections[i].Id < connections[j].Id
|
||||
})
|
||||
|
||||
t.Run("get all", func(t *testing.T) {
|
||||
// Retrieve the connections
|
||||
conns, err := ss.OutgoingOAuthConnection().GetConnections(c, model.OutgoingOAuthConnectionGetConnectionsFilter{Limit: 3})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, conns, 3)
|
||||
})
|
||||
|
||||
t.Run("get connections using pagination", func(t *testing.T) {
|
||||
// Retrieve the first page
|
||||
conns, err := ss.OutgoingOAuthConnection().GetConnections(c, model.OutgoingOAuthConnectionGetConnectionsFilter{Limit: 1})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, conns, 1)
|
||||
require.Equal(t, connections[0].Id, conns[0].Id, "should return the first connection")
|
||||
|
||||
// Retrieve the second page
|
||||
conns, err = ss.OutgoingOAuthConnection().GetConnections(c, model.OutgoingOAuthConnectionGetConnectionsFilter{OffsetId: connections[0].Id})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, conns, 2)
|
||||
require.Equal(t, connections[1].Id, conns[0].Id, "should return the second connection")
|
||||
require.Equal(t, connections[2].Id, conns[1].Id, "should return the third connection")
|
||||
})
|
||||
}
|
||||
|
||||
func testDeleteOutgoingOAuthConnection(t *testing.T, ss store.Store) {
|
||||
c := request.TestContext(t)
|
||||
|
||||
t.Run("delete", func(t *testing.T) {
|
||||
// Define test data
|
||||
connection := newValidOutgoingOAuthConnection()
|
||||
|
||||
// Save the connection
|
||||
_, err := ss.OutgoingOAuthConnection().SaveConnection(c, connection)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete the connection
|
||||
err = ss.OutgoingOAuthConnection().DeleteConnection(c, connection.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Retrieve the connection
|
||||
_, err = ss.OutgoingOAuthConnection().GetConnection(c, connection.Id)
|
||||
var expected *store.ErrNotFound
|
||||
require.ErrorAs(t, err, &expected)
|
||||
})
|
||||
}
|
||||
@@ -29,6 +29,7 @@ type Store struct {
|
||||
ComplianceStore mocks.ComplianceStore
|
||||
SessionStore mocks.SessionStore
|
||||
OAuthStore mocks.OAuthStore
|
||||
OutgoingOAuthConnectionStore mocks.OutgoingOAuthConnectionStore
|
||||
SystemStore mocks.SystemStore
|
||||
WebhookStore mocks.WebhookStore
|
||||
CommandStore mocks.CommandStore
|
||||
@@ -64,21 +65,24 @@ type Store struct {
|
||||
DesktopTokensStore mocks.DesktopTokensStore
|
||||
}
|
||||
|
||||
func (s *Store) SetContext(context context.Context) { s.context = context }
|
||||
func (s *Store) Context() context.Context { return s.context }
|
||||
func (s *Store) Team() store.TeamStore { return &s.TeamStore }
|
||||
func (s *Store) Channel() store.ChannelStore { return &s.ChannelStore }
|
||||
func (s *Store) Post() store.PostStore { return &s.PostStore }
|
||||
func (s *Store) User() store.UserStore { return &s.UserStore }
|
||||
func (s *Store) RetentionPolicy() store.RetentionPolicyStore { return &s.RetentionPolicyStore }
|
||||
func (s *Store) Bot() store.BotStore { return &s.BotStore }
|
||||
func (s *Store) ProductNotices() store.ProductNoticesStore { return &s.ProductNoticesStore }
|
||||
func (s *Store) Audit() store.AuditStore { return &s.AuditStore }
|
||||
func (s *Store) ClusterDiscovery() store.ClusterDiscoveryStore { return &s.ClusterDiscoveryStore }
|
||||
func (s *Store) RemoteCluster() store.RemoteClusterStore { return &s.RemoteClusterStore }
|
||||
func (s *Store) Compliance() store.ComplianceStore { return &s.ComplianceStore }
|
||||
func (s *Store) Session() store.SessionStore { return &s.SessionStore }
|
||||
func (s *Store) OAuth() store.OAuthStore { return &s.OAuthStore }
|
||||
func (s *Store) SetContext(context context.Context) { s.context = context }
|
||||
func (s *Store) Context() context.Context { return s.context }
|
||||
func (s *Store) Team() store.TeamStore { return &s.TeamStore }
|
||||
func (s *Store) Channel() store.ChannelStore { return &s.ChannelStore }
|
||||
func (s *Store) Post() store.PostStore { return &s.PostStore }
|
||||
func (s *Store) User() store.UserStore { return &s.UserStore }
|
||||
func (s *Store) RetentionPolicy() store.RetentionPolicyStore { return &s.RetentionPolicyStore }
|
||||
func (s *Store) Bot() store.BotStore { return &s.BotStore }
|
||||
func (s *Store) ProductNotices() store.ProductNoticesStore { return &s.ProductNoticesStore }
|
||||
func (s *Store) Audit() store.AuditStore { return &s.AuditStore }
|
||||
func (s *Store) ClusterDiscovery() store.ClusterDiscoveryStore { return &s.ClusterDiscoveryStore }
|
||||
func (s *Store) RemoteCluster() store.RemoteClusterStore { return &s.RemoteClusterStore }
|
||||
func (s *Store) Compliance() store.ComplianceStore { return &s.ComplianceStore }
|
||||
func (s *Store) Session() store.SessionStore { return &s.SessionStore }
|
||||
func (s *Store) OAuth() store.OAuthStore { return &s.OAuthStore }
|
||||
func (s *Store) OutgoingOAuthConnection() store.OutgoingOAuthConnectionStore {
|
||||
return &s.OutgoingOAuthConnectionStore
|
||||
}
|
||||
func (s *Store) System() store.SystemStore { return &s.SystemStore }
|
||||
func (s *Store) Webhook() store.WebhookStore { return &s.WebhookStore }
|
||||
func (s *Store) Command() store.CommandStore { return &s.CommandStore }
|
||||
|
||||
@@ -37,6 +37,7 @@ type TimerLayer struct {
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
OutgoingOAuthConnectionStore store.OutgoingOAuthConnectionStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
@@ -137,6 +138,10 @@ func (s *TimerLayer) OAuth() store.OAuthStore {
|
||||
return s.OAuthStore
|
||||
}
|
||||
|
||||
func (s *TimerLayer) OutgoingOAuthConnection() store.OutgoingOAuthConnectionStore {
|
||||
return s.OutgoingOAuthConnectionStore
|
||||
}
|
||||
|
||||
func (s *TimerLayer) Plugin() store.PluginStore {
|
||||
return s.PluginStore
|
||||
}
|
||||
@@ -331,6 +336,11 @@ type TimerLayerOAuthStore struct {
|
||||
Root *TimerLayer
|
||||
}
|
||||
|
||||
type TimerLayerOutgoingOAuthConnectionStore struct {
|
||||
store.OutgoingOAuthConnectionStore
|
||||
Root *TimerLayer
|
||||
}
|
||||
|
||||
type TimerLayerPluginStore struct {
|
||||
store.PluginStore
|
||||
Root *TimerLayer
|
||||
@@ -5233,6 +5243,86 @@ func (s *TimerLayerOAuthStore) UpdateApp(app *model.OAuthApp) (*model.OAuthApp,
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerOutgoingOAuthConnectionStore) DeleteConnection(c request.CTX, id string) error {
|
||||
start := time.Now()
|
||||
|
||||
err := s.OutgoingOAuthConnectionStore.DeleteConnection(c, id)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("OutgoingOAuthConnectionStore.DeleteConnection", success, elapsed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TimerLayerOutgoingOAuthConnectionStore) GetConnection(c request.CTX, id string) (*model.OutgoingOAuthConnection, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, err := s.OutgoingOAuthConnectionStore.GetConnection(c, id)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("OutgoingOAuthConnectionStore.GetConnection", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerOutgoingOAuthConnectionStore) GetConnections(c request.CTX, filters model.OutgoingOAuthConnectionGetConnectionsFilter) ([]*model.OutgoingOAuthConnection, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, err := s.OutgoingOAuthConnectionStore.GetConnections(c, filters)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("OutgoingOAuthConnectionStore.GetConnections", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerOutgoingOAuthConnectionStore) SaveConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, err := s.OutgoingOAuthConnectionStore.SaveConnection(c, conn)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("OutgoingOAuthConnectionStore.SaveConnection", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerOutgoingOAuthConnectionStore) UpdateConnection(c request.CTX, conn *model.OutgoingOAuthConnection) (*model.OutgoingOAuthConnection, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, err := s.OutgoingOAuthConnectionStore.UpdateConnection(c, conn)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("OutgoingOAuthConnectionStore.UpdateConnection", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPluginStore) CompareAndDelete(keyVal *model.PluginKeyValue, oldValue []byte) (bool, error) {
|
||||
start := time.Now()
|
||||
|
||||
@@ -11794,6 +11884,7 @@ func New(childStore store.Store, metrics einterfaces.MetricsInterface) *TimerLay
|
||||
newStore.LinkMetadataStore = &TimerLayerLinkMetadataStore{LinkMetadataStore: childStore.LinkMetadata(), Root: &newStore}
|
||||
newStore.NotifyAdminStore = &TimerLayerNotifyAdminStore{NotifyAdminStore: childStore.NotifyAdmin(), Root: &newStore}
|
||||
newStore.OAuthStore = &TimerLayerOAuthStore{OAuthStore: childStore.OAuth(), Root: &newStore}
|
||||
newStore.OutgoingOAuthConnectionStore = &TimerLayerOutgoingOAuthConnectionStore{OutgoingOAuthConnectionStore: childStore.OutgoingOAuthConnection(), Root: &newStore}
|
||||
newStore.PluginStore = &TimerLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
|
||||
newStore.PostStore = &TimerLayerPostStore{PostStore: childStore.Post(), Root: &newStore}
|
||||
newStore.PostAcknowledgementStore = &TimerLayerPostAcknowledgementStore{PostAcknowledgementStore: childStore.PostAcknowledgement(), Root: &newStore}
|
||||
|
||||
@@ -9382,6 +9382,54 @@
|
||||
"id": "model.outgoing_hook.username.app_error",
|
||||
"translation": "Invalid username."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.audience.empty",
|
||||
"translation": "Audience must not be empty."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.audience.error",
|
||||
"translation": "Some audience URL is incorrect."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.client_id.error",
|
||||
"translation": "Invalid client id."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.client_secret.error",
|
||||
"translation": "Invalid client secret."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.create_at.error",
|
||||
"translation": "Create at must be a valid time."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.creator_id.error",
|
||||
"translation": "Invalid creator id."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.grant_type.error",
|
||||
"translation": "Invalid grant type."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.id.error",
|
||||
"translation": "Invalid id."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.name.error",
|
||||
"translation": "Invalid name."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.oauth_token_url.error",
|
||||
"translation": "Invalid oauth token url."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.password_credentials.error",
|
||||
"translation": "Invalid password credentials."
|
||||
},
|
||||
{
|
||||
"id": "model.outgoing_oauth_connection.is_valid.update_at.error",
|
||||
"translation": "Update at must be a valid time."
|
||||
},
|
||||
{
|
||||
"id": "model.plugin_command.error.app_error",
|
||||
"translation": "An error occurred while trying to execute this command."
|
||||
|
||||
158
server/public/model/outgoing_oauth_connection.go
Normal file
158
server/public/model/outgoing_oauth_connection.go
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type OutgoingOAuthConnectionGrantType string
|
||||
|
||||
func (gt OutgoingOAuthConnectionGrantType) IsValid() bool {
|
||||
return gt == OutgoingOAuthConnectionGrantTypeClientCredentials || gt == OutgoingOAuthConnectionGrantTypePassword
|
||||
}
|
||||
|
||||
const (
|
||||
OutgoingOAuthConnectionGrantTypeClientCredentials OutgoingOAuthConnectionGrantType = "client_credentials"
|
||||
OutgoingOAuthConnectionGrantTypePassword OutgoingOAuthConnectionGrantType = "password"
|
||||
|
||||
defaultGetConnectionsLimit = 50
|
||||
)
|
||||
|
||||
type OutgoingOAuthConnection struct {
|
||||
Id string `json:"id"`
|
||||
CreatorId string `json:"creator_id"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
Name string `json:"name"`
|
||||
ClientId string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
CredentialsUsername *string `json:"credentials_username,omitempty"`
|
||||
CredentialsPassword *string `json:"credentials_password,omitempty"`
|
||||
OAuthTokenURL string `json:"oauth_token_url"`
|
||||
GrantType OutgoingOAuthConnectionGrantType `json:"grant_type"`
|
||||
Audiences StringArray `json:"audiences"`
|
||||
}
|
||||
|
||||
func (oa *OutgoingOAuthConnection) Auditable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"id": oa.Id,
|
||||
"creator_id": oa.CreatorId,
|
||||
"create_at": oa.CreateAt,
|
||||
"update_at": oa.UpdateAt,
|
||||
"name": oa.Name,
|
||||
"grant_type": oa.GrantType,
|
||||
}
|
||||
}
|
||||
|
||||
// IsValid validates the object and returns an error if it isn't properly configured
|
||||
func (oa *OutgoingOAuthConnection) IsValid() *AppError {
|
||||
if !IsValidId(oa.Id) {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.id.error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if oa.CreateAt == 0 {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.create_at.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if oa.UpdateAt == 0 {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.update_at.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if !IsValidId(oa.CreatorId) {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.creator_id.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if utf8.RuneCountInString(oa.Name) > 64 {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.name.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(oa.ClientId) == 0 || utf8.RuneCountInString(oa.ClientId) > 255 {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.client_id.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(oa.ClientSecret) == 0 || utf8.RuneCountInString(oa.ClientSecret) > 255 {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.client_secret.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(oa.OAuthTokenURL) == 0 || utf8.RuneCountInString(oa.OAuthTokenURL) > 256 {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.oauth_token_url.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if err := oa.IsValidGrantType(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(oa.Audiences) == 0 {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.audience.empty", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if len(oa.Audiences) > 0 {
|
||||
for _, audience := range oa.Audiences {
|
||||
if !IsValidHTTPURL(audience) {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.audience.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsValidGrantType validates the grant type and its parameters returning an error if it isn't properly configured
|
||||
func (oa *OutgoingOAuthConnection) IsValidGrantType() *AppError {
|
||||
if !oa.GrantType.IsValid() {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.grant_type.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if oa.GrantType == OutgoingOAuthConnectionGrantTypePassword && (oa.CredentialsUsername == nil || oa.CredentialsPassword == nil) {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.password_credentials.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if oa.GrantType == OutgoingOAuthConnectionGrantTypePassword && (*oa.CredentialsUsername == "" || *oa.CredentialsPassword == "") {
|
||||
return NewAppError("OutgoingOAuthConnection.IsValid", "model.outgoing_oauth_connection.is_valid.password_credentials.error", nil, "id="+oa.Id, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreSave will set the Id if empty, ensuring the object has one and the create/update times.
|
||||
func (oa *OutgoingOAuthConnection) PreSave() {
|
||||
if oa.Id == "" {
|
||||
oa.Id = NewId()
|
||||
}
|
||||
|
||||
oa.CreateAt = GetMillis()
|
||||
oa.UpdateAt = oa.CreateAt
|
||||
}
|
||||
|
||||
// PreUpdate will set the update time to now.
|
||||
func (oa *OutgoingOAuthConnection) PreUpdate() {
|
||||
oa.UpdateAt = GetMillis()
|
||||
}
|
||||
|
||||
// Etag returns the ETag for the cache.
|
||||
func (oa *OutgoingOAuthConnection) Etag() string {
|
||||
return Etag(oa.Id, oa.UpdateAt)
|
||||
}
|
||||
|
||||
// Sanitize removes any sensitive fields from the OutgoingOAuthConnection object.
|
||||
func (oa *OutgoingOAuthConnection) Sanitize() {
|
||||
oa.ClientSecret = ""
|
||||
oa.CredentialsUsername = nil
|
||||
oa.CredentialsPassword = nil
|
||||
}
|
||||
|
||||
// OutgoingOAuthConnectionGetConnectionsFilter is used to filter outgoing connections
|
||||
type OutgoingOAuthConnectionGetConnectionsFilter struct {
|
||||
OffsetId string
|
||||
Limit int
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values for the filter
|
||||
func (oaf *OutgoingOAuthConnectionGetConnectionsFilter) SetDefaults() {
|
||||
if oaf.Limit == 0 {
|
||||
oaf.Limit = defaultGetConnectionsLimit
|
||||
}
|
||||
}
|
||||
313
server/public/model/outgoing_oauth_connection_test.go
Normal file
313
server/public/model/outgoing_oauth_connection_test.go
Normal file
@@ -0,0 +1,313 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
emptyString = ""
|
||||
someString = "userorpass"
|
||||
)
|
||||
|
||||
func newValidOutgoingOAuthConnection() *OutgoingOAuthConnection {
|
||||
return &OutgoingOAuthConnection{
|
||||
Id: NewId(),
|
||||
CreatorId: NewId(),
|
||||
Name: "Test Connection",
|
||||
ClientId: NewId(),
|
||||
ClientSecret: NewId(),
|
||||
OAuthTokenURL: "https://nowhere.com/oauth/token",
|
||||
GrantType: "client_credentials",
|
||||
CreateAt: GetMillis(),
|
||||
UpdateAt: GetMillis(),
|
||||
Audiences: []string{"https://nowhere.com"},
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutgoingOAuthConnectionIsValid(t *testing.T) {
|
||||
var cases = []struct {
|
||||
name string
|
||||
item func() *OutgoingOAuthConnection
|
||||
assert func(t *testing.T, oa *OutgoingOAuthConnection)
|
||||
}{
|
||||
{
|
||||
name: "valid",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
return newValidOutgoingOAuthConnection()
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Nil(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid id",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.Id = ""
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid create_at",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.CreateAt = 0
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid update_at",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.UpdateAt = 0
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid creator_id",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.CreatorId = ""
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid name",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.Name = ""
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid client_id",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.ClientId = ""
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "long client_id",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.ClientId = string(make([]byte, 257))
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid client_secret",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.ClientSecret = ""
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "long client_secret",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.ClientSecret = string(make([]byte, 257))
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty oauth_token_url",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.OAuthTokenURL = ""
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "long oauth_token_url",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.OAuthTokenURL = string(make([]byte, 257))
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid oauth_token_url",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.OAuthTokenURL = "invalid"
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid grant_type",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.GrantType = ""
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil password credentials",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.GrantType = OutgoingOAuthConnectionGrantTypePassword
|
||||
oa.CredentialsUsername = nil
|
||||
oa.CredentialsPassword = nil
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid password credentials username",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.GrantType = OutgoingOAuthConnectionGrantTypePassword
|
||||
oa.CredentialsUsername = &emptyString
|
||||
oa.CredentialsPassword = &someString
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid password credentials password",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.GrantType = OutgoingOAuthConnectionGrantTypePassword
|
||||
oa.CredentialsUsername = &someString
|
||||
oa.CredentialsPassword = &emptyString
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty password credentials",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.GrantType = OutgoingOAuthConnectionGrantTypePassword
|
||||
oa.CredentialsUsername = &emptyString
|
||||
oa.CredentialsPassword = &emptyString
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct password credentials",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.GrantType = OutgoingOAuthConnectionGrantTypePassword
|
||||
oa.CredentialsUsername = &someString
|
||||
oa.CredentialsPassword = &someString
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Nil(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty audience",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.Audiences = []string{}
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid audience",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.Audiences = []string{"https://nowhere.com", "invalid"}
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.assert(t, tc.item())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutgoingOAuthConnectionPreSave(t *testing.T) {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.PreSave()
|
||||
|
||||
require.NotEmpty(t, oa.Id)
|
||||
require.NotZero(t, oa.CreateAt)
|
||||
require.NotZero(t, oa.UpdateAt)
|
||||
}
|
||||
|
||||
func TestOutgoingOAuthConnectionPreUpdate(t *testing.T) {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.PreUpdate()
|
||||
|
||||
require.NotZero(t, oa.UpdateAt)
|
||||
}
|
||||
|
||||
func TestOutgoingOAuthConnectionEtag(t *testing.T) {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.PreSave()
|
||||
|
||||
require.NotEmpty(t, oa.Etag())
|
||||
}
|
||||
|
||||
func TestOutgoingOAuthConnectionSanitize(t *testing.T) {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.Sanitize()
|
||||
|
||||
require.Empty(t, oa.ClientSecret)
|
||||
require.Empty(t, oa.CredentialsUsername)
|
||||
require.Empty(t, oa.CredentialsPassword)
|
||||
}
|
||||
Reference in New Issue
Block a user