Remove remote users from the license counting and explicitly dissallow them to log in (#22582)
* Making all the counts aware of Remote users * Disable login for remote users * Adding tests for login remote_users error * Adding tests for the store * Adding frontend part of not counting remote users in the license * Addressing PR review comment * Adding the new ExternaUserId field to users * Running make migrations-extract * Running make app-layers and make gen-serialized * Revert "Adding the new ExternaUserId field to users" This reverts commit 12e5fd518962a16cdbdb8964179b6cd8e915f230. * Adding GetUserByRemoteID methods * Adding needed migration for users * i18n-extract * Fixing postgres increase remote user id field size migration up and down * run make gen-serialized * Removing migration code * Not count remote users as part of the cloud pricing * Add the cloud subscription when a user gets promote from remote to not-remote * Fixing merge problems --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5a349873f7
Коммит
5f7482e541
@@ -11148,6 +11148,24 @@ func (s *OpenTracingLayerUserStore) GetByEmail(email string) (*model.User, error
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerUserStore) GetByRemoteID(remoteID string) (*model.User, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "UserStore.GetByRemoteID")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.UserStore.GetByRemoteID(remoteID)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerUserStore) GetByUsername(username string) (*model.User, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "UserStore.GetByUsername")
|
||||
|
||||
@@ -12739,6 +12739,27 @@ func (s *RetryLayerUserStore) GetByEmail(email string) (*model.User, error) {
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerUserStore) GetByRemoteID(remoteID string) (*model.User, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.UserStore.GetByRemoteID(remoteID)
|
||||
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 *RetryLayerUserStore) GetByUsername(username string) (*model.User, error) {
|
||||
|
||||
tries := 0
|
||||
|
||||
@@ -1174,6 +1174,26 @@ func (us SqlUserStore) GetByEmail(email string) (*model.User, error) {
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (us SqlUserStore) GetByRemoteID(remoteID string) (*model.User, error) {
|
||||
query := us.usersQuery.Where(sq.Eq{"RemoteId": remoteID})
|
||||
|
||||
queryString, args, err := query.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get_by_remote_id_tosql")
|
||||
}
|
||||
|
||||
user := model.User{}
|
||||
if err := us.GetReplicaX().Get(&user, queryString, args...); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errors.Wrap(store.NewErrNotFound("User", fmt.Sprintf("remoteid=%s", remoteID)), "failed to find User")
|
||||
}
|
||||
|
||||
return nil, errors.Wrapf(err, "failed to get User with RemoteId=%s", remoteID)
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (us SqlUserStore) GetByAuth(authData *string, authService string) (*model.User, error) {
|
||||
if authData == nil || *authData == "" {
|
||||
return nil, store.NewErrInvalidInput("User", "<authData>", "empty or nil")
|
||||
@@ -1310,6 +1330,10 @@ func (us SqlUserStore) Count(options model.UserCountOptions) (int64, error) {
|
||||
query = query.Where("u.DeleteAt = 0")
|
||||
}
|
||||
|
||||
if !options.IncludeRemoteUsers {
|
||||
query = query.Where(sq.Or{sq.Eq{"u.RemoteId": ""}, sq.Eq{"u.RemoteId": nil}})
|
||||
}
|
||||
|
||||
isPostgreSQL := us.DriverName() == model.DatabaseDriverPostgres
|
||||
if options.IncludeBotAccounts {
|
||||
if options.ExcludeRegularUsers {
|
||||
@@ -1365,8 +1389,17 @@ func (us SqlUserStore) AnalyticsActiveCount(timePeriod int64, options model.User
|
||||
query = query.Where(sq.Expr("UserId NOT IN (SELECT UserId FROM Bots)"))
|
||||
}
|
||||
}
|
||||
|
||||
if !options.IncludeRemoteUsers || !options.IncludeDeleted {
|
||||
query = query.LeftJoin("Users ON s.UserId = Users.Id")
|
||||
}
|
||||
|
||||
if !options.IncludeRemoteUsers {
|
||||
query = query.Where(sq.Or{sq.Eq{"Users.RemoteId": ""}, sq.Eq{"Users.RemoteId": nil}})
|
||||
}
|
||||
|
||||
if !options.IncludeDeleted {
|
||||
query = query.LeftJoin("Users ON s.UserId = Users.Id").Where("Users.DeleteAt = 0")
|
||||
query = query.Where("Users.DeleteAt = 0")
|
||||
}
|
||||
|
||||
queryStr, args, err := query.ToSql()
|
||||
@@ -1393,8 +1426,16 @@ func (us SqlUserStore) AnalyticsActiveCountForPeriod(startTime int64, endTime in
|
||||
}
|
||||
}
|
||||
|
||||
if !options.IncludeRemoteUsers || !options.IncludeDeleted {
|
||||
query = query.LeftJoin("Users ON s.UserId = Users.Id")
|
||||
}
|
||||
|
||||
if !options.IncludeRemoteUsers {
|
||||
query = query.Where(sq.Or{sq.Eq{"Users.RemoteId": ""}, sq.Eq{"Users.RemoteId": nil}})
|
||||
}
|
||||
|
||||
if !options.IncludeDeleted {
|
||||
query = query.LeftJoin("Users ON s.UserId = Users.Id").Where("Users.DeleteAt = 0")
|
||||
query = query.Where("Users.DeleteAt = 0")
|
||||
}
|
||||
|
||||
queryStr, args, err := query.ToSql()
|
||||
|
||||
@@ -424,6 +424,7 @@ type UserStore interface {
|
||||
GetProfileByGroupChannelIdsForUser(userID string, channelIds []string) (map[string][]*model.User, error)
|
||||
InvalidateProfileCacheForUser(userID string)
|
||||
GetByEmail(email string) (*model.User, error)
|
||||
GetByRemoteID(remoteID string) (*model.User, error)
|
||||
GetByAuth(authData *string, authService string) (*model.User, error)
|
||||
GetAllUsingAuthService(authService string) ([]*model.User, error)
|
||||
GetAllNotInAuthService(authServices []string) ([]*model.User, error)
|
||||
|
||||
@@ -541,6 +541,32 @@ func (_m *UserStore) GetByEmail(email string) (*model.User, error) {
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetByRemoteID provides a mock function with given fields: remoteID
|
||||
func (_m *UserStore) GetByRemoteID(remoteID string) (*model.User, error) {
|
||||
ret := _m.Called(remoteID)
|
||||
|
||||
var r0 *model.User
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (*model.User, error)); ok {
|
||||
return rf(remoteID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) *model.User); ok {
|
||||
r0 = rf(remoteID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.User)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(remoteID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetByUsername provides a mock function with given fields: username
|
||||
func (_m *UserStore) GetByUsername(username string) (*model.User, error) {
|
||||
ret := _m.Called(username)
|
||||
|
||||
@@ -3963,6 +3963,15 @@ func testCount(t *testing.T, ss store.Store) {
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, ss.User().PermanentDelete(deletedUser.Id)) }()
|
||||
|
||||
// Remote User
|
||||
remoteId := "remote-id"
|
||||
remoteUser, err := ss.User().Save(&model.User{
|
||||
Email: MakeEmail(),
|
||||
RemoteId: &remoteId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer func() { require.NoError(t, ss.User().PermanentDelete(remoteUser.Id)) }()
|
||||
|
||||
// Bot
|
||||
botUser, err := ss.User().Save(&model.User{
|
||||
Email: MakeEmail(),
|
||||
@@ -4067,6 +4076,71 @@ func testCount(t *testing.T, ss store.Store) {
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Include remote accounts no deleted accounts and no team id",
|
||||
model.UserCountOptions{
|
||||
IncludeRemoteUsers: true,
|
||||
IncludeDeleted: false,
|
||||
TeamId: "",
|
||||
},
|
||||
5,
|
||||
},
|
||||
{
|
||||
"Include delete accounts no remote accounts and no team id",
|
||||
model.UserCountOptions{
|
||||
IncludeRemoteUsers: false,
|
||||
IncludeDeleted: true,
|
||||
TeamId: "",
|
||||
},
|
||||
5,
|
||||
},
|
||||
{
|
||||
"Include remote accounts and deleted accounts and no team id",
|
||||
model.UserCountOptions{
|
||||
IncludeRemoteUsers: true,
|
||||
IncludeDeleted: true,
|
||||
TeamId: "",
|
||||
},
|
||||
6,
|
||||
},
|
||||
{
|
||||
"Include remote accounts and deleted accounts with existing team id",
|
||||
model.UserCountOptions{
|
||||
IncludeRemoteUsers: true,
|
||||
IncludeDeleted: true,
|
||||
TeamId: teamId,
|
||||
},
|
||||
4,
|
||||
},
|
||||
{
|
||||
"Include remote accounts and deleted accounts with fake team id",
|
||||
model.UserCountOptions{
|
||||
IncludeRemoteUsers: true,
|
||||
IncludeDeleted: true,
|
||||
TeamId: model.NewId(),
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Include remote accounts and deleted accounts with existing team id and view restrictions allowing team",
|
||||
model.UserCountOptions{
|
||||
IncludeRemoteUsers: true,
|
||||
IncludeDeleted: true,
|
||||
TeamId: teamId,
|
||||
ViewRestrictions: &model.ViewUsersRestrictions{Teams: []string{teamId}},
|
||||
},
|
||||
4,
|
||||
},
|
||||
{
|
||||
"Include remote accounts and deleted accounts with existing team id and view restrictions not allowing current team",
|
||||
model.UserCountOptions{
|
||||
IncludeRemoteUsers: true,
|
||||
IncludeDeleted: true,
|
||||
TeamId: teamId,
|
||||
ViewRestrictions: &model.ViewUsersRestrictions{Teams: []string{model.NewId()}},
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
"Filter by system admins only",
|
||||
model.UserCountOptions{
|
||||
|
||||
@@ -10032,6 +10032,22 @@ func (s *TimerLayerUserStore) GetByEmail(email string) (*model.User, error) {
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerUserStore) GetByRemoteID(remoteID string) (*model.User, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, err := s.UserStore.GetByRemoteID(remoteID)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("UserStore.GetByRemoteID", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerUserStore) GetByUsername(username string) (*model.User, error) {
|
||||
start := time.Now()
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user