[WIP] [MM-55031] OAuth Outgoing Connection App integration (#25379)
* 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 * license changes * OAuthOutgoingConnectionInterface * Oauth -> OAuth * make generated * merge migrations * renamed migrations * model change suggestions * refactor test functionsn * migration typo * refactor store table names * updated sanitize test * cleanup merge * refactor symbol * list endpoint * oauthoutgoingconnection -> outgoingoauthconnection * signature change * i18n update * granttype typo * naming * api list * uppercase typo * i18n * missing license header * fixed path in comments * updated openapi definitions * sanitize connections * make generated * test license and no feature flag * removed t.fatal * updated testhelper calls * yaml schema fixes * switched interface name * suggested translation * missing i18n translation * address comments * updated i18n
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
287cbad2d5
Коммит
81a1d725a0
@@ -471,6 +471,14 @@ func (c *Client4) oAuthAppRoute(appId string) string {
|
||||
return fmt.Sprintf("/oauth/apps/%v", appId)
|
||||
}
|
||||
|
||||
func (c *Client4) outgoingOAuthConnectionsRoute() string {
|
||||
return "/oauth/outgoing_connections"
|
||||
}
|
||||
|
||||
func (c *Client4) outgoingOAuthConnectionRoute(id string) string {
|
||||
return fmt.Sprintf("/oauth/outgoing_connections/%s", id)
|
||||
}
|
||||
|
||||
func (c *Client4) jobsRoute() string {
|
||||
return "/jobs"
|
||||
}
|
||||
@@ -6003,6 +6011,36 @@ func (c *Client4) GetOAuthAccessToken(ctx context.Context, data url.Values) (*Ac
|
||||
return ar, BuildResponse(rp), nil
|
||||
}
|
||||
|
||||
// OutgoingOAuthConnection section
|
||||
|
||||
// GetOutgoingOAuthConnections retrieves the outgoing OAuth connections.
|
||||
func (c *Client4) GetOutgoingOAuthConnections(ctx context.Context, fromID string, limit int) ([]*OutgoingOAuthConnection, *Response, error) {
|
||||
r, err := c.DoAPIGet(ctx, c.outgoingOAuthConnectionsRoute(), "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
var connections []*OutgoingOAuthConnection
|
||||
if err := json.NewDecoder(r.Body).Decode(&connections); err != nil {
|
||||
return nil, nil, NewAppError("GetOutgoingOAuthConnections", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return connections, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// GetOutgoingOAuthConnection retrieves the outgoing OAuth connection with the given ID.
|
||||
func (c *Client4) GetOutgoingOAuthConnection(ctx context.Context, id string) (*OutgoingOAuthConnection, *Response, error) {
|
||||
r, err := c.DoAPIGet(ctx, c.outgoingOAuthConnectionRoute(id), "")
|
||||
if err != nil {
|
||||
return nil, BuildResponse(r), err
|
||||
}
|
||||
defer closeBody(r)
|
||||
var connection *OutgoingOAuthConnection
|
||||
if err := json.NewDecoder(r.Body).Decode(&connection); err != nil {
|
||||
return nil, nil, NewAppError("GetOutgoingOAuthConnection", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||
}
|
||||
return connection, BuildResponse(r), nil
|
||||
}
|
||||
|
||||
// Elasticsearch Section
|
||||
|
||||
// TestElasticsearch will attempt to connect to the configured Elasticsearch server and return OK if configured.
|
||||
|
||||
@@ -50,6 +50,8 @@ type FeatureFlags struct {
|
||||
ConsumePostHook bool
|
||||
|
||||
CloudAnnualRenewals bool
|
||||
|
||||
OutgoingOAuthConnections bool
|
||||
}
|
||||
|
||||
func (f *FeatureFlags) SetDefaults() {
|
||||
@@ -69,6 +71,7 @@ func (f *FeatureFlags) SetDefaults() {
|
||||
f.CloudIPFiltering = false
|
||||
f.ConsumePostHook = false
|
||||
f.CloudAnnualRenewals = false
|
||||
f.OutgoingOAuthConnections = false
|
||||
}
|
||||
|
||||
// ToMap returns the feature flags as a map[string]string
|
||||
|
||||
@@ -158,6 +158,7 @@ type Features struct {
|
||||
Cloud *bool `json:"cloud"`
|
||||
SharedChannels *bool `json:"shared_channels"`
|
||||
RemoteClusterService *bool `json:"remote_cluster_service"`
|
||||
OutgoingOAuthConnections *bool `json:"outgoing_oauth_connections"`
|
||||
|
||||
// after we enabled more features we'll need to control them with this
|
||||
FutureFeatures *bool `json:"future_features"`
|
||||
@@ -191,6 +192,7 @@ func (f *Features) ToMap() map[string]any {
|
||||
"shared_channels": *f.SharedChannels,
|
||||
"remote_cluster_service": *f.RemoteClusterService,
|
||||
"future": *f.FutureFeatures,
|
||||
"outgoing_oauth_connections": *f.OutgoingOAuthConnections,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,6 +316,10 @@ func (f *Features) SetDefaults() {
|
||||
if f.RemoteClusterService == nil {
|
||||
f.RemoteClusterService = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
|
||||
if f.OutgoingOAuthConnections == nil {
|
||||
f.OutgoingOAuthConnections = NewBool(*f.FutureFeatures)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *License) IsExpired() bool {
|
||||
|
||||
@@ -27,8 +27,8 @@ type OutgoingOAuthConnection struct {
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
Name string `json:"name"`
|
||||
ClientId string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
ClientId string `json:"client_id,omitempty"`
|
||||
ClientSecret string `json:"client_secret,omitempty"`
|
||||
CredentialsUsername *string `json:"credentials_username,omitempty"`
|
||||
CredentialsPassword *string `json:"credentials_password,omitempty"`
|
||||
OAuthTokenURL string `json:"oauth_token_url"`
|
||||
@@ -47,6 +47,14 @@ func (oa *OutgoingOAuthConnection) Auditable() map[string]interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize removes any sensitive fields from the OutgoingOAuthConnection object.
|
||||
func (oa *OutgoingOAuthConnection) Sanitize() {
|
||||
oa.ClientId = ""
|
||||
oa.ClientSecret = ""
|
||||
oa.CredentialsUsername = nil
|
||||
oa.CredentialsPassword = nil
|
||||
}
|
||||
|
||||
// IsValid validates the object and returns an error if it isn't properly configured
|
||||
func (oa *OutgoingOAuthConnection) IsValid() *AppError {
|
||||
if !IsValidId(oa.Id) {
|
||||
@@ -65,19 +73,19 @@ func (oa *OutgoingOAuthConnection) IsValid() *AppError {
|
||||
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 {
|
||||
if oa.Name == "" || 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 {
|
||||
if oa.ClientId == "" || 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 {
|
||||
if oa.ClientSecret == "" || 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 {
|
||||
if oa.OAuthTokenURL == "" || 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)
|
||||
}
|
||||
|
||||
@@ -137,13 +145,6 @@ 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
|
||||
|
||||
@@ -19,7 +19,7 @@ func newValidOutgoingOAuthConnection() *OutgoingOAuthConnection {
|
||||
ClientId: NewId(),
|
||||
ClientSecret: NewId(),
|
||||
OAuthTokenURL: "https://nowhere.com/oauth/token",
|
||||
GrantType: "client_credentials",
|
||||
GrantType: OutgoingOAuthConnectionGrantTypeClientCredentials,
|
||||
CreateAt: GetMillis(),
|
||||
UpdateAt: GetMillis(),
|
||||
Audiences: []string{"https://nowhere.com"},
|
||||
@@ -52,6 +52,17 @@ func TestOutgoingOAuthConnectionIsValid(t *testing.T) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty name",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.Name = ""
|
||||
return oa
|
||||
},
|
||||
assert: func(t *testing.T, oa *OutgoingOAuthConnection) {
|
||||
require.Error(t, oa.IsValid())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid create_at",
|
||||
item: func() *OutgoingOAuthConnection {
|
||||
@@ -307,6 +318,7 @@ func TestOutgoingOAuthConnectionSanitize(t *testing.T) {
|
||||
oa := newValidOutgoingOAuthConnection()
|
||||
oa.Sanitize()
|
||||
|
||||
require.Empty(t, oa.ClientId)
|
||||
require.Empty(t, oa.ClientSecret)
|
||||
require.Empty(t, oa.CredentialsUsername)
|
||||
require.Empty(t, oa.CredentialsPassword)
|
||||
|
||||
Ссылка в новой задаче
Block a user