mirror of
https://github.com/grafana/grafana.git
synced 2025-07-28 17:12:19 +08:00
Chore: Remove legacy AC checks from team (#68715)
* removing legacy AC checks from team API handlers * Chore: remove `UserIDFilter` from team queries (#68820) * remove userIDfilter from team queries in favour of RBAC SQL filtering * fix typo * remove redundant tests * remove another unused function * fix failing test
This commit is contained in:
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -541,7 +541,6 @@ lerna.json @grafana/frontend-ops
|
|||||||
/pkg/services/loginattempt/ @grafana/grafana-authnz-team
|
/pkg/services/loginattempt/ @grafana/grafana-authnz-team
|
||||||
/pkg/services/oauthtoken/ @grafana/grafana-authnz-team
|
/pkg/services/oauthtoken/ @grafana/grafana-authnz-team
|
||||||
/pkg/services/serviceaccounts/ @grafana/grafana-authnz-team
|
/pkg/services/serviceaccounts/ @grafana/grafana-authnz-team
|
||||||
/pkg/services/teamguardian/ @grafana/grafana-authnz-team
|
|
||||||
|
|
||||||
# Support bundles
|
# Support bundles
|
||||||
/public/app/features/support-bundles/ @grafana/grafana-authnz-team
|
/public/app/features/support-bundles/ @grafana/grafana-authnz-team
|
||||||
|
@ -235,7 +235,7 @@ func (hs *HTTPServer) AdminDeleteUser(c *contextmodel.ReqContext) response.Respo
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
if err := hs.teamGuardian.DeleteByUser(ctx, cmd.UserID); err != nil {
|
if err := hs.teamService.RemoveUsersMemberships(ctx, cmd.UserID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -93,7 +93,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/store/entity/httpentitystore"
|
"github.com/grafana/grafana/pkg/services/store/entity/httpentitystore"
|
||||||
"github.com/grafana/grafana/pkg/services/tag"
|
"github.com/grafana/grafana/pkg/services/tag"
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
"github.com/grafana/grafana/pkg/services/teamguardian"
|
|
||||||
tempUser "github.com/grafana/grafana/pkg/services/temp_user"
|
tempUser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||||
"github.com/grafana/grafana/pkg/services/updatechecker"
|
"github.com/grafana/grafana/pkg/services/updatechecker"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
@ -168,7 +167,6 @@ type HTTPServer struct {
|
|||||||
grafanaUpdateChecker *updatechecker.GrafanaService
|
grafanaUpdateChecker *updatechecker.GrafanaService
|
||||||
pluginsUpdateChecker *updatechecker.PluginsService
|
pluginsUpdateChecker *updatechecker.PluginsService
|
||||||
searchUsersService searchusers.Service
|
searchUsersService searchusers.Service
|
||||||
teamGuardian teamguardian.TeamGuardian
|
|
||||||
queryDataService query.Service
|
queryDataService query.Service
|
||||||
serviceAccountsService serviceaccounts.Service
|
serviceAccountsService serviceaccounts.Service
|
||||||
authInfoService login.AuthInfoService
|
authInfoService login.AuthInfoService
|
||||||
@ -231,7 +229,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
encryptionService encryption.Internal, grafanaUpdateChecker *updatechecker.GrafanaService,
|
encryptionService encryption.Internal, grafanaUpdateChecker *updatechecker.GrafanaService,
|
||||||
pluginsUpdateChecker *updatechecker.PluginsService, searchUsersService searchusers.Service,
|
pluginsUpdateChecker *updatechecker.PluginsService, searchUsersService searchusers.Service,
|
||||||
dataSourcesService datasources.DataSourceService, queryDataService query.Service, pluginFileStore plugins.FileStore,
|
dataSourcesService datasources.DataSourceService, queryDataService query.Service, pluginFileStore plugins.FileStore,
|
||||||
teamGuardian teamguardian.TeamGuardian, serviceaccountsService serviceaccounts.Service,
|
serviceaccountsService serviceaccounts.Service,
|
||||||
authInfoService login.AuthInfoService, storageService store.StorageService, httpEntityStore httpentitystore.HTTPEntityStore,
|
authInfoService login.AuthInfoService, storageService store.StorageService, httpEntityStore httpentitystore.HTTPEntityStore,
|
||||||
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
|
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
|
||||||
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service,
|
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service,
|
||||||
@ -314,7 +312,6 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
httpEntityStore: httpEntityStore,
|
httpEntityStore: httpEntityStore,
|
||||||
DataSourcesService: dataSourcesService,
|
DataSourcesService: dataSourcesService,
|
||||||
searchUsersService: searchUsersService,
|
searchUsersService: searchUsersService,
|
||||||
teamGuardian: teamGuardian,
|
|
||||||
queryDataService: queryDataService,
|
queryDataService: queryDataService,
|
||||||
serviceAccountsService: serviceaccountsService,
|
serviceAccountsService: serviceaccountsService,
|
||||||
authInfoService: authInfoService,
|
authInfoService: authInfoService,
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
@ -30,10 +29,6 @@ func (hs *HTTPServer) CreateTeam(c *contextmodel.ReqContext) response.Response {
|
|||||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||||
}
|
}
|
||||||
accessControlEnabled := !hs.AccessControl.IsDisabled()
|
|
||||||
if !accessControlEnabled && c.OrgRole == org.RoleViewer {
|
|
||||||
return response.Error(403, "Not allowed to create team.", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := hs.teamService.CreateTeam(cmd.Name, cmd.Email, c.OrgID)
|
t, err := hs.teamService.CreateTeam(cmd.Name, cmd.Email, c.OrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -45,21 +40,17 @@ func (hs *HTTPServer) CreateTeam(c *contextmodel.ReqContext) response.Response {
|
|||||||
|
|
||||||
// Clear permission cache for the user who's created the team, so that new permissions are fetched for their next call
|
// Clear permission cache for the user who's created the team, so that new permissions are fetched for their next call
|
||||||
// Required for cases when caller wants to immediately interact with the newly created object
|
// Required for cases when caller wants to immediately interact with the newly created object
|
||||||
if !hs.AccessControl.IsDisabled() {
|
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
|
||||||
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
if accessControlEnabled || (c.OrgRole == org.RoleEditor && hs.Cfg.EditorsCanAdmin) {
|
// if the request is authenticated using API tokens
|
||||||
// if the request is authenticated using API tokens
|
// the SignedInUser is an empty struct therefore
|
||||||
// the SignedInUser is an empty struct therefore
|
// an additional check whether it is an actual user is required
|
||||||
// an additional check whether it is an actual user is required
|
if c.SignedInUser.IsRealUser() {
|
||||||
if c.SignedInUser.IsRealUser() {
|
if err := addOrUpdateTeamMember(c.Req.Context(), hs.teamPermissionsService, c.SignedInUser.UserID, c.OrgID, t.ID, dashboards.PERMISSION_ADMIN.String()); err != nil {
|
||||||
if err := addOrUpdateTeamMember(c.Req.Context(), hs.teamPermissionsService, c.SignedInUser.UserID, c.OrgID, t.ID, dashboards.PERMISSION_ADMIN.String()); err != nil {
|
c.Logger.Error("Could not add creator to team", "error", err)
|
||||||
c.Logger.Error("Could not add creator to team", "error", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.Logger.Warn("Could not add creator to team because is not a real user")
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
c.Logger.Warn("Could not add creator to team because is not a real user")
|
||||||
}
|
}
|
||||||
return response.JSON(http.StatusOK, &util.DynMap{
|
return response.JSON(http.StatusOK, &util.DynMap{
|
||||||
"teamId": t.ID,
|
"teamId": t.ID,
|
||||||
@ -90,12 +81,6 @@ func (hs *HTTPServer) UpdateTeam(c *contextmodel.ReqContext) response.Response {
|
|||||||
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
if err := hs.teamGuardian.CanAdmin(c.Req.Context(), cmd.OrgID, cmd.ID, c.SignedInUser); err != nil {
|
|
||||||
return response.Error(403, "Not allowed to update team", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hs.teamService.UpdateTeam(c.Req.Context(), &cmd); err != nil {
|
if err := hs.teamService.UpdateTeam(c.Req.Context(), &cmd); err != nil {
|
||||||
if errors.Is(err, team.ErrTeamNameTaken) {
|
if errors.Is(err, team.ErrTeamNameTaken) {
|
||||||
return response.Error(400, "Team name taken", err)
|
return response.Error(400, "Team name taken", err)
|
||||||
@ -122,13 +107,6 @@ func (hs *HTTPServer) DeleteTeamByID(c *contextmodel.ReqContext) response.Respon
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
||||||
}
|
}
|
||||||
user := c.SignedInUser
|
|
||||||
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
if err := hs.teamGuardian.CanAdmin(c.Req.Context(), orgID, teamID, user); err != nil {
|
|
||||||
return response.Error(403, "Not allowed to delete team", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hs.teamService.DeleteTeam(c.Req.Context(), &team.DeleteTeamCommand{OrgID: orgID, ID: teamID}); err != nil {
|
if err := hs.teamService.DeleteTeam(c.Req.Context(), &team.DeleteTeamCommand{OrgID: orgID, ID: teamID}); err != nil {
|
||||||
if errors.Is(err, team.ErrTeamNotFound) {
|
if errors.Is(err, team.ErrTeamNotFound) {
|
||||||
@ -158,17 +136,10 @@ func (hs *HTTPServer) SearchTeams(c *contextmodel.ReqContext) response.Response
|
|||||||
page = 1
|
page = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using accesscontrol the filtering is done based on user permissions
|
|
||||||
userIDFilter := team.FilterIgnoreUser
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
userIDFilter = userFilter(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
query := team.SearchTeamsQuery{
|
query := team.SearchTeamsQuery{
|
||||||
OrgID: c.OrgID,
|
OrgID: c.OrgID,
|
||||||
Query: c.Query("query"),
|
Query: c.Query("query"),
|
||||||
Name: c.Query("name"),
|
Name: c.Query("name"),
|
||||||
UserIDFilter: userIDFilter,
|
|
||||||
Page: page,
|
Page: page,
|
||||||
Limit: perPage,
|
Limit: perPage,
|
||||||
SignedInUser: c.SignedInUser,
|
SignedInUser: c.SignedInUser,
|
||||||
@ -199,17 +170,6 @@ func (hs *HTTPServer) SearchTeams(c *contextmodel.ReqContext) response.Response
|
|||||||
return response.JSON(http.StatusOK, queryResult)
|
return response.JSON(http.StatusOK, queryResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserFilter returns the user ID used in a filter when querying a team
|
|
||||||
// 1. If the user is a viewer or editor, this will return the user's ID.
|
|
||||||
// 2. If the user is an admin, this will return models.FilterIgnoreUser (0)
|
|
||||||
func userFilter(c *contextmodel.ReqContext) int64 {
|
|
||||||
userIdFilter := c.SignedInUser.UserID
|
|
||||||
if c.OrgRole == org.RoleAdmin {
|
|
||||||
userIdFilter = team.FilterIgnoreUser
|
|
||||||
}
|
|
||||||
return userIdFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
// swagger:route GET /teams/{team_id} teams getTeamByID
|
// swagger:route GET /teams/{team_id} teams getTeamByID
|
||||||
//
|
//
|
||||||
// Get Team By ID.
|
// Get Team By ID.
|
||||||
@ -226,18 +186,11 @@ func (hs *HTTPServer) GetTeamByID(c *contextmodel.ReqContext) response.Response
|
|||||||
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using accesscontrol the filtering has already been performed at middleware layer
|
|
||||||
userIdFilter := team.FilterIgnoreUser
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
userIdFilter = userFilter(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
query := team.GetTeamByIDQuery{
|
query := team.GetTeamByIDQuery{
|
||||||
OrgID: c.OrgID,
|
OrgID: c.OrgID,
|
||||||
ID: teamId,
|
ID: teamId,
|
||||||
SignedInUser: c.SignedInUser,
|
SignedInUser: c.SignedInUser,
|
||||||
HiddenUsers: hs.Cfg.HiddenUsers,
|
HiddenUsers: hs.Cfg.HiddenUsers,
|
||||||
UserIdFilter: userIdFilter,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
queryResult, err := hs.teamService.GetTeamByID(c.Req.Context(), &query)
|
queryResult, err := hs.teamService.GetTeamByID(c.Req.Context(), &query)
|
||||||
@ -270,15 +223,7 @@ func (hs *HTTPServer) GetTeamPreferences(c *contextmodel.ReqContext) response.Re
|
|||||||
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
orgId := c.OrgID
|
return hs.getPreferencesFor(c.Req.Context(), c.OrgID, 0, teamId)
|
||||||
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
if err := hs.teamGuardian.CanAdmin(c.Req.Context(), orgId, teamId, c.SignedInUser); err != nil {
|
|
||||||
return response.Error(403, "Not allowed to view team preferences.", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hs.getPreferencesFor(c.Req.Context(), orgId, 0, teamId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:route PUT /teams/{team_id}/preferences teams updateTeamPreferences
|
// swagger:route PUT /teams/{team_id}/preferences teams updateTeamPreferences
|
||||||
@ -301,15 +246,7 @@ func (hs *HTTPServer) UpdateTeamPreferences(c *contextmodel.ReqContext) response
|
|||||||
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
orgId := c.OrgID
|
return hs.updatePreferencesFor(c.Req.Context(), c.OrgID, 0, teamId, &dtoCmd)
|
||||||
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
if err := hs.teamGuardian.CanAdmin(c.Req.Context(), orgId, teamId, c.SignedInUser); err != nil {
|
|
||||||
return response.Error(403, "Not allowed to update team preferences.", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hs.updatePreferencesFor(c.Req.Context(), orgId, 0, teamId, &dtoCmd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// swagger:parameters updateTeamPreferences
|
// swagger:parameters updateTeamPreferences
|
||||||
|
@ -36,14 +36,6 @@ func (hs *HTTPServer) GetTeamMembers(c *contextmodel.ReqContext) response.Respon
|
|||||||
|
|
||||||
query := team.GetTeamMembersQuery{OrgID: c.OrgID, TeamID: teamId, SignedInUser: c.SignedInUser}
|
query := team.GetTeamMembersQuery{OrgID: c.OrgID, TeamID: teamId, SignedInUser: c.SignedInUser}
|
||||||
|
|
||||||
// With accesscontrol the permission check has been done at middleware layer
|
|
||||||
// and the membership filtering will be done at DB layer based on user permissions
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
if err := hs.teamGuardian.CanAdmin(c.Req.Context(), query.OrgID, query.TeamID, c.SignedInUser); err != nil {
|
|
||||||
return response.Error(403, "Not allowed to list team members", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queryResult, err := hs.teamService.GetTeamMembers(c.Req.Context(), &query)
|
queryResult, err := hs.teamService.GetTeamMembers(c.Req.Context(), &query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to get Team Members", err)
|
return response.Error(500, "Failed to get Team Members", err)
|
||||||
@ -91,12 +83,6 @@ func (hs *HTTPServer) AddTeamMember(c *contextmodel.ReqContext) response.Respons
|
|||||||
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
return response.Error(http.StatusBadRequest, "teamId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
if err := hs.teamGuardian.CanAdmin(c.Req.Context(), cmd.OrgID, cmd.TeamID, c.SignedInUser); err != nil {
|
|
||||||
return response.Error(403, "Not allowed to add team member", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isTeamMember, err := hs.teamService.IsTeamMember(c.OrgID, cmd.TeamID, cmd.UserID)
|
isTeamMember, err := hs.teamService.IsTeamMember(c.OrgID, cmd.TeamID, cmd.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to add team member.", err)
|
return response.Error(500, "Failed to add team member.", err)
|
||||||
@ -140,12 +126,6 @@ func (hs *HTTPServer) UpdateTeamMember(c *contextmodel.ReqContext) response.Resp
|
|||||||
}
|
}
|
||||||
orgId := c.OrgID
|
orgId := c.OrgID
|
||||||
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
if err := hs.teamGuardian.CanAdmin(c.Req.Context(), orgId, teamId, c.SignedInUser); err != nil {
|
|
||||||
return response.Error(403, "Not allowed to update team member", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isTeamMember, err := hs.teamService.IsTeamMember(orgId, teamId, userId)
|
isTeamMember, err := hs.teamService.IsTeamMember(orgId, teamId, userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to update team member.", err)
|
return response.Error(500, "Failed to update team member.", err)
|
||||||
@ -192,12 +172,6 @@ func (hs *HTTPServer) RemoveTeamMember(c *contextmodel.ReqContext) response.Resp
|
|||||||
return response.Error(http.StatusBadRequest, "userId is invalid", err)
|
return response.Error(http.StatusBadRequest, "userId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hs.AccessControl.IsDisabled() {
|
|
||||||
if err := hs.teamGuardian.CanAdmin(c.Req.Context(), orgId, teamId, c.SignedInUser); err != nil {
|
|
||||||
return response.Error(403, "Not allowed to remove team member", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
teamIDString := strconv.FormatInt(teamId, 10)
|
teamIDString := strconv.FormatInt(teamId, 10)
|
||||||
if _, err := hs.teamPermissionsService.SetUserPermission(c.Req.Context(), orgId, accesscontrol.User{ID: userId}, teamIDString, ""); err != nil {
|
if _, err := hs.teamPermissionsService.SetUserPermission(c.Req.Context(), orgId, accesscontrol.User{ID: userId}, teamIDString, ""); err != nil {
|
||||||
if errors.Is(err, team.ErrTeamNotFound) {
|
if errors.Is(err, team.ErrTeamNotFound) {
|
||||||
|
@ -1,193 +1,21 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/licensing"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
||||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||||
"github.com/grafana/grafana/pkg/services/teamguardian/database"
|
|
||||||
"github.com/grafana/grafana/pkg/services/teamguardian/manager"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/web/webtest"
|
"github.com/grafana/grafana/pkg/web/webtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TeamGuardianMock struct {
|
func TestAddTeamMembersAPIEndpoint(t *testing.T) {
|
||||||
result error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TeamGuardianMock) CanAdmin(ctx context.Context, orgId int64, teamId int64, user *user.SignedInUser) error {
|
|
||||||
return t.result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TeamGuardianMock) DeleteByUser(ctx context.Context, userID int64) error {
|
|
||||||
return t.result
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUpGetTeamMembersHandler(t *testing.T, sqlStore *sqlstore.SQLStore) {
|
|
||||||
const testOrgID int64 = 1
|
|
||||||
var userCmd user.CreateUserCommand
|
|
||||||
teamSvc := teamimpl.ProvideService(sqlStore, setting.NewCfg())
|
|
||||||
team, err := teamSvc.CreateTeam("group1 name", "test1@test.com", testOrgID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
quotaService := quotaimpl.ProvideService(sqlStore, sqlStore.Cfg)
|
|
||||||
orgService, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, quotaService)
|
|
||||||
require.NoError(t, err)
|
|
||||||
usrSvc, err := userimpl.ProvideService(sqlStore, orgService, sqlStore.Cfg, nil, nil, quotaService, supportbundlestest.NewFakeBundleService())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
userCmd = user.CreateUserCommand{
|
|
||||||
Email: fmt.Sprint("user", i, "@test.com"),
|
|
||||||
Name: fmt.Sprint("user", i),
|
|
||||||
Login: fmt.Sprint("loginuser", i),
|
|
||||||
}
|
|
||||||
// user
|
|
||||||
user, err := usrSvc.Create(context.Background(), &userCmd)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = teamSvc.AddTeamMember(user.ID, testOrgID, team.ID, false, 1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTeamMembersAPIEndpoint_userLoggedIn(t *testing.T) {
|
|
||||||
hs := setupSimpleHTTPServer(nil)
|
|
||||||
settings := hs.Cfg
|
|
||||||
sqlStore := db.InitTestDB(t)
|
|
||||||
sqlStore.Cfg = settings
|
|
||||||
|
|
||||||
hs.SQLStore = sqlStore
|
|
||||||
hs.teamService = teamimpl.ProvideService(sqlStore, settings)
|
|
||||||
hs.License = &licensing.OSSLicensingService{}
|
|
||||||
hs.teamGuardian = &TeamGuardianMock{}
|
|
||||||
mock := dbtest.NewFakeDB()
|
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "api/teams/1/members",
|
|
||||||
"api/teams/:teamId/members", org.RoleAdmin, func(sc *scenarioContext) {
|
|
||||||
setUpGetTeamMembersHandler(t, sqlStore)
|
|
||||||
|
|
||||||
sc.handlerFunc = hs.GetTeamMembers
|
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
||||||
|
|
||||||
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
||||||
|
|
||||||
var resp []team.TeamMemberDTO
|
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Len(t, resp, 3)
|
|
||||||
}, mock)
|
|
||||||
|
|
||||||
t.Run("Given there is two hidden users", func(t *testing.T) {
|
|
||||||
settings.HiddenUsers = map[string]struct{}{
|
|
||||||
"user1": {},
|
|
||||||
testUserLogin: {},
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { settings.HiddenUsers = make(map[string]struct{}) })
|
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "api/teams/1/members",
|
|
||||||
"api/teams/:teamId/members", org.RoleAdmin, func(sc *scenarioContext) {
|
|
||||||
setUpGetTeamMembersHandler(t, sqlStore)
|
|
||||||
|
|
||||||
sc.handlerFunc = hs.GetTeamMembers
|
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
||||||
|
|
||||||
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
||||||
|
|
||||||
var resp []team.TeamMemberDTO
|
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Len(t, resp, 3)
|
|
||||||
assert.Equal(t, "loginuser0", resp[0].Login)
|
|
||||||
assert.Equal(t, "loginuser1", resp[1].Login)
|
|
||||||
assert.Equal(t, "loginuser2", resp[2].Login)
|
|
||||||
}, mock)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddTeamMembersAPIEndpoint_LegacyAccessControl(t *testing.T) {
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
||||||
cfg := setting.NewCfg()
|
|
||||||
cfg.RBACEnabled = false
|
|
||||||
cfg.EditorsCanAdmin = true
|
|
||||||
hs.Cfg = cfg
|
|
||||||
hs.teamService = teamtest.NewFakeService()
|
|
||||||
store := &database.TeamGuardianStoreMock{}
|
|
||||||
store.On("GetTeamMembers", mock.Anything, mock.Anything).Return([]*team.TeamMemberDTO{
|
|
||||||
{UserID: 2, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{UserID: 3, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
}, nil).Maybe()
|
|
||||||
hs.teamGuardian = manager.ProvideService(store)
|
|
||||||
hs.teamPermissionsService = &actest.FakePermissionsService{}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Admin can add team member", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodPost, "/api/teams/1/members", strings.NewReader("{\"userId\": 1}")),
|
|
||||||
&user.SignedInUser{OrgID: 1, OrgRole: org.RoleAdmin},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Editor cannot add team member", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodPost, "/api/teams/1/members", strings.NewReader("{\"userId\": 1}")),
|
|
||||||
&user.SignedInUser{OrgID: 1, OrgRole: org.RoleEditor},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("team member cannot add members", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodPost, "/api/teams/1/members", strings.NewReader("{\"userId\": 1}")),
|
|
||||||
&user.SignedInUser{UserID: 3, OrgID: 1, OrgRole: org.RoleViewer},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("team admin can add members", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodPost, "/api/teams/1/members", strings.NewReader("{\"userId\": 1}")),
|
|
||||||
&user.SignedInUser{UserID: 2, OrgID: 1, OrgRole: org.RoleEditor},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddTeamMembersAPIEndpoint_RBAC(t *testing.T) {
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = teamtest.NewFakeService()
|
hs.teamService = teamtest.NewFakeService()
|
||||||
@ -217,7 +45,7 @@ func TestAddTeamMembersAPIEndpoint_RBAC(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetTeamMembersAPIEndpoint_RBAC(t *testing.T) {
|
func TestGetTeamMembersAPIEndpoint(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = teamtest.NewFakeService()
|
hs.teamService = teamtest.NewFakeService()
|
||||||
@ -246,68 +74,7 @@ func TestGetTeamMembersAPIEndpoint_RBAC(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateTeamMembersAPIEndpoint_LegacyAccessControl(t *testing.T) {
|
func TestUpdateTeamMembersAPIEndpoint(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
||||||
cfg := setting.NewCfg()
|
|
||||||
cfg.RBACEnabled = false
|
|
||||||
cfg.EditorsCanAdmin = true
|
|
||||||
hs.Cfg = cfg
|
|
||||||
hs.teamService = &teamtest.FakeService{ExpectedIsMember: true}
|
|
||||||
store := &database.TeamGuardianStoreMock{}
|
|
||||||
store.On("GetTeamMembers", mock.Anything, mock.Anything).Return([]*team.TeamMemberDTO{
|
|
||||||
{UserID: 2, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{UserID: 3, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
}, nil).Maybe()
|
|
||||||
hs.teamGuardian = manager.ProvideService(store)
|
|
||||||
hs.teamPermissionsService = &actest.FakePermissionsService{}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Admin can update team member", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodPut, "/api/teams/1/members/1", strings.NewReader("{\"permission\": 4}")),
|
|
||||||
&user.SignedInUser{OrgID: 1, OrgRole: org.RoleAdmin},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Editor cannot update team member", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodPut, "/api/teams/1/members/1", strings.NewReader("{\"permission\": 4}")),
|
|
||||||
&user.SignedInUser{OrgID: 1, OrgRole: org.RoleEditor},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("team member cannot update member", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodPut, "/api/teams/1/members/1", strings.NewReader("{\"permission\": 4}")),
|
|
||||||
&user.SignedInUser{UserID: 3, OrgID: 1, OrgRole: org.RoleViewer},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("team admin can add members", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodPut, "/api/teams/1/members/1", strings.NewReader("{\"permission\": 4}")),
|
|
||||||
&user.SignedInUser{UserID: 2, OrgID: 1, OrgRole: org.RoleEditor},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateTeamMembersAPIEndpoint_RBAC(t *testing.T) {
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = &teamtest.FakeService{ExpectedIsMember: true}
|
hs.teamService = &teamtest.FakeService{ExpectedIsMember: true}
|
||||||
@ -336,68 +103,7 @@ func TestUpdateTeamMembersAPIEndpoint_RBAC(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteTeamMembersAPIEndpoint_LegacyAccessControl(t *testing.T) {
|
func TestDeleteTeamMembersAPIEndpoint(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
||||||
cfg := setting.NewCfg()
|
|
||||||
cfg.RBACEnabled = false
|
|
||||||
cfg.EditorsCanAdmin = true
|
|
||||||
hs.Cfg = cfg
|
|
||||||
hs.teamService = &teamtest.FakeService{ExpectedIsMember: true}
|
|
||||||
store := &database.TeamGuardianStoreMock{}
|
|
||||||
store.On("GetTeamMembers", mock.Anything, mock.Anything).Return([]*team.TeamMemberDTO{
|
|
||||||
{UserID: 2, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{UserID: 3, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
}, nil).Maybe()
|
|
||||||
hs.teamGuardian = manager.ProvideService(store)
|
|
||||||
hs.teamPermissionsService = &actest.FakePermissionsService{}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Admin can delete team member", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodDelete, "/api/teams/1/members/1", nil),
|
|
||||||
&user.SignedInUser{OrgID: 1, OrgRole: org.RoleAdmin},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Editor cannot delete team member", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodDelete, "/api/teams/1/members/1", nil),
|
|
||||||
&user.SignedInUser{OrgID: 1, OrgRole: org.RoleEditor},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("team member cannot delete member", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodDelete, "/api/teams/1/members/1", nil),
|
|
||||||
&user.SignedInUser{UserID: 3, OrgID: 1, OrgRole: org.RoleViewer},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("team admin can delete members", func(t *testing.T) {
|
|
||||||
req := webtest.RequestWithSignedInUser(
|
|
||||||
server.NewRequest(http.MethodDelete, "/api/teams/1/members/1", nil),
|
|
||||||
&user.SignedInUser{UserID: 2, OrgID: 1, OrgRole: org.RoleEditor},
|
|
||||||
)
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteTeamMembersAPIEndpoint_RBAC(t *testing.T) {
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = &teamtest.FakeService{ExpectedIsMember: true}
|
hs.teamService = &teamtest.FakeService{ExpectedIsMember: true}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -11,151 +9,18 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log/logtest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||||
"github.com/grafana/grafana/pkg/services/preference/preftest"
|
"github.com/grafana/grafana/pkg/services/preference/preftest"
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
"github.com/grafana/grafana/pkg/services/team"
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
|
||||||
"github.com/grafana/grafana/pkg/web/webtest"
|
"github.com/grafana/grafana/pkg/web/webtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTeamAPIEndpoint(t *testing.T) {
|
|
||||||
t.Run("Given two teams", func(t *testing.T) {
|
|
||||||
hs := setupSimpleHTTPServer(nil)
|
|
||||||
hs.Cfg.EditorsCanAdmin = true
|
|
||||||
store := db.InitTestDB(t)
|
|
||||||
store.Cfg = hs.Cfg
|
|
||||||
hs.teamService = teamimpl.ProvideService(store, hs.Cfg)
|
|
||||||
hs.SQLStore = store
|
|
||||||
mock := dbtest.NewFakeDB()
|
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When admin is calling GET on", "GET", "/api/teams/search", "/api/teams/search",
|
|
||||||
org.RoleAdmin, func(sc *scenarioContext) {
|
|
||||||
_, err := hs.teamService.CreateTeam("team1", "", 1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
_, err = hs.teamService.CreateTeam("team2", "", 1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
sc.handlerFunc = hs.SearchTeams
|
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
||||||
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
||||||
var resp team.SearchTeamQueryResult
|
|
||||||
err = json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.EqualValues(t, 2, resp.TotalCount)
|
|
||||||
assert.Equal(t, 2, len(resp.Teams))
|
|
||||||
}, mock)
|
|
||||||
|
|
||||||
loggedInUserScenario(t, "When editor (with editors_can_admin) is calling GET on", "/api/teams/search",
|
|
||||||
"/api/teams/search", func(sc *scenarioContext) {
|
|
||||||
team1, err := hs.teamService.CreateTeam("team1", "", 1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
_, err = hs.teamService.CreateTeam("team2", "", 1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Adding the test user to the teams in order for him to list them
|
|
||||||
err = hs.teamService.AddTeamMember(testUserID, testOrgID, team1.ID, false, 0)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
sc.handlerFunc = hs.SearchTeams
|
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
||||||
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
||||||
var resp team.SearchTeamQueryResult
|
|
||||||
err = json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.EqualValues(t, 1, resp.TotalCount)
|
|
||||||
assert.Equal(t, 1, len(resp.Teams))
|
|
||||||
}, mock)
|
|
||||||
|
|
||||||
loggedInUserScenario(t, "When editor (with editors_can_admin) calling GET with pagination on",
|
|
||||||
"/api/teams/search", "/api/teams/search", func(sc *scenarioContext) {
|
|
||||||
team1, err := hs.teamService.CreateTeam("team1", "", 1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
team2, err := hs.teamService.CreateTeam("team2", "", 1)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Adding the test user to the teams in order for him to list them
|
|
||||||
err = hs.teamService.AddTeamMember(testUserID, testOrgID, team1.ID, false, 0)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = hs.teamService.AddTeamMember(testUserID, testOrgID, team2.ID, false, 0)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
sc.handlerFunc = hs.SearchTeams
|
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
|
|
||||||
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
||||||
var resp team.SearchTeamQueryResult
|
|
||||||
err = json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.EqualValues(t, 2, resp.TotalCount)
|
|
||||||
assert.Equal(t, 0, len(resp.Teams))
|
|
||||||
}, mock)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("When creating team with API key", func(t *testing.T) {
|
|
||||||
hs := setupSimpleHTTPServer(nil)
|
|
||||||
hs.Cfg.EditorsCanAdmin = true
|
|
||||||
hs.SQLStore = dbtest.NewFakeDB()
|
|
||||||
hs.teamService = &teamtest.FakeService{}
|
|
||||||
teamName := "team foo"
|
|
||||||
|
|
||||||
addTeamMemberCalled := 0
|
|
||||||
addOrUpdateTeamMember = func(ctx context.Context, resourcePermissionService accesscontrol.TeamPermissionsService, userID, orgID, teamID int64,
|
|
||||||
permission string) error {
|
|
||||||
addTeamMemberCalled++
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/api/teams", nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("with no real signed in user", func(t *testing.T) {
|
|
||||||
logger := &logtest.Fake{}
|
|
||||||
c := &contextmodel.ReqContext{
|
|
||||||
Context: &web.Context{Req: req},
|
|
||||||
SignedInUser: &user.SignedInUser{},
|
|
||||||
Logger: logger,
|
|
||||||
}
|
|
||||||
c.OrgRole = org.RoleEditor
|
|
||||||
c.Req.Body = mockRequestBody(team.CreateTeamCommand{Name: teamName})
|
|
||||||
c.Req.Header.Add("Content-Type", "application/json")
|
|
||||||
r := hs.CreateTeam(c)
|
|
||||||
|
|
||||||
assert.Equal(t, 200, r.Status())
|
|
||||||
assert.NotZero(t, logger.WarnLogs.Calls)
|
|
||||||
assert.Equal(t, "Could not add creator to team because is not a real user", logger.WarnLogs.Message)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with real signed in user", func(t *testing.T) {
|
|
||||||
logger := &logtest.Fake{}
|
|
||||||
c := &contextmodel.ReqContext{
|
|
||||||
Context: &web.Context{Req: req},
|
|
||||||
SignedInUser: &user.SignedInUser{UserID: 42},
|
|
||||||
Logger: logger,
|
|
||||||
}
|
|
||||||
c.OrgRole = org.RoleEditor
|
|
||||||
c.Req.Body = mockRequestBody(team.CreateTeamCommand{Name: teamName})
|
|
||||||
c.Req.Header.Add("Content-Type", "application/json")
|
|
||||||
r := hs.CreateTeam(c)
|
|
||||||
assert.Equal(t, 200, r.Status())
|
|
||||||
assert.Zero(t, logger.WarnLogs.Calls)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
searchTeamsURL = "/api/teams/search"
|
searchTeamsURL = "/api/teams/search"
|
||||||
createTeamURL = "/api/teams/"
|
createTeamURL = "/api/teams/"
|
||||||
@ -163,56 +28,9 @@ const (
|
|||||||
detailTeamPreferenceURL = "/api/teams/%d/preferences"
|
detailTeamPreferenceURL = "/api/teams/%d/preferences"
|
||||||
teamCmd = `{"name": "MyTestTeam%d"}`
|
teamCmd = `{"name": "MyTestTeam%d"}`
|
||||||
teamPreferenceCmd = `{"theme": "dark"}`
|
teamPreferenceCmd = `{"theme": "dark"}`
|
||||||
teamPreferenceCmdLight = `{"theme": "light"}`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTeamAPIEndpoint_CreateTeam_LegacyAccessControl(t *testing.T) {
|
func TestTeamAPIEndpoint_CreateTeam(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
||||||
hs.teamService = teamtest.NewFakeService()
|
|
||||||
})
|
|
||||||
|
|
||||||
input := strings.NewReader(fmt.Sprintf(teamCmd, 1))
|
|
||||||
t.Run("Organisation admin can create a team", func(t *testing.T) {
|
|
||||||
req := server.NewPostRequest(createTeamURL, input)
|
|
||||||
req = webtest.RequestWithSignedInUser(req, &user.SignedInUser{OrgRole: org.RoleAdmin})
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
|
|
||||||
input = strings.NewReader(fmt.Sprintf(teamCmd, 2))
|
|
||||||
t.Run("Org editor and server admin cannot create a team", func(t *testing.T) {
|
|
||||||
req := server.NewPostRequest(createTeamURL, input)
|
|
||||||
req = webtest.RequestWithSignedInUser(req, &user.SignedInUser{OrgRole: org.RoleEditor, IsGrafanaAdmin: true})
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTeamAPIEndpoint_CreateTeam_LegacyAccessControl_EditorsCanAdmin(t *testing.T) {
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
|
||||||
cfg := setting.NewCfg()
|
|
||||||
cfg.RBACEnabled = false
|
|
||||||
cfg.EditorsCanAdmin = true
|
|
||||||
hs.Cfg = cfg
|
|
||||||
hs.teamService = teamtest.NewFakeService()
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Editors can create a team if editorsCanAdmin is set to true", func(t *testing.T) {
|
|
||||||
input := strings.NewReader(fmt.Sprintf(teamCmd, 1))
|
|
||||||
req := server.NewPostRequest(createTeamURL, input)
|
|
||||||
req = webtest.RequestWithSignedInUser(req, &user.SignedInUser{OrgRole: org.RoleAdmin})
|
|
||||||
res, err := server.SendJSON(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
||||||
require.NoError(t, res.Body.Close())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTeamAPIEndpoint_CreateTeam_RBAC(t *testing.T) {
|
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = teamtest.NewFakeService()
|
hs.teamService = teamtest.NewFakeService()
|
||||||
@ -241,7 +59,7 @@ func TestTeamAPIEndpoint_CreateTeam_RBAC(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTeamAPIEndpoint_SearchTeams_RBAC(t *testing.T) {
|
func TestTeamAPIEndpoint_SearchTeams(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = teamtest.NewFakeService()
|
hs.teamService = teamtest.NewFakeService()
|
||||||
@ -268,7 +86,7 @@ func TestTeamAPIEndpoint_SearchTeams_RBAC(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTeamAPIEndpoint_GetTeamByID_RBAC(t *testing.T) {
|
func TestTeamAPIEndpoint_GetTeamByID(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = &teamtest.FakeService{ExpectedTeamDTO: &team.TeamDTO{}}
|
hs.teamService = &teamtest.FakeService{ExpectedTeamDTO: &team.TeamDTO{}}
|
||||||
@ -311,7 +129,7 @@ func TestTeamAPIEndpoint_GetTeamByID_RBAC(t *testing.T) {
|
|||||||
// Given a team with a user, when the user is granted X permission,
|
// Given a team with a user, when the user is granted X permission,
|
||||||
// Then the endpoint should return 200 if the user has accesscontrol.ActionTeamsWrite with teams:id:1 scope
|
// Then the endpoint should return 200 if the user has accesscontrol.ActionTeamsWrite with teams:id:1 scope
|
||||||
// else return 403
|
// else return 403
|
||||||
func TestTeamAPIEndpoint_UpdateTeam_RBAC(t *testing.T) {
|
func TestTeamAPIEndpoint_UpdateTeam(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = &teamtest.FakeService{ExpectedTeamDTO: &team.TeamDTO{}}
|
hs.teamService = &teamtest.FakeService{ExpectedTeamDTO: &team.TeamDTO{}}
|
||||||
@ -354,7 +172,7 @@ func TestTeamAPIEndpoint_UpdateTeam_RBAC(t *testing.T) {
|
|||||||
// Given a team with a user, when the user is granted X permission,
|
// Given a team with a user, when the user is granted X permission,
|
||||||
// Then the endpoint should return 200 if the user has accesscontrol.ActionTeamsDelete with teams:id:1 scope
|
// Then the endpoint should return 200 if the user has accesscontrol.ActionTeamsDelete with teams:id:1 scope
|
||||||
// else return 403
|
// else return 403
|
||||||
func TestTeamAPIEndpoint_DeleteTeam_RBAC(t *testing.T) {
|
func TestTeamAPIEndpoint_DeleteTeam(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.teamService = &teamtest.FakeService{ExpectedTeamDTO: &team.TeamDTO{}}
|
hs.teamService = &teamtest.FakeService{ExpectedTeamDTO: &team.TeamDTO{}}
|
||||||
@ -388,7 +206,7 @@ func TestTeamAPIEndpoint_DeleteTeam_RBAC(t *testing.T) {
|
|||||||
// Given a team with a user, when the user is granted X permission,
|
// Given a team with a user, when the user is granted X permission,
|
||||||
// Then the endpoint should return 200 if the user has accesscontrol.ActionTeamsRead with teams:id:1 scope
|
// Then the endpoint should return 200 if the user has accesscontrol.ActionTeamsRead with teams:id:1 scope
|
||||||
// else return 403
|
// else return 403
|
||||||
func TestTeamAPIEndpoint_GetTeamPreferences_RBAC(t *testing.T) {
|
func TestTeamAPIEndpoint_GetTeamPreferences(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.preferenceService = &preftest.FakePreferenceService{ExpectedPreference: &pref.Preference{}}
|
hs.preferenceService = &preftest.FakePreferenceService{ExpectedPreference: &pref.Preference{}}
|
||||||
@ -422,7 +240,7 @@ func TestTeamAPIEndpoint_GetTeamPreferences_RBAC(t *testing.T) {
|
|||||||
// Given a team with a user, when the user is granted X permission,
|
// Given a team with a user, when the user is granted X permission,
|
||||||
// Then the endpoint should return 200 if the user has accesscontrol.ActionTeamsWrite with teams:id:1 scope
|
// Then the endpoint should return 200 if the user has accesscontrol.ActionTeamsWrite with teams:id:1 scope
|
||||||
// else return 403
|
// else return 403
|
||||||
func TestTeamAPIEndpoint_UpdateTeamPreferences_RBAC(t *testing.T) {
|
func TestTeamAPIEndpoint_UpdateTeamPreferences(t *testing.T) {
|
||||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
hs.Cfg = setting.NewCfg()
|
hs.Cfg = setting.NewCfg()
|
||||||
hs.preferenceService = &preftest.FakePreferenceService{ExpectedPreference: &pref.Preference{}}
|
hs.preferenceService = &preftest.FakePreferenceService{ExpectedPreference: &pref.Preference{}}
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
||||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||||
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
@ -41,10 +42,11 @@ import (
|
|||||||
func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
||||||
settings := setting.NewCfg()
|
settings := setting.NewCfg()
|
||||||
sqlStore := db.InitTestDB(t)
|
sqlStore := db.InitTestDB(t)
|
||||||
|
sqlStore.Cfg = settings
|
||||||
hs := &HTTPServer{
|
hs := &HTTPServer{
|
||||||
Cfg: settings,
|
Cfg: settings,
|
||||||
SQLStore: sqlStore,
|
SQLStore: sqlStore,
|
||||||
AccessControl: acmock.New(),
|
AccessControl: acimpl.ProvideAccessControl(settings),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockResult := user.SearchUserQueryResult{
|
mockResult := user.SearchUserQueryResult{
|
||||||
@ -56,6 +58,7 @@ func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
|||||||
}
|
}
|
||||||
mock := dbtest.NewFakeDB()
|
mock := dbtest.NewFakeDB()
|
||||||
userMock := usertest.NewUserServiceFake()
|
userMock := usertest.NewUserServiceFake()
|
||||||
|
|
||||||
loggedInUserScenario(t, "When calling GET on", "api/users/1", "api/users/:id", func(sc *scenarioContext) {
|
loggedInUserScenario(t, "When calling GET on", "api/users/1", "api/users/:id", func(sc *scenarioContext) {
|
||||||
fakeNow := time.Date(2019, 2, 11, 17, 30, 40, 0, time.UTC)
|
fakeNow := time.Date(2019, 2, 11, 17, 30, 40, 0, time.UTC)
|
||||||
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
|
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
|
||||||
@ -68,6 +71,7 @@ func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
|||||||
hs.authInfoService = srv
|
hs.authInfoService = srv
|
||||||
orgSvc, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, quotatest.New(false, nil))
|
orgSvc, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, quotatest.New(false, nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, err)
|
||||||
userSvc, err := userimpl.ProvideService(sqlStore, orgSvc, sc.cfg, nil, nil, quotatest.New(false, nil), supportbundlestest.NewFakeBundleService())
|
userSvc, err := userimpl.ProvideService(sqlStore, orgSvc, sc.cfg, nil, nil, quotatest.New(false, nil), supportbundlestest.NewFakeBundleService())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
hs.userService = userSvc
|
hs.userService = userSvc
|
||||||
|
@ -133,9 +133,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/tag"
|
"github.com/grafana/grafana/pkg/services/tag"
|
||||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/teamguardian"
|
|
||||||
teamguardianDatabase "github.com/grafana/grafana/pkg/services/teamguardian/database"
|
|
||||||
teamguardianManager "github.com/grafana/grafana/pkg/services/teamguardian/manager"
|
|
||||||
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||||
"github.com/grafana/grafana/pkg/services/temp_user/tempuserimpl"
|
"github.com/grafana/grafana/pkg/services/temp_user/tempuserimpl"
|
||||||
"github.com/grafana/grafana/pkg/services/updatechecker"
|
"github.com/grafana/grafana/pkg/services/updatechecker"
|
||||||
@ -282,9 +279,6 @@ var wireBasicSet = wire.NewSet(
|
|||||||
serviceaccountsmanager.ProvideServiceAccountsService,
|
serviceaccountsmanager.ProvideServiceAccountsService,
|
||||||
wire.Bind(new(serviceaccounts.Service), new(*serviceaccountsmanager.ServiceAccountsService)),
|
wire.Bind(new(serviceaccounts.Service), new(*serviceaccountsmanager.ServiceAccountsService)),
|
||||||
expr.ProvideService,
|
expr.ProvideService,
|
||||||
teamguardianDatabase.ProvideTeamGuardianStore,
|
|
||||||
wire.Bind(new(teamguardian.Store), new(*teamguardianDatabase.TeamGuardianStoreImpl)),
|
|
||||||
teamguardianManager.ProvideService,
|
|
||||||
featuremgmt.ProvideManagerService,
|
featuremgmt.ProvideManagerService,
|
||||||
featuremgmt.ProvideToggles,
|
featuremgmt.ProvideToggles,
|
||||||
dashboardservice.ProvideDashboardServiceImpl,
|
dashboardservice.ProvideDashboardServiceImpl,
|
||||||
|
@ -58,7 +58,6 @@ type GetTeamByIDQuery struct {
|
|||||||
ID int64
|
ID int64
|
||||||
SignedInUser *user.SignedInUser
|
SignedInUser *user.SignedInUser
|
||||||
HiddenUsers map[string]struct{}
|
HiddenUsers map[string]struct{}
|
||||||
UserIdFilter int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterIgnoreUser is used in a get / search teams query when the caller does not want to filter teams by user ID / membership
|
// FilterIgnoreUser is used in a get / search teams query when the caller does not want to filter teams by user ID / membership
|
||||||
@ -76,7 +75,6 @@ type SearchTeamsQuery struct {
|
|||||||
Limit int
|
Limit int
|
||||||
Page int
|
Page int
|
||||||
OrgID int64 `xorm:"org_id"`
|
OrgID int64 `xorm:"org_id"`
|
||||||
UserIDFilter int64 `xorm:"user_id_filter"`
|
|
||||||
SignedInUser *user.SignedInUser
|
SignedInUser *user.SignedInUser
|
||||||
HiddenUsers map[string]struct{}
|
HiddenUsers map[string]struct{}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ type Service interface {
|
|||||||
UpdateTeamMember(ctx context.Context, cmd *UpdateTeamMemberCommand) error
|
UpdateTeamMember(ctx context.Context, cmd *UpdateTeamMemberCommand) error
|
||||||
IsTeamMember(orgId int64, teamId int64, userId int64) (bool, error)
|
IsTeamMember(orgId int64, teamId int64, userId int64) (bool, error)
|
||||||
RemoveTeamMember(ctx context.Context, cmd *RemoveTeamMemberCommand) error
|
RemoveTeamMember(ctx context.Context, cmd *RemoveTeamMemberCommand) error
|
||||||
|
RemoveUsersMemberships(tx context.Context, userID int64) error
|
||||||
GetUserTeamMemberships(ctx context.Context, orgID, userID int64, external bool) ([]*TeamMemberDTO, error)
|
GetUserTeamMemberships(ctx context.Context, orgID, userID int64, external bool) ([]*TeamMemberDTO, error)
|
||||||
GetTeamMembers(ctx context.Context, query *GetTeamMembersQuery) ([]*TeamMemberDTO, error)
|
GetTeamMembers(ctx context.Context, query *GetTeamMembersQuery) ([]*TeamMemberDTO, error)
|
||||||
IsAdminOfTeams(ctx context.Context, query *IsAdminOfTeamsQuery) (bool, error)
|
IsAdminOfTeams(ctx context.Context, query *IsAdminOfTeamsQuery) (bool, error)
|
||||||
|
@ -23,6 +23,7 @@ type store interface {
|
|||||||
Search(ctx context.Context, query *team.SearchTeamsQuery) (team.SearchTeamQueryResult, error)
|
Search(ctx context.Context, query *team.SearchTeamsQuery) (team.SearchTeamQueryResult, error)
|
||||||
GetByID(ctx context.Context, query *team.GetTeamByIDQuery) (*team.TeamDTO, error)
|
GetByID(ctx context.Context, query *team.GetTeamByIDQuery) (*team.TeamDTO, error)
|
||||||
GetByUser(ctx context.Context, query *team.GetTeamsByUserQuery) ([]*team.TeamDTO, error)
|
GetByUser(ctx context.Context, query *team.GetTeamsByUserQuery) ([]*team.TeamDTO, error)
|
||||||
|
RemoveUsersMemberships(ctx context.Context, userID int64) error
|
||||||
AddMember(userID, orgID, teamID int64, isExternal bool, permission dashboards.PermissionType) error
|
AddMember(userID, orgID, teamID int64, isExternal bool, permission dashboards.PermissionType) error
|
||||||
UpdateMember(ctx context.Context, cmd *team.UpdateTeamMemberCommand) error
|
UpdateMember(ctx context.Context, cmd *team.UpdateTeamMemberCommand) error
|
||||||
IsMember(orgId int64, teamId int64, userId int64) (bool, error)
|
IsMember(orgId int64, teamId int64, userId int64) (bool, error)
|
||||||
@ -76,19 +77,6 @@ func getTeamSelectSQLBase(db db.DB, filteredUsers []string) string {
|
|||||||
` FROM team as team `
|
` FROM team as team `
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTeamSelectWithPermissionsSQLBase(db db.DB, filteredUsers []string) string {
|
|
||||||
return `SELECT
|
|
||||||
team.id AS id,
|
|
||||||
team.uid,
|
|
||||||
team.org_id,
|
|
||||||
team.name AS name,
|
|
||||||
team.email AS email,
|
|
||||||
team_member.permission, ` +
|
|
||||||
getTeamMemberCount(db, filteredUsers) +
|
|
||||||
` FROM team AS team
|
|
||||||
INNER JOIN team_member ON team.id = team_member.team_id AND team_member.user_id = ? `
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *xormStore) Create(name, email string, orgID int64) (team.Team, error) {
|
func (ss *xormStore) Create(name, email string, orgID int64) (team.Team, error) {
|
||||||
t := team.Team{
|
t := team.Team{
|
||||||
UID: util.GenerateShortUID(),
|
UID: util.GenerateShortUID(),
|
||||||
@ -207,13 +195,7 @@ func (ss *xormStore) Search(ctx context.Context, query *team.SearchTeamsQuery) (
|
|||||||
params = append(params, user)
|
params = append(params, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.UserIDFilter == team.FilterIgnoreUser {
|
sql.WriteString(getTeamSelectSQLBase(ss.db, filteredUsers))
|
||||||
sql.WriteString(getTeamSelectSQLBase(ss.db, filteredUsers))
|
|
||||||
} else {
|
|
||||||
sql.WriteString(getTeamSelectWithPermissionsSQLBase(ss.db, filteredUsers))
|
|
||||||
params = append(params, query.UserIDFilter)
|
|
||||||
}
|
|
||||||
|
|
||||||
sql.WriteString(` WHERE team.org_id = ?`)
|
sql.WriteString(` WHERE team.org_id = ?`)
|
||||||
params = append(params, query.OrgID)
|
params = append(params, query.OrgID)
|
||||||
|
|
||||||
@ -227,18 +209,12 @@ func (ss *xormStore) Search(ctx context.Context, query *team.SearchTeamsQuery) (
|
|||||||
params = append(params, query.Name)
|
params = append(params, query.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
acFilter, err := ac.Filter(query.SignedInUser, "team.id", "teams:id:", ac.ActionTeamsRead)
|
||||||
acFilter ac.SQLFilter
|
if err != nil {
|
||||||
err error
|
return err
|
||||||
)
|
|
||||||
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(` and` + acFilter.Where)
|
||||||
|
params = append(params, acFilter.Args...)
|
||||||
|
|
||||||
sql.WriteString(` order by team.name asc`)
|
sql.WriteString(` order by team.name asc`)
|
||||||
|
|
||||||
@ -263,22 +239,8 @@ func (ss *xormStore) Search(ctx context.Context, query *team.SearchTeamsQuery) (
|
|||||||
countSess.Where("name=?", 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 != team.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
|
// Only count teams user can see
|
||||||
if !ac.IsDisabled(ss.cfg) {
|
countSess.Where(acFilter.Where, acFilter.Args...)
|
||||||
countSess.Where(acFilter.Where, acFilter.Args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err := countSess.Count(&t)
|
count, err := countSess.Count(&t)
|
||||||
queryResult.TotalCount = count
|
queryResult.TotalCount = count
|
||||||
@ -303,11 +265,6 @@ func (ss *xormStore) GetByID(ctx context.Context, query *team.GetTeamByIDQuery)
|
|||||||
params = append(params, user)
|
params = append(params, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.UserIdFilter != team.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 = ?`)
|
sql.WriteString(` WHERE team.org_id = ? and team.id = ?`)
|
||||||
params = append(params, query.OrgID, query.ID)
|
params = append(params, query.OrgID, query.ID)
|
||||||
|
|
||||||
@ -343,16 +300,14 @@ func (ss *xormStore) GetByUser(ctx context.Context, query *team.GetTeamsByUserQu
|
|||||||
sql.WriteString(` INNER JOIN team_member on team.id = team_member.team_id`)
|
sql.WriteString(` INNER JOIN team_member on team.id = team_member.team_id`)
|
||||||
sql.WriteString(` WHERE team.org_id = ? and team_member.user_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)
|
||||||
acFilter, err := ac.Filter(query.SignedInUser, "team.id", "teams:id:", ac.ActionTeamsRead)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
sql.WriteString(` and` + acFilter.Where)
|
|
||||||
params = append(params, acFilter.Args...)
|
|
||||||
}
|
}
|
||||||
|
sql.WriteString(` and` + acFilter.Where)
|
||||||
|
params = append(params, acFilter.Args...)
|
||||||
|
|
||||||
err := sess.SQL(sql.String(), params...).Find(&queryResult)
|
err = sess.SQL(sql.String(), params...).Find(&queryResult)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -500,6 +455,16 @@ func removeTeamMember(sess *db.Session, cmd *team.RemoveTeamMemberCommand) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveUsersMemberships removes all the team membership entries for the given user.
|
||||||
|
// Only used when removing a user from a Grafana instance.
|
||||||
|
func (ss *xormStore) RemoveUsersMemberships(ctx context.Context, userID int64) error {
|
||||||
|
return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||||
|
var rawSQL = "DELETE FROM team_member WHERE user_id = ?"
|
||||||
|
_, err := sess.Exec(rawSQL, userID)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserTeamMemberships return a list of memberships to teams granted to a user
|
// 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
|
// If external is specified, only memberships provided by an external auth provider will be listed
|
||||||
// This function doesn't perform any accesscontrol filtering.
|
// This function doesn't perform any accesscontrol filtering.
|
||||||
@ -521,12 +486,10 @@ func (ss *xormStore) GetMembers(ctx context.Context, query *team.GetTeamMembersQ
|
|||||||
// With accesscontrol we filter out users based on the SignedInUser's permissions
|
// 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
|
// 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 the signed in user is not set no member will be returned
|
||||||
if !ac.IsDisabled(ss.cfg) {
|
sqlID := fmt.Sprintf("%s.%s", ss.db.GetDialect().Quote("user"), ss.db.GetDialect().Quote("id"))
|
||||||
sqlID := fmt.Sprintf("%s.%s", ss.db.GetDialect().Quote("user"), ss.db.GetDialect().Quote("id"))
|
*acFilter, err = ac.Filter(query.SignedInUser, sqlID, "users:id:", ac.ActionOrgUsersRead)
|
||||||
*acFilter, err = ac.Filter(query.SignedInUser, sqlID, "users:id:", ac.ActionOrgUsersRead)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ss.getTeamMembers(ctx, query, acFilter)
|
return ss.getTeamMembers(ctx, query, acFilter)
|
||||||
|
@ -369,13 +369,6 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) {
|
|||||||
team1 := searchQueryResult.Teams[0]
|
team1 := searchQueryResult.Teams[0]
|
||||||
require.EqualValues(t, team1.MemberCount, 2)
|
require.EqualValues(t, team1.MemberCount, 2)
|
||||||
|
|
||||||
searchQueryFilteredByUser := &team.SearchTeamsQuery{OrgID: testOrgID, Page: 1, Limit: 10, UserIDFilter: userIds[0], SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
|
||||||
searchQueryFilteredByUserResult, err := teamSvc.SearchTeams(context.Background(), searchQueryFilteredByUser)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, len(searchQueryFilteredByUserResult.Teams), 1)
|
|
||||||
team1 = searchQueryResult.Teams[0]
|
|
||||||
require.EqualValues(t, team1.MemberCount, 2)
|
|
||||||
|
|
||||||
getTeamQuery := &team.GetTeamByIDQuery{OrgID: testOrgID, ID: teamId, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
getTeamQuery := &team.GetTeamByIDQuery{OrgID: testOrgID, ID: teamId, SignedInUser: signedInUser, HiddenUsers: hiddenUsers}
|
||||||
getTeamQueryResult, err := teamSvc.GetTeamByID(context.Background(), getTeamQuery)
|
getTeamQueryResult, err := teamSvc.GetTeamByID(context.Background(), getTeamQuery)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -427,9 +420,9 @@ func TestIntegrationSQLStore_SearchTeams(t *testing.T) {
|
|||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
}
|
}
|
||||||
type searchTeamsTestCase struct {
|
type searchTeamsTestCase struct {
|
||||||
desc string
|
desc string
|
||||||
query *team.SearchTeamsQuery
|
query *team.SearchTeamsQuery
|
||||||
expectedNumUsers int
|
expectedTeamCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []searchTeamsTestCase{
|
tests := []searchTeamsTestCase{
|
||||||
@ -442,7 +435,7 @@ func TestIntegrationSQLStore_SearchTeams(t *testing.T) {
|
|||||||
Permissions: map[int64]map[string][]string{1: {ac.ActionTeamsRead: {ac.ScopeTeamsAll}}},
|
Permissions: map[int64]map[string][]string{1: {ac.ActionTeamsRead: {ac.ScopeTeamsAll}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedNumUsers: 10,
|
expectedTeamCount: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "should return no teams",
|
desc: "should return no teams",
|
||||||
@ -453,7 +446,7 @@ func TestIntegrationSQLStore_SearchTeams(t *testing.T) {
|
|||||||
Permissions: map[int64]map[string][]string{1: {ac.ActionTeamsRead: {""}}},
|
Permissions: map[int64]map[string][]string{1: {ac.ActionTeamsRead: {""}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedNumUsers: 0,
|
expectedTeamCount: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "should return some teams",
|
desc: "should return some teams",
|
||||||
@ -468,7 +461,7 @@ func TestIntegrationSQLStore_SearchTeams(t *testing.T) {
|
|||||||
}}},
|
}}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedNumUsers: 3,
|
expectedTeamCount: 3,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,8 +478,8 @@ func TestIntegrationSQLStore_SearchTeams(t *testing.T) {
|
|||||||
t.Run(tt.desc, func(t *testing.T) {
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
queryResult, err := teamSvc.SearchTeams(context.Background(), tt.query)
|
queryResult, err := teamSvc.SearchTeams(context.Background(), tt.query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, queryResult.Teams, tt.expectedNumUsers)
|
assert.Len(t, queryResult.Teams, tt.expectedTeamCount)
|
||||||
assert.Equal(t, queryResult.TotalCount, int64(tt.expectedNumUsers))
|
assert.Equal(t, queryResult.TotalCount, int64(tt.expectedTeamCount))
|
||||||
|
|
||||||
if !hasWildcardScope(tt.query.SignedInUser, ac.ActionTeamsRead) {
|
if !hasWildcardScope(tt.query.SignedInUser, ac.ActionTeamsRead) {
|
||||||
for _, team := range queryResult.Teams {
|
for _, team := range queryResult.Teams {
|
||||||
|
@ -57,6 +57,10 @@ func (s *Service) RemoveTeamMember(ctx context.Context, cmd *team.RemoveTeamMemb
|
|||||||
return s.store.RemoveMember(ctx, cmd)
|
return s.store.RemoveMember(ctx, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) RemoveUsersMemberships(ctx context.Context, userID int64) error {
|
||||||
|
return s.store.RemoveUsersMemberships(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GetUserTeamMemberships(ctx context.Context, orgID, userID int64, external bool) ([]*team.TeamMemberDTO, error) {
|
func (s *Service) GetUserTeamMemberships(ctx context.Context, orgID, userID int64, external bool) ([]*team.TeamMemberDTO, error) {
|
||||||
return s.store.GetMemberships(ctx, orgID, userID, external)
|
return s.store.GetMemberships(ctx, orgID, userID, external)
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,10 @@ func (s *FakeService) RemoveTeamMember(ctx context.Context, cmd *team.RemoveTeam
|
|||||||
return s.ExpectedError
|
return s.ExpectedError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *FakeService) RemoveUsersMemberships(ctx context.Context, userID int64) error {
|
||||||
|
return s.ExpectedError
|
||||||
|
}
|
||||||
|
|
||||||
func (s *FakeService) GetUserTeamMemberships(ctx context.Context, orgID, userID int64, external bool) ([]*team.TeamMemberDTO, error) {
|
func (s *FakeService) GetUserTeamMemberships(ctx context.Context, orgID, userID int64, external bool) ([]*team.TeamMemberDTO, error) {
|
||||||
return s.ExpectedMembers, s.ExpectedError
|
return s.ExpectedMembers, s.ExpectedError
|
||||||
}
|
}
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TeamGuardianStoreImpl struct {
|
|
||||||
sqlStore db.DB
|
|
||||||
teamService team.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideTeamGuardianStore(sqlStore db.DB, teamService team.Service) *TeamGuardianStoreImpl {
|
|
||||||
return &TeamGuardianStoreImpl{sqlStore: sqlStore, teamService: teamService}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TeamGuardianStoreImpl) GetTeamMembers(ctx context.Context, query team.GetTeamMembersQuery) ([]*team.TeamMemberDTO, error) {
|
|
||||||
queryResult, err := t.teamService.GetTeamMembers(ctx, &query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return queryResult, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TeamGuardianStoreImpl) DeleteByUser(ctx context.Context, userID int64) error {
|
|
||||||
return t.sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
||||||
var rawSQL = "DELETE FROM team_member WHERE user_id = ?"
|
|
||||||
_, err := sess.Exec(rawSQL, userID)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TeamGuardianStoreMock struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TeamGuardianStoreMock) GetTeamMembers(ctx context.Context, query team.GetTeamMembersQuery) ([]*team.TeamMemberDTO, error) {
|
|
||||||
args := t.Called(ctx, query)
|
|
||||||
return args.Get(0).([]*team.TeamMemberDTO), args.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TeamGuardianStoreMock) DeleteByUser(ctx context.Context, userID int64) error {
|
|
||||||
args := t.Called(ctx, userID)
|
|
||||||
return args.Get(0).(error)
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package manager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
"github.com/grafana/grafana/pkg/services/teamguardian"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
store teamguardian.Store
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProvideService(store teamguardian.Store) teamguardian.TeamGuardian {
|
|
||||||
return &Service{store: store}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) CanAdmin(ctx context.Context, orgId int64, teamId int64, user *user.SignedInUser) error {
|
|
||||||
if user.OrgRole == org.RoleAdmin {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.OrgID != orgId {
|
|
||||||
return team.ErrNotAllowedToUpdateTeamInDifferentOrg
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := team.GetTeamMembersQuery{
|
|
||||||
OrgID: orgId,
|
|
||||||
TeamID: teamId,
|
|
||||||
UserID: user.UserID,
|
|
||||||
SignedInUser: user,
|
|
||||||
}
|
|
||||||
|
|
||||||
results, err := s.store.GetTeamMembers(ctx, cmd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, member := range results {
|
|
||||||
if member.UserID == user.UserID && member.Permission == dashboards.PERMISSION_ADMIN {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return team.ErrNotAllowedToUpdateTeam
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) DeleteByUser(ctx context.Context, userID int64) error {
|
|
||||||
return s.store.DeleteByUser(ctx, userID)
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package manager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TeamGuardianMock struct {
|
|
||||||
mock.Mock
|
|
||||||
ExpectedError error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTeamGuardianMock() *TeamGuardianMock {
|
|
||||||
return &TeamGuardianMock{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TeamGuardianMock) CanAdmin(ctx context.Context, orgId int64, teamId int64, user *user.SignedInUser) error {
|
|
||||||
args := t.Called(ctx, orgId, teamId, user)
|
|
||||||
return args.Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TeamGuardianMock) DeleteByUser(ctx context.Context, userID int64) error {
|
|
||||||
return t.ExpectedError
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
package manager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
"github.com/grafana/grafana/pkg/services/teamguardian/database"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUpdateTeam(t *testing.T) {
|
|
||||||
store := new(database.TeamGuardianStoreMock)
|
|
||||||
teamGuardianService := ProvideService(store)
|
|
||||||
|
|
||||||
t.Run("Updating a team", func(t *testing.T) {
|
|
||||||
admin := user.SignedInUser{
|
|
||||||
UserID: 1,
|
|
||||||
OrgID: 1,
|
|
||||||
OrgRole: org.RoleAdmin,
|
|
||||||
}
|
|
||||||
editor := user.SignedInUser{
|
|
||||||
UserID: 2,
|
|
||||||
OrgID: 1,
|
|
||||||
OrgRole: org.RoleEditor,
|
|
||||||
}
|
|
||||||
testTeam := team.Team{
|
|
||||||
ID: 1,
|
|
||||||
OrgID: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Given an editor and a team he isn't a member of", func(t *testing.T) {
|
|
||||||
t.Run("Should not be able to update the team", func(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
store.On("GetTeamMembers", ctx, mock.Anything).Return([]*team.TeamMemberDTO{}, nil).Once()
|
|
||||||
err := teamGuardianService.CanAdmin(ctx, testTeam.OrgID, testTeam.ID, &editor)
|
|
||||||
require.Equal(t, team.ErrNotAllowedToUpdateTeam, err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Given an editor and a team he is an admin in", func(t *testing.T) {
|
|
||||||
t.Run("Should be able to update the team", func(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
result := []*team.TeamMemberDTO{{
|
|
||||||
OrgID: testTeam.OrgID,
|
|
||||||
TeamID: testTeam.ID,
|
|
||||||
UserID: editor.UserID,
|
|
||||||
Permission: dashboards.PERMISSION_ADMIN,
|
|
||||||
}}
|
|
||||||
|
|
||||||
store.On("GetTeamMembers", ctx, mock.Anything).Return(result, nil).Once()
|
|
||||||
err := teamGuardianService.CanAdmin(ctx, testTeam.OrgID, testTeam.ID, &editor)
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Given an editor and a team in another org", func(t *testing.T) {
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
testTeamOtherOrg := team.Team{
|
|
||||||
ID: 1,
|
|
||||||
OrgID: 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Shouldn't be able to update the team", func(t *testing.T) {
|
|
||||||
result := []*team.TeamMemberDTO{{
|
|
||||||
OrgID: testTeamOtherOrg.OrgID,
|
|
||||||
TeamID: testTeamOtherOrg.ID,
|
|
||||||
UserID: editor.UserID,
|
|
||||||
Permission: dashboards.PERMISSION_ADMIN,
|
|
||||||
}}
|
|
||||||
|
|
||||||
store.On("GetTeamMembers", ctx, mock.Anything).Return(result, nil).Once()
|
|
||||||
err := teamGuardianService.CanAdmin(ctx, testTeamOtherOrg.OrgID, testTeamOtherOrg.ID, &editor)
|
|
||||||
require.Equal(t, team.ErrNotAllowedToUpdateTeamInDifferentOrg, err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Given an org admin and a team", func(t *testing.T) {
|
|
||||||
t.Run("Should be able to update the team", func(t *testing.T) {
|
|
||||||
err := teamGuardianService.CanAdmin(context.Background(), testTeam.OrgID, testTeam.ID, &admin)
|
|
||||||
require.NoError(t, err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package teamguardian
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TeamGuardian interface {
|
|
||||||
CanAdmin(context.Context, int64, int64, *user.SignedInUser) error
|
|
||||||
DeleteByUser(context.Context, int64) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type Store interface {
|
|
||||||
GetTeamMembers(context.Context, team.GetTeamMembersQuery) ([]*team.TeamMemberDTO, error)
|
|
||||||
DeleteByUser(context.Context, int64) error
|
|
||||||
}
|
|
Reference in New Issue
Block a user