[MM-37984] Allow Desktop App to authenticate via external providers outside of the app on supported servers (#23795)

* WIP

* Add rate limiting for desktop token API

* Missing mocks

* Style fixes

* Update snapshots

* Maybe use an actual redirect link :P

* Refactoring for tests

* Add tests for server

* Fix lint issue

* Fix tests

* Fix lint

* Add front-end screen component

* Component logic

* Style changes

* Quick style fix

* Lint fixes

* Initial PR feedback

* Enable logging into the browser as well when completing the login process

* Refactor to push more logic to the other component

* Remove unnecessary helper code

* Fix i18n

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Devin Binnie
2023-07-12 09:25:05 -04:00
коммит произвёл GitHub
родитель 5e3c03a0a8
Коммит abdf4e58c3
41 изменённых файлов: 1847 добавлений и 24 удалений

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

@@ -0,0 +1,163 @@
// 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/v8/channels/store"
"github.com/mattermost/mattermost/server/v8/einterfaces"
sq "github.com/mattermost/squirrel"
"github.com/pkg/errors"
)
type SqlDesktopTokensStore struct {
*SqlStore
metrics einterfaces.MetricsInterface
}
func newSqlDesktopTokensStore(sqlStore *SqlStore, metrics einterfaces.MetricsInterface) store.DesktopTokensStore {
return &SqlDesktopTokensStore{
SqlStore: sqlStore,
metrics: metrics,
}
}
func (s *SqlDesktopTokensStore) GetUserId(desktopToken string, minCreateAt int64) (string, error) {
query := s.getQueryBuilder().
Select("UserId").
From("DesktopTokens").
Where(sq.Eq{
"DesktopToken": desktopToken,
}).
Where(sq.GtOrEq{
"CreateAt": minCreateAt,
})
dt := struct{ UserId sql.NullString }{}
err := s.GetReplicaX().GetBuilder(&dt, query)
if err != nil {
if err == sql.ErrNoRows {
return "", store.NewErrNotFound("DesktopTokens", desktopToken)
}
return "", errors.Wrapf(err, "No token for %s", desktopToken)
}
// Check if the string is NULL, if so just return a blank string
if dt.UserId.Valid {
return dt.UserId.String, nil
}
return "", nil
}
func (s *SqlDesktopTokensStore) Insert(desktopToken string, createAt int64, userId *string) error {
builder := s.getQueryBuilder().
Insert("DesktopTokens").
Columns("DesktopToken", "CreateAt", "UserId").
Values(desktopToken, createAt, userId)
query, args, err := builder.ToSql()
if err != nil {
return errors.Wrap(err, "insert_desktoptokens_tosql")
}
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
return errors.Wrap(err, "failed to insert token row")
}
return nil
}
func (s *SqlDesktopTokensStore) SetUserId(desktopToken string, minCreateAt int64, userId string) error {
builder := s.getQueryBuilder().
Update("DesktopTokens").
Set("UserId", userId).
Where(sq.Eq{
"DesktopToken": desktopToken,
}).
Where(sq.GtOrEq{
"CreateAt": minCreateAt,
})
query, args, err := builder.ToSql()
if err != nil {
return errors.Wrap(err, "set_userid_desktoptokens_tosql")
}
result, err := s.GetMasterX().Exec(query, args...)
if err != nil {
return errors.Wrap(err, "failed to update token row")
}
num, err := result.RowsAffected()
if err != nil {
return errors.Wrap(err, "nothing updated")
}
if num == 0 {
return errors.New("no rows updated")
}
return nil
}
func (s *SqlDesktopTokensStore) Delete(desktopToken string) error {
builder := s.getQueryBuilder().
Delete("DesktopTokens").
Where(sq.Eq{
"DesktopToken": desktopToken,
})
query, args, err := builder.ToSql()
if err != nil {
return errors.Wrap(err, "delete_desktoptokens_tosql")
}
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
return errors.Wrap(err, "failed to delete token row")
}
return nil
}
func (s *SqlDesktopTokensStore) DeleteByUserId(userId string) error {
builder := s.getQueryBuilder().
Delete("DesktopTokens").
Where(sq.Eq{
"UserId": userId,
})
query, args, err := builder.ToSql()
if err != nil {
return errors.Wrap(err, "delete_by_userid_desktoptokens_tosql")
}
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
return errors.Wrap(err, "failed to delete token row")
}
return nil
}
func (s *SqlDesktopTokensStore) DeleteOlderThan(minCreateAt int64) error {
builder := s.getQueryBuilder().
Delete("DesktopTokens").
Where(sq.Lt{
"CreateAt": minCreateAt,
})
query, args, err := builder.ToSql()
if err != nil {
return errors.Wrap(err, "delete_old_desktoptokens_tosql")
}
if _, err = s.GetMasterX().Exec(query, args...); err != nil {
return errors.Wrap(err, "failed to delete token row")
}
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 TestDesktopTokensStore(t *testing.T) {
StoreTestWithSqlStore(t, storetest.TestDesktopTokensStore)
}

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

@@ -105,6 +105,7 @@ type SqlStoreStores struct {
postAcknowledgement store.PostAcknowledgementStore
postPersistentNotification store.PostPersistentNotificationStore
trueUpReview store.TrueUpReviewStore
desktopTokens store.DesktopTokensStore
}
type SqlStore struct {
@@ -226,6 +227,7 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlS
store.stores.postAcknowledgement = newSqlPostAcknowledgementStore(store)
store.stores.postPersistentNotification = newSqlPostPersistentNotificationStore(store)
store.stores.trueUpReview = newSqlTrueUpReviewStore(store)
store.stores.desktopTokens = newSqlDesktopTokensStore(store, metrics)
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
@@ -1077,6 +1079,10 @@ func (ss *SqlStore) TrueUpReview() store.TrueUpReviewStore {
return ss.stores.trueUpReview
}
func (ss *SqlStore) DesktopTokens() store.DesktopTokensStore {
return ss.stores.desktopTokens
}
func (ss *SqlStore) DropAllTables() {
var tableSchemaFn string
if ss.DriverName() == model.DatabaseDriverPostgres {