[MM-28717] Refactor applyMultiRoleFilters to use sq builder (#15500)

* Refactor apply multi role filters and add role filters to get all profiles

* Add some tests

* Fix tests

* Fix lint

* Trigger CI

* Rename param to make more sense

* Tie get filtered user stats to usermanagement read users

* Dont filter out other system roles when searching for team members or team admins only filter out system admins

* add new permissions

* add migration

* fix test

* remove system roles as default permissions

* implement changes discussed with dennis

* add read only and fix i18n

* use model consts instead of strings

* turn the permissions into pseudo constants

* Update read only default permissions

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Hossein Ahmadian-Yazdi <hyazdi1997@gmail.com>
Этот коммит содержится в:
Farhan Munshi
2020-11-13 10:57:57 -05:00
коммит произвёл GitHub
родитель 1d15900f84
Коммит c9a4a475d3
11 изменённых файлов: 268 добавлений и 126 удалений

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

@@ -412,6 +412,7 @@ func (us SqlUserStore) GetAllProfiles(options *model.UserGetOptions) ([]*model.U
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, true)
query = applyRoleFilter(query, options.Role, isPostgreSQL)
query = applyMultiRoleFilters(query, options.Roles, []string{}, []string{}, isPostgreSQL)
if options.Inactive {
query = query.Where("u.DeleteAt != 0")
@@ -451,119 +452,75 @@ func applyRoleFilter(query sq.SelectBuilder, role string, isPostgreSQL bool) sq.
return query.Where("u.Roles LIKE ? ESCAPE '*'", roleParam)
}
func applyMultiRoleFilters(query sq.SelectBuilder, roles []string, teamRoles []string, channelRoles []string) sq.SelectBuilder {
queryString := ""
if len(roles) > 0 && roles[0] != "" {
schemeGuest := false
schemeAdmin := false
schemeUser := false
func applyMultiRoleFilters(query sq.SelectBuilder, systemRoles []string, teamRoles []string, channelRoles []string, isPostgreSQL bool) sq.SelectBuilder {
sqOr := sq.Or{}
for _, role := range roles {
if len(systemRoles) > 0 && systemRoles[0] != "" {
for _, role := range systemRoles {
queryRole := wildcardSearchTerm(role)
switch role {
case model.SYSTEM_ADMIN_ROLE_ID:
schemeAdmin = true
case model.SYSTEM_USER_ROLE_ID:
schemeUser = true
case model.SYSTEM_GUEST_ROLE_ID:
schemeGuest = true
}
}
if schemeAdmin || schemeUser || schemeGuest {
if schemeAdmin && schemeUser {
queryString += `(u.Roles LIKE '%system_user%' OR u.Roles LIKE '%system_admin%') `
} else if schemeAdmin {
queryString += `(u.Roles LIKE '%system_admin%') `
} else if schemeUser {
queryString += `(u.Roles LIKE '%system_user%' AND u.Roles NOT LIKE '%system_admin%') `
}
if schemeGuest {
if queryString != "" {
queryString += "OR "
// If querying for a `system_user` ensure that the user is only a system_user.
sqOr = append(sqOr, sq.Eq{"u.Roles": role})
case model.SYSTEM_GUEST_ROLE_ID, model.SYSTEM_ADMIN_ROLE_ID, model.SYSTEM_USER_MANAGER_ROLE_ID, model.SYSTEM_READ_ONLY_ADMIN_ROLE_ID, model.SYSTEM_MANAGER_ROLE_ID:
// If querying for any other roles search using a wildcard.
if isPostgreSQL {
sqOr = append(sqOr, sq.ILike{"u.Roles": queryRole})
} else {
sqOr = append(sqOr, sq.Like{"u.Roles": queryRole})
}
queryString += `(u.Roles LIKE '%system_guest%') `
}
}
}
if len(channelRoles) > 0 && channelRoles[0] != "" {
schemeGuest := false
schemeAdmin := false
schemeUser := false
for _, channelRole := range channelRoles {
switch channelRole {
case model.CHANNEL_ADMIN_ROLE_ID:
schemeAdmin = true
case model.CHANNEL_USER_ROLE_ID:
schemeUser = true
case model.CHANNEL_GUEST_ROLE_ID:
schemeGuest = true
}
}
if schemeAdmin || schemeUser || schemeGuest {
if queryString != "" {
queryString += "OR "
}
if schemeAdmin && schemeUser {
queryString += `(cm.SchemeUser = true AND u.Roles = 'system_user')`
} else if schemeAdmin {
queryString += `(cm.SchemeAdmin = true AND u.Roles = 'system_user')`
} else if schemeUser {
queryString += `(cm.SchemeUser = true AND cm.SchemeAdmin = false AND u.Roles = 'system_user')`
}
if schemeGuest {
if queryString != "" && queryString[len(queryString)-3:] != "OR " {
queryString += "OR "
if isPostgreSQL {
sqOr = append(sqOr, sq.And{sq.Eq{"cm.SchemeAdmin": true}, sq.NotILike{"u.Roles": wildcardSearchTerm(model.SYSTEM_ADMIN_ROLE_ID)}})
} else {
sqOr = append(sqOr, sq.And{sq.Eq{"cm.SchemeAdmin": true}, sq.NotLike{"u.Roles": wildcardSearchTerm(model.SYSTEM_ADMIN_ROLE_ID)}})
}
queryString += `(cm.SchemeGuest = true AND u.Roles = 'system_guest')`
case model.CHANNEL_USER_ROLE_ID:
if isPostgreSQL {
sqOr = append(sqOr, sq.And{sq.Eq{"cm.SchemeUser": true}, sq.Eq{"cm.SchemeAdmin": false}, sq.NotILike{"u.Roles": wildcardSearchTerm(model.SYSTEM_ADMIN_ROLE_ID)}})
} else {
sqOr = append(sqOr, sq.And{sq.Eq{"cm.SchemeUser": true}, sq.Eq{"cm.SchemeAdmin": false}, sq.NotLike{"u.Roles": wildcardSearchTerm(model.SYSTEM_ADMIN_ROLE_ID)}})
}
case model.CHANNEL_GUEST_ROLE_ID:
sqOr = append(sqOr, sq.Eq{"cm.SchemeGuest": true})
}
}
}
if len(teamRoles) > 0 && teamRoles[0] != "" {
schemeAdmin := false
schemeUser := false
schemeGuest := false
for _, teamRole := range teamRoles {
switch teamRole {
case model.TEAM_ADMIN_ROLE_ID:
schemeAdmin = true
case model.TEAM_USER_ROLE_ID:
schemeUser = true
case model.TEAM_GUEST_ROLE_ID:
schemeGuest = true
}
}
if schemeAdmin || schemeUser || schemeGuest {
if queryString != "" {
queryString += "OR "
}
if schemeAdmin && schemeUser {
queryString += `(tm.SchemeUser = true AND u.Roles = 'system_user')`
} else if schemeAdmin {
queryString += `(tm.SchemeAdmin = true AND u.Roles = 'system_user')`
} else if schemeUser {
queryString += `(tm.SchemeUser = true AND tm.SchemeAdmin = false AND u.Roles = 'system_user')`
}
if schemeGuest {
if queryString != "" && queryString[len(queryString)-3:] != "OR " {
queryString += "OR "
if isPostgreSQL {
sqOr = append(sqOr, sq.And{sq.Eq{"tm.SchemeAdmin": true}, sq.NotILike{"u.Roles": wildcardSearchTerm(model.SYSTEM_ADMIN_ROLE_ID)}})
} else {
sqOr = append(sqOr, sq.And{sq.Eq{"tm.SchemeAdmin": true}, sq.NotLike{"u.Roles": wildcardSearchTerm(model.SYSTEM_ADMIN_ROLE_ID)}})
}
queryString += `(tm.SchemeGuest = true AND u.Roles = 'system_guest')`
case model.TEAM_USER_ROLE_ID:
if isPostgreSQL {
sqOr = append(sqOr, sq.And{sq.Eq{"tm.SchemeUser": true}, sq.Eq{"tm.SchemeAdmin": false}, sq.NotILike{"u.Roles": wildcardSearchTerm(model.SYSTEM_ADMIN_ROLE_ID)}})
} else {
sqOr = append(sqOr, sq.And{sq.Eq{"tm.SchemeUser": true}, sq.Eq{"tm.SchemeAdmin": false}, sq.NotLike{"u.Roles": wildcardSearchTerm(model.SYSTEM_ADMIN_ROLE_ID)}})
}
case model.TEAM_GUEST_ROLE_ID:
sqOr = append(sqOr, sq.Eq{"tm.SchemeGuest": true})
}
}
}
if queryString != "" {
query = query.Where("(" + queryString + ")")
if len(sqOr) > 0 {
return query.Where(sqOr)
} else {
return query
}
return query
}
func applyChannelGroupConstrainedFilter(query sq.SelectBuilder, channelId string) sq.SelectBuilder {
@@ -633,7 +590,7 @@ func (us SqlUserStore) GetProfiles(options *model.UserGetOptions) ([]*model.User
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, true)
query = applyRoleFilter(query, options.Role, isPostgreSQL)
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles)
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles, isPostgreSQL)
if options.Inactive {
query = query.Where("u.DeleteAt != 0")
@@ -1228,7 +1185,7 @@ func (us SqlUserStore) Count(options model.UserCountOptions) (int64, error) {
query = query.LeftJoin("ChannelMembers AS cm ON u.Id = cm.UserId").Where("cm.ChannelId = ?", options.ChannelId)
}
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, false)
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles)
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles, isPostgreSQL)
if isPostgreSQL {
query = query.PlaceholderFormat(sq.Dollar)
@@ -1461,7 +1418,7 @@ func (us SqlUserStore) performSearch(query sq.SelectBuilder, term string, option
isPostgreSQL := us.DriverName() == model.DATABASE_DRIVER_POSTGRES
query = applyRoleFilter(query, options.Role, isPostgreSQL)
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles)
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles, isPostgreSQL)
if !options.AllowInactive {
query = query.Where("u.DeleteAt = 0")

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

@@ -368,6 +368,7 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
u1, err := ss.User().Save(&model.User{
Email: MakeEmail(),
Username: "u1" + model.NewId(),
Roles: model.SYSTEM_USER_ROLE_ID,
})
require.Nil(t, err)
defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }()
@@ -375,6 +376,7 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
u2, err := ss.User().Save(&model.User{
Email: MakeEmail(),
Username: "u2" + model.NewId(),
Roles: model.SYSTEM_USER_ROLE_ID,
})
require.Nil(t, err)
defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }()
@@ -423,14 +425,15 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
Email: MakeEmail(),
Username: "u7" + model.NewId(),
DeleteAt: model.GetMillis(),
Roles: model.SYSTEM_USER_ROLE_ID,
})
require.Nil(t, err)
defer func() { require.Nil(t, ss.User().PermanentDelete(u7.Id)) }()
t.Run("get offset 0, limit 100", func(t *testing.T) {
options := &model.UserGetOptions{Page: 0, PerPage: 100}
actual, err := ss.User().GetAllProfiles(options)
require.Nil(t, err)
actual, userErr := ss.User().GetAllProfiles(options)
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u1),
@@ -444,19 +447,19 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
})
t.Run("get offset 0, limit 1", func(t *testing.T) {
actual, err := ss.User().GetAllProfiles(&model.UserGetOptions{
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 1,
})
require.Nil(t, err)
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u1),
}, actual)
})
t.Run("get all", func(t *testing.T) {
actual, err := ss.User().GetAll()
require.Nil(t, err)
actual, userErr := ss.User().GetAll()
require.Nil(t, userErr)
require.Equal(t, []*model.User{
u1,
@@ -474,8 +477,8 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
uNew := &model.User{}
uNew.Email = MakeEmail()
_, err := ss.User().Save(uNew)
require.Nil(t, err)
_, userErr := ss.User().Save(uNew)
require.Nil(t, userErr)
defer func() { require.Nil(t, ss.User().PermanentDelete(uNew.Id)) }()
updatedEtag := ss.User().GetEtagForAllProfiles()
@@ -483,12 +486,12 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
})
t.Run("filter to system_admin role", func(t *testing.T) {
actual, err := ss.User().GetAllProfiles(&model.UserGetOptions{
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 10,
Role: "system_admin",
})
require.Nil(t, err)
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u5),
sanitized(u6),
@@ -496,25 +499,25 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
})
t.Run("filter to system_admin role, inactive", func(t *testing.T) {
actual, err := ss.User().GetAllProfiles(&model.UserGetOptions{
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 10,
Role: "system_admin",
Inactive: true,
})
require.Nil(t, err)
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u6),
}, actual)
})
t.Run("filter to inactive", func(t *testing.T) {
actual, err := ss.User().GetAllProfiles(&model.UserGetOptions{
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 10,
Inactive: true,
})
require.Nil(t, err)
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u6),
sanitized(u7),
@@ -522,12 +525,12 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
})
t.Run("filter to active", func(t *testing.T) {
actual, err := ss.User().GetAllProfiles(&model.UserGetOptions{
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 10,
Active: true,
})
require.Nil(t, err)
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u1),
sanitized(u2),
@@ -538,18 +541,87 @@ func testUserStoreGetAllProfiles(t *testing.T, ss store.Store) {
})
t.Run("try to filter to active and inactive", func(t *testing.T) {
actual, err := ss.User().GetAllProfiles(&model.UserGetOptions{
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 10,
Inactive: true,
Active: true,
})
require.Nil(t, err)
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u6),
sanitized(u7),
}, actual)
})
u8, err := ss.User().Save(&model.User{
Email: MakeEmail(),
Username: "u8" + model.NewId(),
DeleteAt: model.GetMillis(),
Roles: "system_user_manager system_user",
})
require.Nil(t, err)
defer func() { require.Nil(t, ss.User().PermanentDelete(u8.Id)) }()
u9, err := ss.User().Save(&model.User{
Email: MakeEmail(),
Username: "u9" + model.NewId(),
DeleteAt: model.GetMillis(),
Roles: "system_manager system_user",
})
require.Nil(t, err)
defer func() { require.Nil(t, ss.User().PermanentDelete(u9.Id)) }()
u10, err := ss.User().Save(&model.User{
Email: MakeEmail(),
Username: "u10" + model.NewId(),
DeleteAt: model.GetMillis(),
Roles: "system_read_only_admin system_user",
})
require.Nil(t, err)
defer func() { require.Nil(t, ss.User().PermanentDelete(u10.Id)) }()
t.Run("filter by system_user_manager role", func(t *testing.T) {
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 10,
Roles: []string{"system_user_manager"},
})
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u8),
}, actual)
})
t.Run("filter by multiple system roles", func(t *testing.T) {
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 10,
Roles: []string{"system_manager", "system_user_manager", "system_read_only_admin", "system_admin"},
})
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u10),
sanitized(u5),
sanitized(u6),
sanitized(u8),
sanitized(u9),
}, actual)
})
t.Run("filter by system_user only", func(t *testing.T) {
actual, userErr := ss.User().GetAllProfiles(&model.UserGetOptions{
Page: 0,
PerPage: 10,
Roles: []string{"system_user"},
})
require.Nil(t, userErr)
require.Equal(t, []*model.User{
sanitized(u1),
sanitized(u2),
sanitized(u7),
}, actual)
})
}
func testUserStoreGetProfiles(t *testing.T, ss store.Store) {
@@ -2437,7 +2509,7 @@ func testUserStoreSearch(t *testing.T, ss store.Store) {
u2 := &model.User{
Username: "jim2-bobby" + model.NewId(),
Email: MakeEmail(),
Roles: "system_user",
Roles: "system_user system_user_manager",
}
_, err = ss.User().Save(u2)
require.Nil(t, err)