Chore: Move team store implementation to a separate package (#55514)
* Chore: move team store implementation to a separate package * trying to fix more tests * fix tests in service accounts and access control * fix common tests * restore commented out test * add todos
This commit is contained in:
@@ -214,15 +214,6 @@ func createDummyUser(t *testing.T, sqlStore *SQLStore) *user.User {
|
||||
return user
|
||||
}
|
||||
|
||||
func createDummyTeam(t *testing.T, sqlStore *SQLStore) models.Team {
|
||||
t.Helper()
|
||||
|
||||
team, err := sqlStore.CreateTeam("test", "test@example.com", 1)
|
||||
require.NoError(t, err)
|
||||
|
||||
return team
|
||||
}
|
||||
|
||||
func createDummyDashboard(t *testing.T, sqlStore *SQLStore, dashboardProps DashboardProps) *models.Dashboard {
|
||||
t.Helper()
|
||||
|
||||
@@ -273,16 +264,8 @@ func createDummyACL(t *testing.T, sqlStore *SQLStore, dashboardPermission *Dashb
|
||||
}
|
||||
|
||||
if dashboardPermission.Team {
|
||||
t.Logf("Creating team")
|
||||
team := createDummyTeam(t, sqlStore)
|
||||
if search.UserFromACL {
|
||||
user = createDummyUser(t, sqlStore)
|
||||
err := sqlStore.AddTeamMember(user.ID, 1, team.Id, false, 0)
|
||||
require.NoError(t, err)
|
||||
t.Logf("Created team member with ID %d", user.ID)
|
||||
}
|
||||
|
||||
acl.TeamID = team.Id
|
||||
// TODO: Restore/refactor sqlBuilder tests after user, org and team services are split
|
||||
t.Skip("Creating team: skip, team service is moved")
|
||||
}
|
||||
|
||||
if len(string(dashboardPermission.Role)) > 0 {
|
||||
|
||||
@@ -1,579 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
type TeamStore interface {
|
||||
UpdateTeam(ctx context.Context, cmd *models.UpdateTeamCommand) error
|
||||
DeleteTeam(ctx context.Context, cmd *models.DeleteTeamCommand) error
|
||||
SearchTeams(ctx context.Context, query *models.SearchTeamsQuery) error
|
||||
GetTeamById(ctx context.Context, query *models.GetTeamByIdQuery) error
|
||||
UpdateTeamMember(ctx context.Context, cmd *models.UpdateTeamMemberCommand) error
|
||||
RemoveTeamMember(ctx context.Context, cmd *models.RemoveTeamMemberCommand) error
|
||||
GetTeamMembers(ctx context.Context, cmd *models.GetTeamMembersQuery) error
|
||||
GetUserTeamMemberships(ctx context.Context, orgID, userID int64, external bool) ([]*models.TeamMemberDTO, error)
|
||||
}
|
||||
|
||||
func getFilteredUsers(signedInUser *user.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 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 getTeamSelectSQLBase(filteredUsers []string) string {
|
||||
return `SELECT
|
||||
team.id as id,
|
||||
team.org_id,
|
||||
team.name as name,
|
||||
team.email as email, ` +
|
||||
getTeamMemberCount(filteredUsers) +
|
||||
` FROM team as team `
|
||||
}
|
||||
|
||||
func getTeamSelectWithPermissionsSQLBase(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 (ss *SQLStore) CreateTeam(name, email string, orgID int64) (models.Team, error) {
|
||||
team := models.Team{
|
||||
Name: name,
|
||||
Email: email,
|
||||
OrgId: orgID,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
if isNameTaken, err := isTeamNameTaken(orgID, name, 0, sess); err != nil {
|
||||
return err
|
||||
} else if isNameTaken {
|
||||
return models.ErrTeamNameTaken
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&team)
|
||||
return err
|
||||
})
|
||||
return team, err
|
||||
}
|
||||
|
||||
func (ss *SQLStore) UpdateTeam(ctx context.Context, cmd *models.UpdateTeamCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
if isNameTaken, err := isTeamNameTaken(cmd.OrgId, cmd.Name, cmd.Id, sess); err != nil {
|
||||
return err
|
||||
} else if isNameTaken {
|
||||
return models.ErrTeamNameTaken
|
||||
}
|
||||
|
||||
team := models.Team{
|
||||
Name: cmd.Name,
|
||||
Email: cmd.Email,
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
sess.MustCols("email")
|
||||
|
||||
affectedRows, err := sess.ID(cmd.Id).Update(&team)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if affectedRows == 0 {
|
||||
return models.ErrTeamNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteTeam will delete a team, its member and any permissions connected to the team
|
||||
func (ss *SQLStore) DeleteTeam(ctx context.Context, cmd *models.DeleteTeamCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
if _, err := teamExists(cmd.OrgId, cmd.Id, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deletes := []string{
|
||||
"DELETE FROM team_member WHERE org_id=? and team_id = ?",
|
||||
"DELETE FROM team WHERE org_id=? and id = ?",
|
||||
"DELETE FROM dashboard_acl WHERE org_id=? and team_id = ?",
|
||||
"DELETE FROM team_role WHERE org_id=? and team_id = ?",
|
||||
}
|
||||
|
||||
for _, sql := range deletes {
|
||||
_, err := sess.Exec(sql, cmd.OrgId, cmd.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := sess.Exec("DELETE FROM permission WHERE scope=?", ac.Scope("teams", "id", fmt.Sprint(cmd.Id)))
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func teamExists(orgID int64, teamID int64, sess *DBSession) (bool, error) {
|
||||
if res, err := sess.Query("SELECT 1 from team WHERE org_id=? and id=?", orgID, teamID); err != nil {
|
||||
return false, err
|
||||
} else if len(res) != 1 {
|
||||
return false, models.ErrTeamNotFound
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func isTeamNameTaken(orgId int64, name string, existingId int64, sess *DBSession) (bool, error) {
|
||||
var team models.Team
|
||||
exists, err := sess.Where("org_id=? and name=?", orgId, name).Get(&team)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if exists && existingId != team.Id {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (ss *SQLStore) SearchTeams(ctx context.Context, query *models.SearchTeamsQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
query.Result = models.SearchTeamQueryResult{
|
||||
Teams: make([]*models.TeamDTO, 0),
|
||||
}
|
||||
queryWithWildcards := "%" + query.Query + "%"
|
||||
|
||||
var sql bytes.Buffer
|
||||
params := make([]interface{}, 0)
|
||||
|
||||
filteredUsers := getFilteredUsers(query.SignedInUser, query.HiddenUsers)
|
||||
for _, user := range filteredUsers {
|
||||
params = append(params, user)
|
||||
}
|
||||
|
||||
if query.UserIdFilter == models.FilterIgnoreUser {
|
||||
sql.WriteString(getTeamSelectSQLBase(filteredUsers))
|
||||
} else {
|
||||
sql.WriteString(getTeamSelectWithPermissionsSQLBase(filteredUsers))
|
||||
params = append(params, query.UserIdFilter)
|
||||
}
|
||||
|
||||
sql.WriteString(` WHERE team.org_id = ?`)
|
||||
params = append(params, query.OrgId)
|
||||
|
||||
if query.Query != "" {
|
||||
sql.WriteString(` and team.name ` + ss.Dialect.LikeStr() + ` ?`)
|
||||
params = append(params, queryWithWildcards)
|
||||
}
|
||||
|
||||
if query.Name != "" {
|
||||
sql.WriteString(` and team.name = ?`)
|
||||
params = append(params, query.Name)
|
||||
}
|
||||
|
||||
var (
|
||||
acFilter ac.SQLFilter
|
||||
err error
|
||||
)
|
||||
if !ac.IsDisabled(ss.Cfg) {
|
||||
acFilter, err = ac.Filter(query.SignedInUser, "team.id", "teams:id:", ac.ActionTeamsRead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sql.WriteString(` and` + acFilter.Where)
|
||||
params = append(params, acFilter.Args...)
|
||||
}
|
||||
|
||||
sql.WriteString(` order by team.name asc`)
|
||||
|
||||
if query.Limit != 0 {
|
||||
offset := query.Limit * (query.Page - 1)
|
||||
sql.WriteString(ss.Dialect.LimitOffset(int64(query.Limit), int64(offset)))
|
||||
}
|
||||
|
||||
if err := sess.SQL(sql.String(), params...).Find(&query.Result.Teams); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
team := models.Team{}
|
||||
countSess := sess.Table("team")
|
||||
countSess.Where("team.org_id=?", query.OrgId)
|
||||
|
||||
if query.Query != "" {
|
||||
countSess.Where(`name `+dialect.LikeStr()+` ?`, queryWithWildcards)
|
||||
}
|
||||
|
||||
if query.Name != "" {
|
||||
countSess.Where("name=?", query.Name)
|
||||
}
|
||||
|
||||
// If we're not retrieving all results, then only search for teams that this user has access to
|
||||
if query.UserIdFilter != models.FilterIgnoreUser {
|
||||
countSess.
|
||||
Where(`
|
||||
team.id IN (
|
||||
SELECT
|
||||
team_id
|
||||
FROM team_member
|
||||
WHERE team_member.user_id = ?
|
||||
)`, query.UserIdFilter)
|
||||
}
|
||||
|
||||
// Only count teams user can see
|
||||
if !ac.IsDisabled(ss.Cfg) {
|
||||
countSess.Where(acFilter.Where, acFilter.Args...)
|
||||
}
|
||||
|
||||
count, err := countSess.Count(&team)
|
||||
query.Result.TotalCount = count
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) GetTeamById(ctx context.Context, query *models.GetTeamByIdQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) 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)
|
||||
}
|
||||
|
||||
if query.UserIdFilter != models.FilterIgnoreUser {
|
||||
sql.WriteString(` INNER JOIN team_member ON team.id = team_member.team_id AND team_member.user_id = ?`)
|
||||
params = append(params, query.UserIdFilter)
|
||||
}
|
||||
|
||||
sql.WriteString(` WHERE team.org_id = ? and team.id = ?`)
|
||||
params = append(params, query.OrgId, query.Id)
|
||||
|
||||
var team models.TeamDTO
|
||||
exists, err := sess.SQL(sql.String(), params...).Get(&team)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return models.ErrTeamNotFound
|
||||
}
|
||||
|
||||
query.Result = &team
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetTeamsByUser is used by the Guardian when checking a users' permissions
|
||||
func (ss *SQLStore) GetTeamsByUser(ctx context.Context, query *models.GetTeamsByUserQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
query.Result = make([]*models.TeamDTO, 0)
|
||||
|
||||
var sql bytes.Buffer
|
||||
var params []interface{}
|
||||
params = append(params, query.OrgId, query.UserId)
|
||||
|
||||
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 = ?`)
|
||||
|
||||
if !ac.IsDisabled(ss.Cfg) {
|
||||
acFilter, err := ac.Filter(query.SignedInUser, "team.id", "teams:id:", ac.ActionTeamsRead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sql.WriteString(` and` + acFilter.Where)
|
||||
params = append(params, acFilter.Args...)
|
||||
}
|
||||
|
||||
err := sess.SQL(sql.String(), params...).Find(&query.Result)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// AddTeamMember adds a user to a team
|
||||
func (ss *SQLStore) AddTeamMember(userID, orgID, teamID int64, isExternal bool, permission models.PermissionType) error {
|
||||
return ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
if isMember, err := isTeamMember(sess, orgID, teamID, userID); err != nil {
|
||||
return err
|
||||
} else if isMember {
|
||||
return models.ErrTeamMemberAlreadyAdded
|
||||
}
|
||||
|
||||
return addTeamMember(sess, orgID, teamID, userID, isExternal, permission)
|
||||
})
|
||||
}
|
||||
|
||||
func getTeamMember(sess *DBSession, orgId int64, teamId int64, userId int64) (models.TeamMember, error) {
|
||||
rawSQL := `SELECT * FROM team_member WHERE org_id=? and team_id=? and user_id=?`
|
||||
var member models.TeamMember
|
||||
exists, err := sess.SQL(rawSQL, orgId, teamId, userId).Get(&member)
|
||||
|
||||
if err != nil {
|
||||
return member, err
|
||||
}
|
||||
if !exists {
|
||||
return member, models.ErrTeamMemberNotFound
|
||||
}
|
||||
|
||||
return member, nil
|
||||
}
|
||||
|
||||
// UpdateTeamMember updates a team member
|
||||
func (ss *SQLStore) UpdateTeamMember(ctx context.Context, cmd *models.UpdateTeamMemberCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
return updateTeamMember(sess, cmd.OrgId, cmd.TeamId, cmd.UserId, cmd.Permission)
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) IsTeamMember(orgId int64, teamId int64, userId int64) (bool, error) {
|
||||
var isMember bool
|
||||
|
||||
err := ss.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
||||
var err error
|
||||
isMember, err = isTeamMember(sess, orgId, teamId, userId)
|
||||
return err
|
||||
})
|
||||
|
||||
return isMember, err
|
||||
}
|
||||
|
||||
func isTeamMember(sess *DBSession, orgId int64, teamId int64, userId int64) (bool, error) {
|
||||
if res, err := sess.Query("SELECT 1 FROM team_member WHERE org_id=? and team_id=? and user_id=?", orgId, teamId, userId); err != nil {
|
||||
return false, err
|
||||
} else if len(res) != 1 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// AddOrUpdateTeamMemberHook is called from team resource permission service
|
||||
// it adds user to a team or updates user permissions in a team within the given transaction session
|
||||
func AddOrUpdateTeamMemberHook(sess *DBSession, userID, orgID, teamID int64, isExternal bool, permission models.PermissionType) error {
|
||||
isMember, err := isTeamMember(sess, orgID, teamID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isMember {
|
||||
err = updateTeamMember(sess, orgID, teamID, userID, permission)
|
||||
} else {
|
||||
err = addTeamMember(sess, orgID, teamID, userID, isExternal, permission)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func addTeamMember(sess *DBSession, orgID, teamID, userID int64, isExternal bool, permission models.PermissionType) error {
|
||||
if _, err := teamExists(orgID, teamID, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entity := models.TeamMember{
|
||||
OrgId: orgID,
|
||||
TeamId: teamID,
|
||||
UserId: userID,
|
||||
External: isExternal,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
Permission: permission,
|
||||
}
|
||||
|
||||
_, err := sess.Insert(&entity)
|
||||
return err
|
||||
}
|
||||
|
||||
func updateTeamMember(sess *DBSession, orgID, teamID, userID int64, permission models.PermissionType) error {
|
||||
member, err := getTeamMember(sess, orgID, teamID, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if permission != models.PERMISSION_ADMIN {
|
||||
permission = 0 // make sure we don't get invalid permission levels in store
|
||||
}
|
||||
|
||||
member.Permission = permission
|
||||
_, err = sess.Cols("permission").Where("org_id=? and team_id=? and user_id=?", orgID, teamID, userID).Update(member)
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveTeamMember removes a member from a team
|
||||
func (ss *SQLStore) RemoveTeamMember(ctx context.Context, cmd *models.RemoveTeamMemberCommand) error {
|
||||
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
||||
return removeTeamMember(sess, cmd)
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveTeamMemberHook is called from team resource permission service
|
||||
// it removes a member from a team within the given transaction session
|
||||
func RemoveTeamMemberHook(sess *DBSession, cmd *models.RemoveTeamMemberCommand) error {
|
||||
return removeTeamMember(sess, cmd)
|
||||
}
|
||||
|
||||
func removeTeamMember(sess *DBSession, cmd *models.RemoveTeamMemberCommand) error {
|
||||
if _, err := teamExists(cmd.OrgId, cmd.TeamId, sess); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rawSQL = "DELETE FROM team_member WHERE org_id=? and team_id=? and user_id=?"
|
||||
res, err := sess.Exec(rawSQL, cmd.OrgId, cmd.TeamId, cmd.UserId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rows, err := res.RowsAffected()
|
||||
if rows == 0 {
|
||||
return models.ErrTeamMemberNotFound
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// GetUserTeamMemberships return a list of memberships to teams granted to a user
|
||||
// If external is specified, only memberships provided by an external auth provider will be listed
|
||||
// This function doesn't perform any accesscontrol filtering.
|
||||
func (ss *SQLStore) GetUserTeamMemberships(ctx context.Context, orgID, userID int64, external bool) ([]*models.TeamMemberDTO, error) {
|
||||
query := &models.GetTeamMembersQuery{
|
||||
OrgId: orgID,
|
||||
UserId: userID,
|
||||
External: external,
|
||||
Result: []*models.TeamMemberDTO{},
|
||||
}
|
||||
err := ss.getTeamMembers(ctx, query, nil)
|
||||
return query.Result, err
|
||||
}
|
||||
|
||||
// GetTeamMembers return a list of members for the specified team filtered based on the user's permissions
|
||||
func (ss *SQLStore) GetTeamMembers(ctx context.Context, query *models.GetTeamMembersQuery) error {
|
||||
acFilter := &ac.SQLFilter{}
|
||||
var err error
|
||||
|
||||
// With accesscontrol we filter out users based on the SignedInUser's permissions
|
||||
// Note we assume that checking SignedInUser is allowed to see team members for this team has already been performed
|
||||
// If the signed in user is not set no member will be returned
|
||||
if !ac.IsDisabled(ss.Cfg) {
|
||||
sqlID := fmt.Sprintf("%s.%s", ss.engine.Dialect().Quote("user"), ss.engine.Dialect().Quote("id"))
|
||||
*acFilter, err = ac.Filter(query.SignedInUser, sqlID, "users:id:", ac.ActionOrgUsersRead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ss.getTeamMembers(ctx, query, acFilter)
|
||||
}
|
||||
|
||||
// getTeamMembers return a list of members for the specified team
|
||||
func (ss *SQLStore) getTeamMembers(ctx context.Context, query *models.GetTeamMembersQuery, acUserFilter *ac.SQLFilter) error {
|
||||
return ss.WithDbSession(ctx, func(dbSess *DBSession) error {
|
||||
query.Result = make([]*models.TeamMemberDTO, 0)
|
||||
sess := dbSess.Table("team_member")
|
||||
sess.Join("INNER", ss.Dialect.Quote("user"),
|
||||
fmt.Sprintf("team_member.user_id=%s.%s", ss.Dialect.Quote("user"), ss.Dialect.Quote("id")),
|
||||
)
|
||||
|
||||
// explicitly check for serviceaccounts
|
||||
sess.Where(fmt.Sprintf("%s.is_service_account=?", ss.Dialect.Quote("user")), ss.Dialect.BooleanStr(false))
|
||||
|
||||
if acUserFilter != nil {
|
||||
sess.Where(acUserFilter.Where, acUserFilter.Args...)
|
||||
}
|
||||
|
||||
// Join with only most recent auth module
|
||||
authJoinCondition := `(
|
||||
SELECT id from user_auth
|
||||
WHERE user_auth.user_id = team_member.user_id
|
||||
ORDER BY user_auth.created DESC `
|
||||
authJoinCondition = "user_auth.id=" + authJoinCondition + ss.Dialect.Limit(1) + ")"
|
||||
sess.Join("LEFT", "user_auth", authJoinCondition)
|
||||
|
||||
if query.OrgId != 0 {
|
||||
sess.Where("team_member.org_id=?", query.OrgId)
|
||||
}
|
||||
if query.TeamId != 0 {
|
||||
sess.Where("team_member.team_id=?", query.TeamId)
|
||||
}
|
||||
if query.UserId != 0 {
|
||||
sess.Where("team_member.user_id=?", query.UserId)
|
||||
}
|
||||
if query.External {
|
||||
sess.Where("team_member.external=?", ss.Dialect.BooleanStr(true))
|
||||
}
|
||||
sess.Cols(
|
||||
"team_member.org_id",
|
||||
"team_member.team_id",
|
||||
"team_member.user_id",
|
||||
"user.email",
|
||||
"user.name",
|
||||
"user.login",
|
||||
"team_member.external",
|
||||
"team_member.permission",
|
||||
"user_auth.auth_module",
|
||||
)
|
||||
sess.Asc("user.login", "user.email")
|
||||
|
||||
err := sess.Find(&query.Result)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (ss *SQLStore) IsAdminOfTeams(ctx context.Context, query *models.IsAdminOfTeamsQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
builder := &SQLBuilder{}
|
||||
builder.Write("SELECT COUNT(team.id) AS count FROM team INNER JOIN team_member ON team_member.team_id = team.id WHERE team.org_id = ? AND team_member.user_id = ? AND team_member.permission = ?", query.SignedInUser.OrgID, query.SignedInUser.UserID, models.PERMISSION_ADMIN)
|
||||
|
||||
type teamCount struct {
|
||||
Count int64
|
||||
}
|
||||
|
||||
resp := make([]*teamCount, 0)
|
||||
if err := sess.SQL(builder.GetSQLString(), builder.params...).Find(&resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
query.Result = len(resp) > 0 && resp[0].Count > 0
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -1,580 +0,0 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/serviceaccounts"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
func TestIntegrationTeamCommandsAndQueries(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
t.Run("Testing Team commands & queries", func(t *testing.T) {
|
||||
sqlStore := InitTestDB(t)
|
||||
testUser := &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
1: {
|
||||
ac.ActionTeamsRead: []string{ac.ScopeTeamsAll},
|
||||
ac.ActionOrgUsersRead: []string{ac.ScopeUsersAll},
|
||||
serviceaccounts.ActionRead: []string{serviceaccounts.ScopeAll},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Given saved users and two teams", func(t *testing.T) {
|
||||
var userIds []int64
|
||||
const testOrgID int64 = 1
|
||||
var team1, team2 models.Team
|
||||
var usr *user.User
|
||||
var userCmd user.CreateUserCommand
|
||||
var err error
|
||||
|
||||
setup := func() {
|
||||
for i := 0; i < 5; i++ {
|
||||
userCmd = user.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
Login: fmt.Sprint("loginuser", i),
|
||||
}
|
||||
usr, err = sqlStore.CreateUser(context.Background(), userCmd)
|
||||
require.NoError(t, err)
|
||||
userIds = append(userIds, usr.ID)
|
||||
}
|
||||
team1, err = sqlStore.CreateTeam("group1 name", "test1@test.com", testOrgID)
|
||||
require.NoError(t, err)
|
||||
team2, err = sqlStore.CreateTeam("group2 name", "test2@test.com", testOrgID)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
setup()
|
||||
|
||||
t.Run("Should be able to create teams and add users", func(t *testing.T) {
|
||||
query := &models.SearchTeamsQuery{OrgId: testOrgID, Name: "group1 name", Page: 1, Limit: 10, SignedInUser: testUser}
|
||||
err = sqlStore.SearchTeams(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, query.Page, 1)
|
||||
|
||||
team1 := query.Result.Teams[0]
|
||||
require.Equal(t, team1.Name, "group1 name")
|
||||
require.Equal(t, team1.Email, "test1@test.com")
|
||||
require.Equal(t, team1.OrgId, testOrgID)
|
||||
require.EqualValues(t, team1.MemberCount, 0)
|
||||
|
||||
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, 0)
|
||||
require.NoError(t, err)
|
||||
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, true, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
q1 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), q1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(q1.Result), 2)
|
||||
require.Equal(t, q1.Result[0].TeamId, team1.Id)
|
||||
require.Equal(t, q1.Result[0].Login, "loginuser0")
|
||||
require.Equal(t, q1.Result[0].OrgId, testOrgID)
|
||||
require.Equal(t, q1.Result[1].TeamId, team1.Id)
|
||||
require.Equal(t, q1.Result[1].Login, "loginuser1")
|
||||
require.Equal(t, q1.Result[1].OrgId, testOrgID)
|
||||
require.Equal(t, q1.Result[1].External, true)
|
||||
|
||||
q2 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, External: true, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), q2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(q2.Result), 1)
|
||||
require.Equal(t, q2.Result[0].TeamId, team1.Id)
|
||||
require.Equal(t, q2.Result[0].Login, "loginuser1")
|
||||
require.Equal(t, q2.Result[0].OrgId, testOrgID)
|
||||
require.Equal(t, q2.Result[0].External, true)
|
||||
|
||||
err = sqlStore.SearchTeams(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
team1 = query.Result.Teams[0]
|
||||
require.EqualValues(t, team1.MemberCount, 2)
|
||||
|
||||
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: team1.Id, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamById(context.Background(), getTeamQuery)
|
||||
require.NoError(t, err)
|
||||
team1 = getTeamQuery.Result
|
||||
require.Equal(t, team1.Name, "group1 name")
|
||||
require.Equal(t, team1.Email, "test1@test.com")
|
||||
require.Equal(t, team1.OrgId, testOrgID)
|
||||
require.EqualValues(t, team1.MemberCount, 2)
|
||||
})
|
||||
|
||||
t.Run("Should return latest auth module for users when getting team members", func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
setup()
|
||||
userId := userIds[1]
|
||||
|
||||
teamQuery := &models.SearchTeamsQuery{OrgId: testOrgID, Name: "group1 name", Page: 1, Limit: 10, SignedInUser: testUser}
|
||||
err = sqlStore.SearchTeams(context.Background(), teamQuery)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, teamQuery.Page, 1)
|
||||
|
||||
team1 := teamQuery.Result.Teams[0]
|
||||
|
||||
err = sqlStore.AddTeamMember(userId, testOrgID, team1.Id, true, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
memberQuery := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, External: true, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), memberQuery)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(memberQuery.Result), 1)
|
||||
require.Equal(t, memberQuery.Result[0].TeamId, team1.Id)
|
||||
require.Equal(t, memberQuery.Result[0].Login, "loginuser1")
|
||||
require.Equal(t, memberQuery.Result[0].OrgId, testOrgID)
|
||||
require.Equal(t, memberQuery.Result[0].External, true)
|
||||
})
|
||||
|
||||
t.Run("Should be able to update users in a team", func(t *testing.T) {
|
||||
userId := userIds[0]
|
||||
team := team1
|
||||
err = sqlStore.AddTeamMember(userId, testOrgID, team.Id, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), qBeforeUpdate)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, qBeforeUpdate.Result[0].Permission, 0)
|
||||
|
||||
err = sqlStore.UpdateTeamMember(context.Background(), &models.UpdateTeamMemberCommand{
|
||||
UserId: userId,
|
||||
OrgId: testOrgID,
|
||||
TeamId: team.Id,
|
||||
Permission: models.PERMISSION_ADMIN,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), qAfterUpdate)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, qAfterUpdate.Result[0].Permission, models.PERMISSION_ADMIN)
|
||||
})
|
||||
|
||||
t.Run("Should default to member permission level when updating a user with invalid permission level", func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
setup()
|
||||
userID := userIds[0]
|
||||
team := team1
|
||||
err = sqlStore.AddTeamMember(userID, testOrgID, team.Id, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
qBeforeUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), qBeforeUpdate)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, qBeforeUpdate.Result[0].Permission, 0)
|
||||
|
||||
invalidPermissionLevel := models.PERMISSION_EDIT
|
||||
err = sqlStore.UpdateTeamMember(context.Background(), &models.UpdateTeamMemberCommand{
|
||||
UserId: userID,
|
||||
OrgId: testOrgID,
|
||||
TeamId: team.Id,
|
||||
Permission: invalidPermissionLevel,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
qAfterUpdate := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team.Id, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), qAfterUpdate)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, qAfterUpdate.Result[0].Permission, 0)
|
||||
})
|
||||
|
||||
t.Run("Shouldn't be able to update a user not in the team.", func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
setup()
|
||||
err = sqlStore.UpdateTeamMember(context.Background(), &models.UpdateTeamMemberCommand{
|
||||
UserId: 1,
|
||||
OrgId: testOrgID,
|
||||
TeamId: team1.Id,
|
||||
Permission: models.PERMISSION_ADMIN,
|
||||
})
|
||||
|
||||
require.Error(t, err, models.ErrTeamMemberNotFound)
|
||||
})
|
||||
|
||||
t.Run("Should be able to search for teams", func(t *testing.T) {
|
||||
query := &models.SearchTeamsQuery{OrgId: testOrgID, Query: "group", Page: 1, SignedInUser: testUser}
|
||||
err = sqlStore.SearchTeams(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(query.Result.Teams), 2)
|
||||
require.EqualValues(t, query.Result.TotalCount, 2)
|
||||
|
||||
query2 := &models.SearchTeamsQuery{OrgId: testOrgID, Query: "", SignedInUser: testUser}
|
||||
err = sqlStore.SearchTeams(context.Background(), query2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(query2.Result.Teams), 2)
|
||||
})
|
||||
|
||||
t.Run("Should be able to return all teams a user is member of", func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
setup()
|
||||
groupId := team2.Id
|
||||
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
query := &models.GetTeamsByUserQuery{
|
||||
OrgId: testOrgID,
|
||||
UserId: userIds[0],
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: testOrgID,
|
||||
Permissions: map[int64]map[string][]string{testOrgID: {ac.ActionOrgUsersRead: {ac.ScopeUsersAll}, ac.ActionTeamsRead: {ac.ScopeTeamsAll}}},
|
||||
},
|
||||
}
|
||||
err = sqlStore.GetTeamsByUser(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(query.Result), 1)
|
||||
require.Equal(t, query.Result[0].Name, "group2 name")
|
||||
require.Equal(t, query.Result[0].Email, "test2@test.com")
|
||||
})
|
||||
|
||||
t.Run("Should be able to remove users from a group", func(t *testing.T) {
|
||||
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = sqlStore.RemoveTeamMember(context.Background(), &models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0]})
|
||||
require.NoError(t, err)
|
||||
|
||||
q2 := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: team1.Id, SignedInUser: testUser}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), q2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(q2.Result), 0)
|
||||
})
|
||||
|
||||
t.Run("Should have empty teams", func(t *testing.T) {
|
||||
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("A user should be able to remove the admin permission for the last admin", func(t *testing.T) {
|
||||
err = sqlStore.UpdateTeamMember(context.Background(), &models.UpdateTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], Permission: 0})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("A user should be able to remove the last member", func(t *testing.T) {
|
||||
err = sqlStore.RemoveTeamMember(context.Background(), &models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0]})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("A user should be able to remove the admin permission if there are other admins", func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
setup()
|
||||
|
||||
err = sqlStore.AddTeamMember(userIds[0], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = sqlStore.AddTeamMember(userIds[1], testOrgID, team1.Id, false, models.PERMISSION_ADMIN)
|
||||
require.NoError(t, err)
|
||||
err = sqlStore.UpdateTeamMember(context.Background(), &models.UpdateTeamMemberCommand{OrgId: testOrgID, TeamId: team1.Id, UserId: userIds[0], Permission: 0})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Should be able to remove a group with users and permissions", func(t *testing.T) {
|
||||
groupId := team2.Id
|
||||
err := sqlStore.AddTeamMember(userIds[1], testOrgID, groupId, false, 0)
|
||||
require.NoError(t, err)
|
||||
err = sqlStore.AddTeamMember(userIds[2], testOrgID, groupId, false, 0)
|
||||
require.NoError(t, err)
|
||||
err = updateDashboardACL(t, sqlStore, 1, &models.DashboardACL{
|
||||
DashboardID: 1, OrgID: testOrgID, Permission: models.PERMISSION_EDIT, TeamID: groupId,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = sqlStore.DeleteTeam(context.Background(), &models.DeleteTeamCommand{OrgId: testOrgID, Id: groupId})
|
||||
require.NoError(t, err)
|
||||
|
||||
query := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: groupId}
|
||||
err = sqlStore.GetTeamById(context.Background(), query)
|
||||
require.Equal(t, err, models.ErrTeamNotFound)
|
||||
|
||||
permQuery := &models.GetDashboardACLInfoListQuery{DashboardID: 1, OrgID: testOrgID}
|
||||
err = getDashboardACLInfoList(sqlStore, permQuery)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(permQuery.Result), 0)
|
||||
})
|
||||
|
||||
t.Run("Should be able to return if user is admin of teams or not", func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
setup()
|
||||
groupId := team2.Id
|
||||
err := sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
|
||||
require.NoError(t, err)
|
||||
err = sqlStore.AddTeamMember(userIds[1], testOrgID, groupId, false, models.PERMISSION_ADMIN)
|
||||
require.NoError(t, err)
|
||||
|
||||
query := &models.IsAdminOfTeamsQuery{SignedInUser: &user.SignedInUser{OrgID: testOrgID, UserID: userIds[0]}}
|
||||
err = sqlStore.IsAdminOfTeams(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.False(t, query.Result)
|
||||
|
||||
query = &models.IsAdminOfTeamsQuery{SignedInUser: &user.SignedInUser{OrgID: testOrgID, UserID: userIds[1]}}
|
||||
err = sqlStore.IsAdminOfTeams(context.Background(), query)
|
||||
require.NoError(t, err)
|
||||
require.True(t, query.Result)
|
||||
})
|
||||
|
||||
t.Run("Should not return hidden users in team member count", func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
setup()
|
||||
signedInUser := &user.SignedInUser{
|
||||
Login: "loginuser0",
|
||||
OrgID: testOrgID,
|
||||
Permissions: map[int64]map[string][]string{
|
||||
testOrgID: {
|
||||
ac.ActionTeamsRead: []string{ac.ScopeTeamsAll},
|
||||
ac.ActionOrgUsersRead: []string{ac.ScopeUsersAll},
|
||||
},
|
||||
},
|
||||
}
|
||||
hiddenUsers := map[string]struct{}{"loginuser0": {}, "loginuser1": {}}
|
||||
|
||||
teamId := team1.Id
|
||||
err = sqlStore.AddTeamMember(userIds[0], testOrgID, teamId, false, 0)
|
||||
require.NoError(t, err)
|
||||
err = sqlStore.AddTeamMember(userIds[1], testOrgID, teamId, false, 0)
|
||||
require.NoError(t, err)
|
||||
err = sqlStore.AddTeamMember(userIds[2], testOrgID, teamId, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
searchQuery := &models.SearchTeamsQuery{OrgId: testOrgID, Page: 1, Limit: 10, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||
err = sqlStore.SearchTeams(context.Background(), searchQuery)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(searchQuery.Result.Teams), 2)
|
||||
team1 := searchQuery.Result.Teams[0]
|
||||
require.EqualValues(t, team1.MemberCount, 2)
|
||||
|
||||
searchQueryFilteredByUser := &models.SearchTeamsQuery{OrgId: testOrgID, Page: 1, Limit: 10, UserIdFilter: userIds[0], SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||
err = sqlStore.SearchTeams(context.Background(), searchQueryFilteredByUser)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(searchQueryFilteredByUser.Result.Teams), 1)
|
||||
team1 = searchQuery.Result.Teams[0]
|
||||
require.EqualValues(t, team1.MemberCount, 2)
|
||||
|
||||
getTeamQuery := &models.GetTeamByIdQuery{OrgId: testOrgID, Id: teamId, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||
err = sqlStore.GetTeamById(context.Background(), getTeamQuery)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, getTeamQuery.Result.MemberCount, 2)
|
||||
})
|
||||
|
||||
t.Run("Should be able to exclude service accounts from teamembers", func(t *testing.T) {
|
||||
sqlStore = InitTestDB(t)
|
||||
setup()
|
||||
userCmd = user.CreateUserCommand{
|
||||
Email: fmt.Sprint("sa", 1, "@test.com"),
|
||||
Name: fmt.Sprint("sa", 1),
|
||||
Login: fmt.Sprint("login-sa", 1),
|
||||
IsServiceAccount: true,
|
||||
}
|
||||
serviceAccount, err := sqlStore.CreateUser(context.Background(), userCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
groupId := team2.Id
|
||||
// add service account to team
|
||||
err = sqlStore.AddTeamMember(serviceAccount.ID, testOrgID, groupId, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
// add user to team
|
||||
err = sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
|
||||
require.NoError(t, err)
|
||||
|
||||
teamMembersQuery := &models.GetTeamMembersQuery{
|
||||
OrgId: testOrgID,
|
||||
SignedInUser: testUser,
|
||||
TeamId: groupId,
|
||||
}
|
||||
err = sqlStore.GetTeamMembers(context.Background(), teamMembersQuery)
|
||||
require.NoError(t, err)
|
||||
// should not receive service account from query
|
||||
require.Equal(t, len(teamMembersQuery.Result), 1)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationSQLStore_SearchTeams(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
type searchTeamsTestCase struct {
|
||||
desc string
|
||||
query *models.SearchTeamsQuery
|
||||
expectedNumUsers int
|
||||
}
|
||||
|
||||
tests := []searchTeamsTestCase{
|
||||
{
|
||||
desc: "should return all teams",
|
||||
query: &models.SearchTeamsQuery{
|
||||
OrgId: 1,
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
Permissions: map[int64]map[string][]string{1: {ac.ActionTeamsRead: {ac.ScopeTeamsAll}}},
|
||||
},
|
||||
},
|
||||
expectedNumUsers: 10,
|
||||
},
|
||||
{
|
||||
desc: "should return no teams",
|
||||
query: &models.SearchTeamsQuery{
|
||||
OrgId: 1,
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
Permissions: map[int64]map[string][]string{1: {ac.ActionTeamsRead: {""}}},
|
||||
},
|
||||
},
|
||||
expectedNumUsers: 0,
|
||||
},
|
||||
{
|
||||
desc: "should return some teams",
|
||||
query: &models.SearchTeamsQuery{
|
||||
OrgId: 1,
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: 1,
|
||||
Permissions: map[int64]map[string][]string{1: {ac.ActionTeamsRead: {
|
||||
"teams:id:1",
|
||||
"teams:id:5",
|
||||
"teams:id:9",
|
||||
}}},
|
||||
},
|
||||
},
|
||||
expectedNumUsers: 3,
|
||||
},
|
||||
}
|
||||
|
||||
store := InitTestDB(t, InitTestDBOpt{})
|
||||
|
||||
// Seed 10 teams
|
||||
for i := 1; i <= 10; i++ {
|
||||
_, err := store.CreateTeam(fmt.Sprintf("team-%d", i), fmt.Sprintf("team-%d@example.org", i), 1)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
err := store.SearchTeams(context.Background(), tt.query)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, tt.query.Result.Teams, tt.expectedNumUsers)
|
||||
assert.Equal(t, tt.query.Result.TotalCount, int64(tt.expectedNumUsers))
|
||||
|
||||
if !hasWildcardScope(tt.query.SignedInUser, ac.ActionTeamsRead) {
|
||||
for _, team := range tt.query.Result.Teams {
|
||||
assert.Contains(t, tt.query.SignedInUser.Permissions[tt.query.SignedInUser.OrgID][ac.ActionTeamsRead], fmt.Sprintf("teams:id:%d", team.Id))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestSQLStore_GetTeamMembers_ACFilter tests the accesscontrol filtering of
|
||||
// team members based on the signed in user permissions
|
||||
func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
testOrgID := int64(2)
|
||||
userIds := make([]int64, 4)
|
||||
|
||||
// Seed 2 teams with 2 members
|
||||
setup := func(store *SQLStore) {
|
||||
team1, errCreateTeam := store.CreateTeam("group1 name", "test1@example.org", testOrgID)
|
||||
require.NoError(t, errCreateTeam)
|
||||
team2, errCreateTeam := store.CreateTeam("group2 name", "test2@example.org", testOrgID)
|
||||
require.NoError(t, errCreateTeam)
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
userCmd := user.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@example.org"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
Login: fmt.Sprint("loginuser", i),
|
||||
}
|
||||
user, errCreateUser := store.CreateUser(context.Background(), userCmd)
|
||||
require.NoError(t, errCreateUser)
|
||||
userIds[i] = user.ID
|
||||
}
|
||||
|
||||
errAddMember := store.AddTeamMember(userIds[0], testOrgID, team1.Id, false, 0)
|
||||
require.NoError(t, errAddMember)
|
||||
errAddMember = store.AddTeamMember(userIds[1], testOrgID, team1.Id, false, 0)
|
||||
require.NoError(t, errAddMember)
|
||||
errAddMember = store.AddTeamMember(userIds[2], testOrgID, team2.Id, false, 0)
|
||||
require.NoError(t, errAddMember)
|
||||
errAddMember = store.AddTeamMember(userIds[3], testOrgID, team2.Id, false, 0)
|
||||
require.NoError(t, errAddMember)
|
||||
}
|
||||
|
||||
store := InitTestDB(t, InitTestDBOpt{})
|
||||
setup(store)
|
||||
|
||||
type getTeamMembersTestCase struct {
|
||||
desc string
|
||||
query *models.GetTeamMembersQuery
|
||||
expectedNumUsers int
|
||||
}
|
||||
|
||||
tests := []getTeamMembersTestCase{
|
||||
{
|
||||
desc: "should return all team members",
|
||||
query: &models.GetTeamMembersQuery{
|
||||
OrgId: testOrgID,
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: testOrgID,
|
||||
Permissions: map[int64]map[string][]string{testOrgID: {ac.ActionOrgUsersRead: {ac.ScopeUsersAll}}},
|
||||
},
|
||||
},
|
||||
expectedNumUsers: 4,
|
||||
},
|
||||
{
|
||||
desc: "should return no team members",
|
||||
query: &models.GetTeamMembersQuery{
|
||||
OrgId: testOrgID,
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: testOrgID,
|
||||
Permissions: map[int64]map[string][]string{testOrgID: {ac.ActionOrgUsersRead: {""}}},
|
||||
},
|
||||
},
|
||||
expectedNumUsers: 0,
|
||||
},
|
||||
{
|
||||
|
||||
desc: "should return some team members",
|
||||
query: &models.GetTeamMembersQuery{
|
||||
OrgId: testOrgID,
|
||||
SignedInUser: &user.SignedInUser{
|
||||
OrgID: testOrgID,
|
||||
Permissions: map[int64]map[string][]string{testOrgID: {ac.ActionOrgUsersRead: {
|
||||
ac.Scope("users", "id", fmt.Sprintf("%d", userIds[0])),
|
||||
ac.Scope("users", "id", fmt.Sprintf("%d", userIds[2])),
|
||||
ac.Scope("users", "id", fmt.Sprintf("%d", userIds[3])),
|
||||
}}},
|
||||
},
|
||||
},
|
||||
expectedNumUsers: 3,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
err := store.GetTeamMembers(context.Background(), tt.query)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, tt.query.Result, tt.expectedNumUsers)
|
||||
|
||||
if !hasWildcardScope(tt.query.SignedInUser, ac.ActionOrgUsersRead) {
|
||||
for _, member := range tt.query.Result {
|
||||
assert.Contains(t,
|
||||
tt.query.SignedInUser.Permissions[tt.query.SignedInUser.OrgID][ac.ActionOrgUsersRead],
|
||||
ac.Scope("users", "id", fmt.Sprintf("%d", member.UserId)),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
@@ -583,6 +584,56 @@ func (ss *SQLStore) GetSignedInUser(ctx context.Context, query *models.GetSigned
|
||||
})
|
||||
}
|
||||
|
||||
// GetTeamsByUser is used by the Guardian when checking a users' permissions
|
||||
// TODO: use team.Service after user service is split
|
||||
func (ss *SQLStore) GetTeamsByUser(ctx context.Context, query *models.GetTeamsByUserQuery) error {
|
||||
return ss.WithDbSession(ctx, func(sess *DBSession) error {
|
||||
query.Result = make([]*models.TeamDTO, 0)
|
||||
|
||||
var sql bytes.Buffer
|
||||
var params []interface{}
|
||||
params = append(params, query.OrgId, query.UserId)
|
||||
|
||||
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 = ?`)
|
||||
|
||||
if !ac.IsDisabled(ss.Cfg) {
|
||||
acFilter, err := ac.Filter(query.SignedInUser, "team.id", "teams:id:", ac.ActionTeamsRead)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sql.WriteString(` and` + acFilter.Where)
|
||||
params = append(params, acFilter.Args...)
|
||||
}
|
||||
|
||||
err := sess.SQL(sql.String(), params...).Find(&query.Result)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
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 getTeamSelectSQLBase(filteredUsers []string) string {
|
||||
return `SELECT
|
||||
team.id as id,
|
||||
team.org_id,
|
||||
team.name as name,
|
||||
team.email as email, ` +
|
||||
getTeamMemberCount(filteredUsers) +
|
||||
` FROM team as team `
|
||||
}
|
||||
|
||||
func (ss *SQLStore) SearchUsers(ctx context.Context, query *models.SearchUsersQuery) error {
|
||||
return ss.WithDbSession(ctx, func(dbSess *DBSession) error {
|
||||
query.Result = models.SearchUserQueryResult{
|
||||
|
||||
Reference in New Issue
Block a user