Add an option to hide certain users in the UI (#28942)
* Add an option to hide certain users in the UI * revert changes for admin users routes * fix sqlstore function name * Improve slice management Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> * Hidden users: convert slice to map * filter with user logins instead of IDs * put HiddenUsers in Cfg struct * hide hidden users from dashboards/folders permissions list * Update conf/defaults.ini Co-authored-by: Torkel Ödegaard <torkel@grafana.com> * fix params order * fix tests * fix dashboard/folder update with hidden user * add team tests * add dashboard and folder permissions tests * fixes after merge * fix tests * API: add test for org users endpoints * update hidden users management for dashboard / folder permissions * improve dashboard / folder permissions tests * fixes after merge * Guardian: add hidden acl tests * API: add team members tests * fix team sql syntax for postgres * api tests update * fix linter error * fix tests errors after merge Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.com> Co-authored-by: Leonard Gram <leo@xlson.com>
This commit is contained in:
co-authored by
Emil Tullstedt
Torkel Ödegaard
Leonard Gram
parent
4c47fc56bb
commit
22788d1d86
@@ -3,6 +3,7 @@ package sqlstore
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@@ -24,26 +25,54 @@ func init() {
|
||||
bus.AddHandler("sql", IsAdminOfTeams)
|
||||
}
|
||||
|
||||
func getTeamSearchSQLBase() string {
|
||||
return `SELECT
|
||||
team.id as id,
|
||||
team.org_id,
|
||||
team.name as name,
|
||||
team.email as email,
|
||||
(SELECT COUNT(*) from team_member where team_member.team_id = team.id) as member_count,
|
||||
team_member.permission
|
||||
FROM team as team
|
||||
INNER JOIN team_member on team.id = team_member.team_id AND team_member.user_id = ? `
|
||||
func getFilteredUsers(signedInUser *models.SignedInUser, hiddenUsers map[string]struct{}) []string {
|
||||
filteredUsers := make([]string, 0, len(hiddenUsers))
|
||||
if signedInUser == nil || signedInUser.IsGrafanaAdmin {
|
||||
return filteredUsers
|
||||
}
|
||||
|
||||
for u := range hiddenUsers {
|
||||
if u == signedInUser.Login {
|
||||
continue
|
||||
}
|
||||
filteredUsers = append(filteredUsers, u)
|
||||
}
|
||||
|
||||
return filteredUsers
|
||||
}
|
||||
|
||||
func getTeamSelectSQLBase() string {
|
||||
func getTeamMemberCount(filteredUsers []string) string {
|
||||
if len(filteredUsers) > 0 {
|
||||
return `(SELECT COUNT(*) FROM team_member
|
||||
INNER JOIN ` + dialect.Quote("user") + ` ON team_member.user_id = ` + dialect.Quote("user") + `.id
|
||||
WHERE team_member.team_id = team.id AND ` + dialect.Quote("user") + `.login NOT IN (?` +
|
||||
strings.Repeat(",?", len(filteredUsers)-1) + ")" +
|
||||
`) AS member_count `
|
||||
}
|
||||
|
||||
return "(SELECT COUNT(*) FROM team_member WHERE team_member.team_id = team.id) AS member_count "
|
||||
}
|
||||
|
||||
func getTeamSearchSQLBase(filteredUsers []string) string {
|
||||
return `SELECT
|
||||
team.id AS id,
|
||||
team.org_id,
|
||||
team.name AS name,
|
||||
team.email AS email,
|
||||
team_member.permission, ` +
|
||||
getTeamMemberCount(filteredUsers) +
|
||||
` FROM team AS team
|
||||
INNER JOIN team_member ON team.id = team_member.team_id AND team_member.user_id = ? `
|
||||
}
|
||||
|
||||
func getTeamSelectSQLBase(filteredUsers []string) string {
|
||||
return `SELECT
|
||||
team.id as id,
|
||||
team.org_id,
|
||||
team.name as name,
|
||||
team.email as email,
|
||||
(SELECT COUNT(*) from team_member where team_member.team_id = team.id) as member_count
|
||||
FROM team as team `
|
||||
team.email as email, ` +
|
||||
getTeamMemberCount(filteredUsers) +
|
||||
` FROM team as team `
|
||||
}
|
||||
|
||||
func CreateTeam(cmd *models.CreateTeamCommand) error {
|
||||
@@ -157,14 +186,21 @@ func SearchTeams(query *models.SearchTeamsQuery) error {
|
||||
var sql bytes.Buffer
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
filteredUsers := getFilteredUsers(query.SignedInUser, query.HiddenUsers)
|
||||
if query.UserIdFilter > 0 {
|
||||
sql.WriteString(getTeamSearchSQLBase())
|
||||
sql.WriteString(getTeamSearchSQLBase(filteredUsers))
|
||||
for _, user := range filteredUsers {
|
||||
params = append(params, user)
|
||||
}
|
||||
params = append(params, query.UserIdFilter)
|
||||
} else {
|
||||
sql.WriteString(getTeamSelectSQLBase())
|
||||
sql.WriteString(getTeamSelectSQLBase(filteredUsers))
|
||||
for _, user := range filteredUsers {
|
||||
params = append(params, user)
|
||||
}
|
||||
}
|
||||
sql.WriteString(` WHERE team.org_id = ?`)
|
||||
|
||||
sql.WriteString(` WHERE team.org_id = ?`)
|
||||
params = append(params, query.OrgId)
|
||||
|
||||
if query.Query != "" {
|
||||
@@ -206,12 +242,19 @@ func SearchTeams(query *models.SearchTeamsQuery) error {
|
||||
|
||||
func GetTeamById(query *models.GetTeamByIdQuery) error {
|
||||
var sql bytes.Buffer
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
filteredUsers := getFilteredUsers(query.SignedInUser, query.HiddenUsers)
|
||||
sql.WriteString(getTeamSelectSQLBase(filteredUsers))
|
||||
for _, user := range filteredUsers {
|
||||
params = append(params, user)
|
||||
}
|
||||
|
||||
sql.WriteString(getTeamSelectSQLBase())
|
||||
sql.WriteString(` WHERE team.org_id = ? and team.id = ?`)
|
||||
params = append(params, query.OrgId, query.Id)
|
||||
|
||||
var team models.TeamDTO
|
||||
exists, err := x.SQL(sql.String(), query.OrgId, query.Id).Get(&team)
|
||||
exists, err := x.SQL(sql.String(), params...).Get(&team)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -231,7 +274,7 @@ func GetTeamsByUser(query *models.GetTeamsByUserQuery) error {
|
||||
|
||||
var sql bytes.Buffer
|
||||
|
||||
sql.WriteString(getTeamSelectSQLBase())
|
||||
sql.WriteString(getTeamSelectSQLBase([]string{}))
|
||||
sql.WriteString(` INNER JOIN team_member on team.id = team_member.team_id`)
|
||||
sql.WriteString(` WHERE team.org_id = ? and team_member.user_id = ?`)
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
So(team1.Name, ShouldEqual, "group1 name")
|
||||
So(team1.Email, ShouldEqual, "test1@test.com")
|
||||
So(team1.OrgId, ShouldEqual, testOrgId)
|
||||
So(team1.MemberCount, ShouldEqual, 0)
|
||||
|
||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: team1.Id, UserId: userIds[0]})
|
||||
So(err, ShouldBeNil)
|
||||
@@ -74,6 +75,20 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
So(q2.Result[0].Login, ShouldEqual, "loginuser1")
|
||||
So(q2.Result[0].OrgId, ShouldEqual, testOrgId)
|
||||
So(q2.Result[0].External, ShouldEqual, true)
|
||||
|
||||
err = SearchTeams(query)
|
||||
So(err, ShouldBeNil)
|
||||
team1 = query.Result.Teams[0]
|
||||
So(team1.MemberCount, ShouldEqual, 2)
|
||||
|
||||
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: team1.Id}
|
||||
err = GetTeamById(getTeamQuery)
|
||||
So(err, ShouldBeNil)
|
||||
team1 = getTeamQuery.Result
|
||||
So(team1.Name, ShouldEqual, "group1 name")
|
||||
So(team1.Email, ShouldEqual, "test1@test.com")
|
||||
So(team1.OrgId, ShouldEqual, testOrgId)
|
||||
So(team1.MemberCount, ShouldEqual, 2)
|
||||
})
|
||||
|
||||
Convey("Should return latest auth module for users when getting team members", func() {
|
||||
@@ -275,6 +290,38 @@ func TestTeamCommandsAndQueries(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(query.Result, ShouldBeTrue)
|
||||
})
|
||||
|
||||
Convey("Should not return hidden users in team member count", func() {
|
||||
signedInUser := &models.SignedInUser{Login: "loginuser0"}
|
||||
hiddenUsers := map[string]struct{}{"loginuser0": {}, "loginuser1": {}}
|
||||
|
||||
teamId := group1.Result.Id
|
||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[0]})
|
||||
So(err, ShouldBeNil)
|
||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[1]})
|
||||
So(err, ShouldBeNil)
|
||||
err = AddTeamMember(&models.AddTeamMemberCommand{OrgId: testOrgId, TeamId: teamId, UserId: userIds[2]})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
searchQuery := &models.SearchTeamsQuery{OrgId: testOrgId, Page: 1, Limit: 10, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||
err = SearchTeams(searchQuery)
|
||||
So(err, ShouldBeNil)
|
||||
So(searchQuery.Result.Teams, ShouldHaveLength, 2)
|
||||
team1 := searchQuery.Result.Teams[0]
|
||||
So(team1.MemberCount, ShouldEqual, 2)
|
||||
|
||||
searchQueryFilteredByUser := &models.SearchTeamsQuery{OrgId: testOrgId, Page: 1, Limit: 10, UserIdFilter: userIds[0], SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||
err = SearchTeams(searchQueryFilteredByUser)
|
||||
So(err, ShouldBeNil)
|
||||
So(searchQueryFilteredByUser.Result.Teams, ShouldHaveLength, 1)
|
||||
team1 = searchQuery.Result.Teams[0]
|
||||
So(team1.MemberCount, ShouldEqual, 2)
|
||||
|
||||
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgId, Id: teamId, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||
err = GetTeamById(getTeamQuery)
|
||||
So(err, ShouldBeNil)
|
||||
So(getTeamQuery.Result.MemberCount, ShouldEqual, 2)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user