From b3a10202d47a5c45fc5a2804f5cce4b63871851c Mon Sep 17 00:00:00 2001 From: Ieva Date: Tue, 26 Jul 2022 09:43:29 +0100 Subject: [PATCH] Revert "Service accounts: Add service account to teams" (#52710) * Revert "Service accounts: Add service account to teams (#51536)" This reverts commit 0f919671e79f5130f8d63a52361beef4b0ae3609. * remove unneeded line * fix test --- .../rbac-fixed-basic-role-definitions.md | 2 +- pkg/api/accesscontrol.go | 3 +- .../database/resource_permissions.go | 68 ++++----- pkg/services/accesscontrol/models.go | 29 ++-- .../ossaccesscontrol/permissions_services.go | 28 ++-- .../accesscontrol/resourcepermissions/api.go | 59 ++++---- .../resourcepermissions/api_test.go | 134 +++--------------- .../resourcepermissions/service.go | 2 +- .../resourcepermissions/service_test.go | 16 +-- pkg/services/serviceaccounts/api/api_test.go | 6 +- pkg/services/sqlstore/team.go | 18 +-- pkg/services/sqlstore/team_test.go | 94 ++++-------- .../AccessControl/AddPermission.tsx | 9 -- .../components/AccessControl/Permissions.tsx | 21 +-- .../core/components/AccessControl/types.ts | 3 - .../Select/ServiceAccountPicker.tsx | 69 --------- 16 files changed, 151 insertions(+), 410 deletions(-) delete mode 100644 public/app/core/components/Select/ServiceAccountPicker.tsx diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions.md index 700aaf98114..ccb0ffebc7c 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions.md @@ -78,7 +78,7 @@ The following tables list permissions associated with basic and fixed roles. | `fixed:settings:reader` | `settings:read` | Read Grafana instance settings. | | `fixed:settings:writer` | All permissions from `fixed:settings:reader` and
`settings:write` | Read and update Grafana instance settings. | | `fixed:stats:reader` | `server.stats:read` | Read Grafana instance statistics. | -| `fixed:teams:creator` | `teams:create`
`org.users:read`
`serviceaccounts:read` | Create a team and list organization users and service accounts (required to manage the created team). | +| `fixed:teams:creator` | `teams:create`
`org.users:read` | Create a team and list organization users (required to manage the created team). | | `fixed:teams:writer` | `teams:create`
`teams:delete`
`teams:read`
`teams:write`
`teams.permissions:read`
`teams.permissions:write` | Create, read, update and delete teams and manage team memberships. | | `fixed:users:reader` | `users:read`
`users.quotas:read`
`users.authtoken:read`
` | Read all users and their information, such as team memberships, authentication tokens, and quotas. | | `fixed:users:writer` | All permissions from `fixed:users:reader` and
`users:write`
`users:create`
`users:delete`
`users:enable`
`users:disable`
`users.password:write`
`users.permissions:write`
`users:logout`
`users.authtoken:write`
`users.quotas:write` | Read and update all attributes and settings for all users in Grafana: update user information, read user information, create or enable or disable a user, make a user a Grafana administrator, sign out a user, update a user’s authentication token, or update quotas for all users. | diff --git a/pkg/api/accesscontrol.go b/pkg/api/accesscontrol.go index 799f83e22fd..e7716d7e2b2 100644 --- a/pkg/api/accesscontrol.go +++ b/pkg/api/accesscontrol.go @@ -230,12 +230,11 @@ func (hs *HTTPServer) declareFixedRoles() error { Role: ac.RoleDTO{ Name: "fixed:teams:creator", DisplayName: "Team creator", - Description: "Create teams and read organisation users and service accounts (required to manage the created teams).", + Description: "Create teams and read organisation users (required to manage the created teams).", Group: "Teams", Permissions: []ac.Permission{ {Action: ac.ActionTeamsCreate}, {Action: ac.ActionOrgUsersRead, Scope: ac.ScopeUsersAll}, - {Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}, }, }, Grants: teamCreatorGrants, diff --git a/pkg/services/accesscontrol/database/resource_permissions.go b/pkg/services/accesscontrol/database/resource_permissions.go index 38b6a8e474a..69034277678 100644 --- a/pkg/services/accesscontrol/database/resource_permissions.go +++ b/pkg/services/accesscontrol/database/resource_permissions.go @@ -9,26 +9,24 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions/types" - "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/user" ) type flatResourcePermission struct { - ID int64 `xorm:"id"` - RoleName string - Action string - Scope string - UserId int64 - UserLogin string - UserEmail string - UserIsServiceAccount bool - TeamId int64 - TeamEmail string - Team string - BuiltInRole string - Created time.Time - Updated time.Time + ID int64 `xorm:"id"` + RoleName string + Action string + Scope string + UserId int64 + UserLogin string + UserEmail string + TeamId int64 + TeamEmail string + Team string + BuiltInRole string + Created time.Time + Updated time.Time } func (p *flatResourcePermission) IsManaged(scope string) bool { @@ -295,7 +293,6 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, or ur.user_id AS user_id, u.login AS user_login, u.email AS user_email, - u.is_service_account AS user_is_service_account, 0 AS team_id, '' AS team, '' AS team_email, @@ -306,7 +303,6 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, or 0 AS user_id, '' AS user_login, '' AS user_email, - false AS user_is_service_account, tr.team_id AS team_id, t.name AS team, t.email AS team_email, @@ -317,7 +313,6 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, or 0 AS user_id, '' AS user_login, '' AS user_email, - false AS user_is_service_account, 0 as team_id, '' AS team, '' AS team_email, @@ -376,13 +371,8 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, or if err != nil { return nil, err } - serviceAccountFilter, err := accesscontrol.Filter(query.User, "u.id", "serviceaccounts:id:", serviceaccounts.ActionRead) - if err != nil { - return nil, err - } - user := userSelect + userFrom + where + " AND (" + userFilter.Where + " OR " + serviceAccountFilter.Where + ") " + user := userSelect + userFrom + where + " AND " + userFilter.Where args = append(args, userFilter.Args...) - args = append(args, serviceAccountFilter.Args...) teamFilter, err := accesscontrol.Filter(query.User, "t.id", "teams:id:", accesscontrol.ActionTeamsRead) if err != nil { @@ -468,21 +458,20 @@ func flatPermissionsToResourcePermission(scope string, permissions []flatResourc first := permissions[0] return &accesscontrol.ResourcePermission{ - ID: first.ID, - RoleName: first.RoleName, - Actions: actions, - Scope: first.Scope, - UserId: first.UserId, - UserLogin: first.UserLogin, - UserEmail: first.UserEmail, - UserIsServiceAccount: first.UserIsServiceAccount, - TeamId: first.TeamId, - TeamEmail: first.TeamEmail, - Team: first.Team, - BuiltInRole: first.BuiltInRole, - Created: first.Created, - Updated: first.Updated, - IsManaged: first.IsManaged(scope), + ID: first.ID, + RoleName: first.RoleName, + Actions: actions, + Scope: first.Scope, + UserId: first.UserId, + UserLogin: first.UserLogin, + UserEmail: first.UserEmail, + TeamId: first.TeamId, + TeamEmail: first.TeamEmail, + Team: first.Team, + BuiltInRole: first.BuiltInRole, + Created: first.Created, + Updated: first.Updated, + IsManaged: first.IsManaged(scope), } } @@ -593,7 +582,6 @@ func (s *AccessControlStore) getResourcePermissionsByIds(sess *sqlstore.DBSessio ur.user_id AS user_id, u.login AS user_login, u.email AS user_email, - u.is_service_account AS user_is_service_account, tr.team_id AS team_id, t.name AS team, t.email AS team_email, diff --git a/pkg/services/accesscontrol/models.go b/pkg/services/accesscontrol/models.go index 85ef4f1188b..0317ea73b52 100644 --- a/pkg/services/accesscontrol/models.go +++ b/pkg/services/accesscontrol/models.go @@ -222,21 +222,20 @@ type ScopeParams struct { // ResourcePermission is structure that holds all actions that either a team / user / builtin-role // can perform against specific resource. type ResourcePermission struct { - ID int64 - RoleName string - Actions []string - Scope string - UserId int64 - UserLogin string - UserEmail string - UserIsServiceAccount bool - TeamId int64 - TeamEmail string - Team string - BuiltInRole string - IsManaged bool - Created time.Time - Updated time.Time + ID int64 + RoleName string + Actions []string + Scope string + UserId int64 + UserLogin string + UserEmail string + TeamId int64 + TeamEmail string + Team string + BuiltInRole string + IsManaged bool + Created time.Time + Updated time.Time } func (p *ResourcePermission) Contains(targetActions []string) bool { diff --git a/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go b/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go index 8bc62f0e590..d33ab1a4c39 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go @@ -59,10 +59,9 @@ func ProvideTeamPermissions( return nil }, Assignments: resourcepermissions.Assignments{ - Users: true, - Teams: false, - BuiltInRoles: false, - ServiceAccounts: true, + Users: true, + Teams: false, + BuiltInRoles: false, }, PermissionsToActions: map[string][]string{ "Member": TeamMemberActions, @@ -151,10 +150,9 @@ func ProvideDashboardPermissions( return []string{}, nil }, Assignments: resourcepermissions.Assignments{ - Users: true, - Teams: true, - BuiltInRoles: true, - ServiceAccounts: false, + Users: true, + Teams: true, + BuiltInRoles: true, }, PermissionsToActions: map[string][]string{ "View": DashboardViewActions, @@ -209,10 +207,9 @@ func ProvideFolderPermissions( return nil }, Assignments: resourcepermissions.Assignments{ - Users: true, - Teams: true, - BuiltInRoles: true, - ServiceAccounts: false, + Users: true, + Teams: true, + BuiltInRoles: true, }, PermissionsToActions: map[string][]string{ "View": append(DashboardViewActions, FolderViewActions...), @@ -283,10 +280,9 @@ func ProvideServiceAccountPermissions( return err }, Assignments: resourcepermissions.Assignments{ - Users: true, - Teams: true, - BuiltInRoles: false, - ServiceAccounts: false, + Users: true, + Teams: true, + BuiltInRoles: false, }, PermissionsToActions: map[string][]string{ "Edit": {serviceaccounts.ActionRead, serviceaccounts.ActionWrite}, diff --git a/pkg/services/accesscontrol/resourcepermissions/api.go b/pkg/services/accesscontrol/resourcepermissions/api.go index c4d8d963ef8..a1892130cfd 100644 --- a/pkg/services/accesscontrol/resourcepermissions/api.go +++ b/pkg/services/accesscontrol/resourcepermissions/api.go @@ -40,7 +40,7 @@ func (a *api) registerEndpoints() { scope := accesscontrol.Scope(a.service.options.Resource, a.service.options.ResourceAttribute, accesscontrol.Parameter(":resourceID")) r.Get("/description", auth(disable, accesscontrol.EvalPermission(actionRead)), routing.Wrap(a.getDescription)) r.Get("/:resourceID", inheritanceSolver, auth(disable, accesscontrol.EvalPermission(actionRead, scope)), routing.Wrap(a.getPermissions)) - if a.service.options.Assignments.Users || a.service.options.Assignments.ServiceAccounts { + if a.service.options.Assignments.Users { r.Post("/:resourceID/users/:userID", inheritanceSolver, auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setUserPermission)) } if a.service.options.Assignments.Teams { @@ -53,10 +53,9 @@ func (a *api) registerEndpoints() { } type Assignments struct { - Users bool `json:"users"` - Teams bool `json:"teams"` - BuiltInRoles bool `json:"builtInRoles"` - ServiceAccounts bool `json:"serviceAccounts"` + Users bool `json:"users"` + Teams bool `json:"teams"` + BuiltInRoles bool `json:"builtInRoles"` } type Description struct { @@ -72,19 +71,18 @@ func (a *api) getDescription(c *models.ReqContext) response.Response { } type resourcePermissionDTO struct { - ID int64 `json:"id"` - RoleName string `json:"roleName"` - IsManaged bool `json:"isManaged"` - UserID int64 `json:"userId,omitempty"` - UserLogin string `json:"userLogin,omitempty"` - UserAvatarUrl string `json:"userAvatarUrl,omitempty"` - UserIsServiceAccount bool `json:"userIsServiceAccount,omitempty"` - Team string `json:"team,omitempty"` - TeamID int64 `json:"teamId,omitempty"` - TeamAvatarUrl string `json:"teamAvatarUrl,omitempty"` - BuiltInRole string `json:"builtInRole,omitempty"` - Actions []string `json:"actions"` - Permission string `json:"permission"` + ID int64 `json:"id"` + RoleName string `json:"roleName"` + IsManaged bool `json:"isManaged"` + UserID int64 `json:"userId,omitempty"` + UserLogin string `json:"userLogin,omitempty"` + UserAvatarUrl string `json:"userAvatarUrl,omitempty"` + Team string `json:"team,omitempty"` + TeamID int64 `json:"teamId,omitempty"` + TeamAvatarUrl string `json:"teamAvatarUrl,omitempty"` + BuiltInRole string `json:"builtInRole,omitempty"` + Actions []string `json:"actions"` + Permission string `json:"permission"` } func (a *api) getPermissions(c *models.ReqContext) response.Response { @@ -112,19 +110,18 @@ func (a *api) getPermissions(c *models.ReqContext) response.Response { } dto = append(dto, resourcePermissionDTO{ - ID: p.ID, - RoleName: p.RoleName, - UserID: p.UserId, - UserLogin: p.UserLogin, - UserAvatarUrl: dtos.GetGravatarUrl(p.UserEmail), - UserIsServiceAccount: p.UserIsServiceAccount, - Team: p.Team, - TeamID: p.TeamId, - TeamAvatarUrl: teamAvatarUrl, - BuiltInRole: p.BuiltInRole, - Actions: p.Actions, - Permission: permission, - IsManaged: p.IsManaged, + ID: p.ID, + RoleName: p.RoleName, + UserID: p.UserId, + UserLogin: p.UserLogin, + UserAvatarUrl: dtos.GetGravatarUrl(p.UserEmail), + Team: p.Team, + TeamID: p.TeamId, + TeamAvatarUrl: teamAvatarUrl, + BuiltInRole: p.BuiltInRole, + Actions: p.Actions, + Permission: permission, + IsManaged: p.IsManaged, }) } } diff --git a/pkg/services/accesscontrol/resourcepermissions/api_test.go b/pkg/services/accesscontrol/resourcepermissions/api_test.go index 2178aa8d0fe..2f32ae41b81 100644 --- a/pkg/services/accesscontrol/resourcepermissions/api_test.go +++ b/pkg/services/accesscontrol/resourcepermissions/api_test.go @@ -18,7 +18,6 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/contexthandler/ctxkey" - "github.com/grafana/grafana/pkg/services/serviceaccounts" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/setting" @@ -160,12 +159,12 @@ func TestApi_getPermissions(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - service, sql := setupTestEnvironment(t, tt.permissions, testOptionsDashboards) + service, sql := setupTestEnvironment(t, tt.permissions, testOptions) server := setupTestServer(t, &models.SignedInUser{OrgId: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}}, service) seedPermissions(t, tt.resourceID, sql, service) - permissions, recorder := getPermission(t, server, testOptionsDashboards.Resource, tt.resourceID) + permissions, recorder := getPermission(t, server, testOptions.Resource, tt.resourceID) assert.Equal(t, tt.expectedStatus, recorder.Code) if tt.expectedStatus == http.StatusOK { @@ -237,14 +236,14 @@ func TestApi_setBuiltinRolePermission(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - service, _ := setupTestEnvironment(t, tt.permissions, testOptionsDashboards) + service, _ := setupTestEnvironment(t, tt.permissions, testOptions) server := setupTestServer(t, &models.SignedInUser{OrgId: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}}, service) - recorder := setPermission(t, server, testOptionsDashboards.Resource, tt.resourceID, tt.permission, "builtInRoles", tt.builtInRole) + recorder := setPermission(t, server, testOptions.Resource, tt.resourceID, tt.permission, "builtInRoles", tt.builtInRole) assert.Equal(t, tt.expectedStatus, recorder.Code) if tt.expectedStatus == http.StatusOK { - permissions, _ := getPermission(t, server, testOptionsDashboards.Resource, tt.resourceID) + permissions, _ := getPermission(t, server, testOptions.Resource, tt.resourceID) require.Len(t, permissions, 1) assert.Equal(t, tt.permission, permissions[0].Permission) assert.Equal(t, tt.builtInRole, permissions[0].BuiltInRole) @@ -315,19 +314,19 @@ func TestApi_setTeamPermission(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - service, sql := setupTestEnvironment(t, tt.permissions, testOptionsDashboards) + service, sql := setupTestEnvironment(t, tt.permissions, testOptions) server := setupTestServer(t, &models.SignedInUser{OrgId: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}}, service) // seed team _, err := sql.CreateTeam("test", "test@test.com", 1) require.NoError(t, err) - recorder := setPermission(t, server, testOptionsDashboards.Resource, tt.resourceID, tt.permission, "teams", strconv.Itoa(int(tt.teamID))) + recorder := setPermission(t, server, testOptions.Resource, tt.resourceID, tt.permission, "teams", strconv.Itoa(int(tt.teamID))) assert.Equal(t, tt.expectedStatus, recorder.Code) assert.Equal(t, tt.expectedStatus, recorder.Code) if tt.expectedStatus == http.StatusOK { - permissions, _ := getPermission(t, server, testOptionsDashboards.Resource, tt.resourceID) + permissions, _ := getPermission(t, server, testOptions.Resource, tt.resourceID) require.Len(t, permissions, 1) assert.Equal(t, tt.permission, permissions[0].Permission) assert.Equal(t, tt.teamID, permissions[0].TeamID) @@ -398,18 +397,19 @@ func TestApi_setUserPermission(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - service, sql := setupTestEnvironment(t, tt.permissions, testOptionsDashboards) + service, sql := setupTestEnvironment(t, tt.permissions, testOptions) server := setupTestServer(t, &models.SignedInUser{OrgId: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}}, service) // seed user _, err := sql.CreateUser(context.Background(), user.CreateUserCommand{Login: "test", OrgID: 1}) require.NoError(t, err) - recorder := setPermission(t, server, testOptionsDashboards.Resource, tt.resourceID, tt.permission, "users", strconv.Itoa(int(tt.userID))) + recorder := setPermission(t, server, testOptions.Resource, tt.resourceID, tt.permission, "users", strconv.Itoa(int(tt.userID))) assert.Equal(t, tt.expectedStatus, recorder.Code) + assert.Equal(t, tt.expectedStatus, recorder.Code) if tt.expectedStatus == http.StatusOK { - permissions, _ := getPermission(t, server, testOptionsDashboards.Resource, tt.resourceID) + permissions, _ := getPermission(t, server, testOptions.Resource, tt.resourceID) require.Len(t, permissions, 1) assert.Equal(t, tt.permission, permissions[0].Permission) assert.Equal(t, tt.userID, permissions[0].UserID) @@ -418,92 +418,6 @@ func TestApi_setUserPermission(t *testing.T) { } } -type setServiceAccountPermissionTestCase struct { - desc string - serviceaccountID int64 - resourceID string - expectedStatus int - permission string - permissions []accesscontrol.Permission -} - -func TestApi_setServiceAccountPermission(t *testing.T) { - tests := []setServiceAccountPermissionTestCase{ - { - desc: "should set Edit permission for serviceaccount 1", - serviceaccountID: 1, - resourceID: "1", - expectedStatus: 200, - permission: "Edit", - permissions: []accesscontrol.Permission{ - {Action: "teams.permissions:read", Scope: "teams:id:1"}, - {Action: "teams.permissions:write", Scope: "teams:id:1"}, - {Action: accesscontrol.ActionTeamsRead, Scope: accesscontrol.ScopeTeamsAll}, - {Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}, - }, - }, - { - desc: "should set View permission for serviceaccount 1", - serviceaccountID: 1, - resourceID: "1", - expectedStatus: 200, - permission: "View", - permissions: []accesscontrol.Permission{ - {Action: "teams.permissions:read", Scope: "teams:id:1"}, - {Action: "teams.permissions:write", Scope: "teams:id:1"}, - {Action: accesscontrol.ActionTeamsRead, Scope: accesscontrol.ScopeTeamsAll}, - {Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}, - }, - }, - { - desc: "should set return http 400 when serviceaccount does not exist", - serviceaccountID: 2, - resourceID: "1", - expectedStatus: http.StatusBadRequest, - permission: "View", - permissions: []accesscontrol.Permission{ - {Action: "teams.permissions:read", Scope: "teams:id:1"}, - {Action: "teams.permissions:write", Scope: "teams:id:1"}, - {Action: accesscontrol.ActionTeamsRead, Scope: accesscontrol.ScopeTeamsAll}, - {Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll}, - }, - }, - { - desc: "should return http 403 when missing permissions", - serviceaccountID: 1, - resourceID: "1", - expectedStatus: http.StatusForbidden, - permission: "View", - permissions: []accesscontrol.Permission{ - {Action: "teams.permissions:read", Scope: "teams:id:1"}, - {Action: accesscontrol.ActionTeamsRead, Scope: accesscontrol.ScopeTeamsAll}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - service, sql := setupTestEnvironment(t, tt.permissions, testOptionsTeams) - server := setupTestServer(t, &models.SignedInUser{OrgId: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}}, service) - - // seed serviceaccount - _, err := sql.CreateUser(context.Background(), user.CreateUserCommand{Login: "test", OrgID: 1, IsServiceAccount: true}) - require.NoError(t, err) - - recorder := setPermission(t, server, testOptionsTeams.Resource, tt.resourceID, tt.permission, "users", strconv.Itoa(int(tt.serviceaccountID))) - assert.Equal(t, tt.expectedStatus, recorder.Code) - - if tt.expectedStatus == http.StatusOK { - permissions, _ := getPermission(t, server, testOptionsTeams.Resource, tt.resourceID) - require.Len(t, permissions, 1) - assert.Equal(t, tt.permission, permissions[0].Permission) - assert.Equal(t, tt.serviceaccountID, permissions[0].UserID) - assert.Equal(t, true, permissions[0].UserIsServiceAccount) - } - }) - } -} - func setupTestServer(t *testing.T, user *models.SignedInUser, service *Service) *web.Mux { server := web.New() server.UseMiddleware(web.Renderer(path.Join(setting.StaticRootPath, "views"), "[[", "]]")) @@ -530,14 +444,13 @@ func contextProvider(tc *testContext) web.Handler { } } -var testOptionsDashboards = Options{ +var testOptions = Options{ Resource: "dashboards", ResourceAttribute: "id", Assignments: Assignments{ - Users: true, - Teams: true, - BuiltInRoles: true, - ServiceAccounts: true, + Users: true, + Teams: true, + BuiltInRoles: true, }, PermissionsToActions: map[string][]string{ "View": {"dashboards:read"}, @@ -545,21 +458,6 @@ var testOptionsDashboards = Options{ }, } -var testOptionsTeams = Options{ - Resource: "teams", - ResourceAttribute: "id", - Assignments: Assignments{ - Users: true, - Teams: true, - BuiltInRoles: true, - ServiceAccounts: true, - }, - PermissionsToActions: map[string][]string{ - "View": {"teams:read"}, - "Edit": {"teams:read", "teams:write", "teams:delete"}, - }, -} - func getPermission(t *testing.T, server *web.Mux, resource, resourceID string) ([]resourcePermissionDTO, *httptest.ResponseRecorder) { req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/api/access-control/%s/%s", resource, resourceID), nil) require.NoError(t, err) diff --git a/pkg/services/accesscontrol/resourcepermissions/service.go b/pkg/services/accesscontrol/resourcepermissions/service.go index 2722dba149a..d8ae4903963 100644 --- a/pkg/services/accesscontrol/resourcepermissions/service.go +++ b/pkg/services/accesscontrol/resourcepermissions/service.go @@ -275,7 +275,7 @@ func (s *Service) validateResource(ctx context.Context, orgID int64, resourceID } func (s *Service) validateUser(ctx context.Context, orgID, userID int64) error { - if !(s.options.Assignments.Users || s.options.Assignments.ServiceAccounts) { + if !s.options.Assignments.Users { return ErrInvalidAssignment } diff --git a/pkg/services/accesscontrol/resourcepermissions/service_test.go b/pkg/services/accesscontrol/resourcepermissions/service_test.go index be8a18c5db8..8775502ab99 100644 --- a/pkg/services/accesscontrol/resourcepermissions/service_test.go +++ b/pkg/services/accesscontrol/resourcepermissions/service_test.go @@ -38,7 +38,7 @@ func TestService_SetUserPermission(t *testing.T) { t.Run(tt.desc, func(t *testing.T) { service, sql := setupTestEnvironment(t, []accesscontrol.Permission{}, Options{ Resource: "dashboards", - Assignments: Assignments{Users: true, ServiceAccounts: true}, + Assignments: Assignments{Users: true}, PermissionsToActions: nil, }) @@ -159,10 +159,9 @@ func TestService_SetPermissions(t *testing.T) { options: Options{ Resource: "dashboards", Assignments: Assignments{ - Users: true, - Teams: true, - BuiltInRoles: true, - ServiceAccounts: true, + Users: true, + Teams: true, + BuiltInRoles: true, }, PermissionsToActions: map[string][]string{ "View": {"dashboards:read"}, @@ -179,10 +178,9 @@ func TestService_SetPermissions(t *testing.T) { options: Options{ Resource: "dashboards", Assignments: Assignments{ - Users: true, - Teams: true, - BuiltInRoles: true, - ServiceAccounts: true, + Users: true, + Teams: true, + BuiltInRoles: true, }, PermissionsToActions: map[string][]string{ "View": {"dashboards:read"}, diff --git a/pkg/services/serviceaccounts/api/api_test.go b/pkg/services/serviceaccounts/api/api_test.go index 7bbe322cc2b..8e136eabdd5 100644 --- a/pkg/services/serviceaccounts/api/api_test.go +++ b/pkg/services/serviceaccounts/api/api_test.go @@ -177,10 +177,12 @@ func TestServiceAccountsAPI_CreateServiceAccount(t *testing.T) { assert.Equal(t, tc.body["name"], sa.Name) assert.Equal(t, tc.wantID, sa.Login) tempUser := &models.SignedInUser{ - OrgId: 1, + OrgId: 1, + UserId: 1, Permissions: map[int64]map[string][]string{ 1: { - serviceaccounts.ActionRead: []string{serviceaccounts.ScopeAll}, + serviceaccounts.ActionRead: []string{serviceaccounts.ScopeAll}, + accesscontrol.ActionOrgUsersRead: []string{accesscontrol.ScopeUsersAll}, }, }, } diff --git a/pkg/services/sqlstore/team.go b/pkg/services/sqlstore/team.go index dce533502c5..b303b031a51 100644 --- a/pkg/services/sqlstore/team.go +++ b/pkg/services/sqlstore/team.go @@ -9,7 +9,6 @@ import ( "github.com/grafana/grafana/pkg/models" ac "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/serviceaccounts" ) type TeamStore interface { @@ -529,25 +528,17 @@ func (ss *SQLStore) GetTeamMembers(ctx context.Context, query *models.GetTeamMem // 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")) - var filter ac.SQLFilter *acFilter, err = ac.Filter(query.SignedInUser, sqlID, "users:id:", ac.ActionOrgUsersRead) if err != nil { return err } - - filter, err = ac.Filter(query.SignedInUser, sqlID, "serviceaccounts:id:", serviceaccounts.ActionRead) - if err != nil { - return err - } - acFilter.Where = fmt.Sprintf("(%s OR %s)", acFilter.Where, filter.Where) - acFilter.Args = append(acFilter.Args, filter.Args...) } 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, acFilter *ac.SQLFilter) error { +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") @@ -555,8 +546,11 @@ func (ss *SQLStore) getTeamMembers(ctx context.Context, query *models.GetTeamMem fmt.Sprintf("team_member.user_id=%s.%s", ss.Dialect.Quote("user"), ss.Dialect.Quote("id")), ) - if acFilter != nil { - sess.Where(acFilter.Where, acFilter.Args...) + // 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 diff --git a/pkg/services/sqlstore/team_test.go b/pkg/services/sqlstore/team_test.go index b84383c3359..04c3c80dfc6 100644 --- a/pkg/services/sqlstore/team_test.go +++ b/pkg/services/sqlstore/team_test.go @@ -24,8 +24,9 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) { OrgId: 1, Permissions: map[int64]map[string][]string{ 1: { - ac.ActionTeamsRead: []string{ac.ScopeTeamsAll}, - ac.ActionOrgUsersRead: []string{ac.ScopeUsersAll}, + ac.ActionTeamsRead: []string{ac.ScopeTeamsAll}, + ac.ActionOrgUsersRead: []string{ac.ScopeUsersAll}, + serviceaccounts.ActionRead: []string{serviceaccounts.ScopeAll}, }, }, } @@ -372,21 +373,9 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) { require.EqualValues(t, getTeamQuery.Result.MemberCount, 2) }) - t.Run("Should be able to add service accounts to teams, list it as a team member and remove it from team", func(t *testing.T) { + t.Run("Should be able to exclude service accounts from teamembers", func(t *testing.T) { sqlStore = InitTestDB(t) setup() - - signedInUser := &models.SignedInUser{ - Login: "loginuser0", - OrgId: testOrgID, - Permissions: map[int64]map[string][]string{ - testOrgID: { - ac.ActionTeamsRead: []string{ac.ScopeTeamsAll}, - ac.ActionOrgUsersRead: []string{ac.ScopeUsersAll}, - }, - }, - } - userCmd = user.CreateUserCommand{ Email: fmt.Sprint("sa", 1, "@test.com"), Name: fmt.Sprint("sa", 1), @@ -396,23 +385,24 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) { serviceAccount, err := sqlStore.CreateUser(context.Background(), userCmd) require.NoError(t, err) - teamId := team1.Id - err = sqlStore.AddTeamMember(serviceAccount.ID, testOrgID, teamId, false, 0) + groupId := team2.Id + // add service account to team + err = sqlStore.AddTeamMember(serviceAccount.ID, testOrgID, groupId, false, 0) require.NoError(t, err) - getTeamMembersQuery := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: teamId, SignedInUser: signedInUser} - err = sqlStore.GetTeamMembers(context.Background(), getTeamMembersQuery) - require.NoError(t, err) - require.EqualValues(t, 1, len(getTeamMembersQuery.Result)) - require.EqualValues(t, serviceAccount.ID, getTeamMembersQuery.Result[0].UserId) - - removeTeamMemberCmd := &models.RemoveTeamMemberCommand{OrgId: testOrgID, TeamId: teamId, UserId: serviceAccount.ID} - err = sqlStore.RemoveTeamMember(context.Background(), removeTeamMemberCmd) + // add user to team + err = sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0) require.NoError(t, err) - err = sqlStore.GetTeamMembers(context.Background(), getTeamMembersQuery) + teamMembersQuery := &models.GetTeamMembersQuery{ + OrgId: testOrgID, + SignedInUser: testUser, + TeamId: groupId, + } + err = sqlStore.GetTeamMembers(context.Background(), teamMembersQuery) require.NoError(t, err) - require.EqualValues(t, 0, len(getTeamMembersQuery.Result)) + // should not receive service account from query + require.Equal(t, len(teamMembersQuery.Result), 1) }) }) }) @@ -499,7 +489,7 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) { t.Skip("skipping integration test") } testOrgID := int64(2) - userIds := make([]int64, 5) + userIds := make([]int64, 4) // Seed 2 teams with 2 members setup := func(store *SQLStore) { @@ -508,15 +498,12 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) { team2, errCreateTeam := store.CreateTeam("group2 name", "test2@example.org", testOrgID) require.NoError(t, errCreateTeam) - for i := 0; i < 5; i++ { + 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), } - if i >= 3 { - userCmd.IsServiceAccount = true - } user, errCreateUser := store.CreateUser(context.Background(), userCmd) require.NoError(t, errCreateUser) userIds[i] = user.ID @@ -530,8 +517,6 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) { require.NoError(t, errAddMember) errAddMember = store.AddTeamMember(userIds[3], testOrgID, team2.Id, false, 0) require.NoError(t, errAddMember) - errAddMember = store.AddTeamMember(userIds[4], testOrgID, team2.Id, false, 0) - require.NoError(t, errAddMember) } store := InitTestDB(t, InitTestDBOpt{}) @@ -549,14 +534,11 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) { query: &models.GetTeamMembersQuery{ OrgId: testOrgID, SignedInUser: &models.SignedInUser{ - OrgId: testOrgID, - Permissions: map[int64]map[string][]string{testOrgID: { - ac.ActionOrgUsersRead: {ac.ScopeUsersAll}, - serviceaccounts.ActionRead: {serviceaccounts.ScopeAll}, - }}, + OrgId: testOrgID, + Permissions: map[int64]map[string][]string{testOrgID: {ac.ActionOrgUsersRead: {ac.ScopeUsersAll}}}, }, }, - expectedNumUsers: 5, + expectedNumUsers: 4, }, { desc: "should return no team members", @@ -576,15 +558,11 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) { OrgId: testOrgID, SignedInUser: &models.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])), - }, - serviceaccounts.ActionRead: { - ac.Scope("serviceaccounts", "id", fmt.Sprintf("%d", userIds[3])), - }, - }}, + 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, @@ -598,20 +576,10 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) { if !hasWildcardScope(tt.query.SignedInUser, ac.ActionOrgUsersRead) { for _, member := range tt.query.Result { - hasPermission := false - for _, scope := range tt.query.SignedInUser.Permissions[tt.query.SignedInUser.OrgId][ac.ActionOrgUsersRead] { - if scope == ac.Scope("users", "id", fmt.Sprintf("%d", member.UserId)) { - hasPermission = true - break - } - } - for _, scope := range tt.query.SignedInUser.Permissions[tt.query.SignedInUser.OrgId][serviceaccounts.ActionRead] { - if scope == ac.Scope("serviceaccounts", "id", fmt.Sprintf("%d", member.UserId)) { - hasPermission = true - break - } - } - assert.True(t, hasPermission) + assert.Contains(t, + tt.query.SignedInUser.Permissions[tt.query.SignedInUser.OrgId][ac.ActionOrgUsersRead], + ac.Scope("users", "id", fmt.Sprintf("%d", member.UserId)), + ) } } }) diff --git a/public/app/core/components/AccessControl/AddPermission.tsx b/public/app/core/components/AccessControl/AddPermission.tsx index 9785b75776a..8c17862ec7a 100644 --- a/public/app/core/components/AccessControl/AddPermission.tsx +++ b/public/app/core/components/AccessControl/AddPermission.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useMemo, useState } from 'react'; import { Button, Form, HorizontalGroup, Select } from '@grafana/ui'; import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; -import { ServiceAccountPicker } from 'app/core/components/Select/ServiceAccountPicker'; import { TeamPicker } from 'app/core/components/Select/TeamPicker'; import { UserPicker } from 'app/core/components/Select/UserPicker'; import { OrgRole } from 'app/types/acl'; @@ -29,9 +28,6 @@ export const AddPermission = ({ title = 'Add Permission For', permissions, assig if (assignments.users) { options.push({ value: PermissionTarget.User, label: 'User' }); } - if (assignments.serviceAccounts) { - options.push({ value: PermissionTarget.ServiceAccount, label: 'Service Account' }); - } if (assignments.teams) { options.push({ value: PermissionTarget.Team, label: 'Team' }); } @@ -50,7 +46,6 @@ export const AddPermission = ({ title = 'Add Permission For', permissions, assig const isValid = () => (target === PermissionTarget.Team && teamId > 0) || (target === PermissionTarget.User && userId > 0) || - (target === PermissionTarget.ServiceAccount && userId > 0) || (PermissionTarget.BuiltInRole && OrgRole.hasOwnProperty(builtInRole)); return ( @@ -77,10 +72,6 @@ export const AddPermission = ({ title = 'Add Permission For', permissions, assig setUserId(u.value || 0)} className={'width-20'} /> )} - {target === PermissionTarget.ServiceAccount && ( - setUserId(s.value?.id || 0)} className={'width-20'} /> - )} - {target === PermissionTarget.Team && ( setTeamId(t.value?.id || 0)} className={'width-20'} /> )} diff --git a/public/app/core/components/AccessControl/Permissions.tsx b/public/app/core/components/AccessControl/Permissions.tsx index 0e8b9955ef1..39446dedd47 100644 --- a/public/app/core/components/AccessControl/Permissions.tsx +++ b/public/app/core/components/AccessControl/Permissions.tsx @@ -16,7 +16,6 @@ const INITIAL_DESCRIPTION: Description = { assignments: { teams: false, users: false, - serviceAccounts: false, builtInRoles: false, }, }; @@ -58,7 +57,7 @@ export const Permissions = ({ const onAdd = (state: SetPermission) => { let promise: Promise | null = null; - if (state.target === PermissionTarget.User || state.target === PermissionTarget.ServiceAccount) { + if (state.target === PermissionTarget.User) { promise = setUserPermission(resource, resourceId, state.userId!, state.permission); } else if (state.target === PermissionTarget.Team) { promise = setTeamPermission(resource, resourceId, state.teamId!, state.permission); @@ -110,15 +109,7 @@ export const Permissions = ({ const users = useMemo( () => sortBy( - items.filter((i) => i.userId && !i.userIsServiceAccount), - ['userLogin'] - ), - [items] - ); - const serviceAccounts = useMemo( - () => - sortBy( - items.filter((i) => i.userId && i.userIsServiceAccount), + items.filter((i) => i.userId), ['userLogin'] ), [items] @@ -170,14 +161,6 @@ export const Permissions = ({ onRemove={onRemove} canSet={canSetPermissions} /> - ) => void; - className?: string; - inputId?: string; - autoFocus?: boolean; -} - -export function ServiceAccountPicker({ onSelected, className, inputId, autoFocus }: Props) { - const [loadingServiceAccounts, setLoadingServiceAccounts] = useState(false); - - // For whatever reason the autoFocus prop doesn't seem to work - // with AsyncSelect, hence this workaround. Maybe fixed in a later version? - useEffect(() => { - if (autoFocus && inputId) { - document.getElementById(inputId)?.focus(); - } - }, [autoFocus, inputId]); - - let search = (query?: string) => { - setLoadingServiceAccounts(true); - - if (isNil(query)) { - query = ''; - } - - return getBackendSrv() - .get(`/api/serviceaccounts/search?query=${query}`) - .then((result: { serviceAccounts: ServiceAccount[] }) => { - const serviceAccounts: Array> = result.serviceAccounts.map((serviceAccount) => { - return { - id: serviceAccount.id, - value: serviceAccount, - label: serviceAccount.login, - imgUrl: serviceAccount.avatarUrl, - login: serviceAccount.login, - }; - }); - setLoadingServiceAccounts(false); - return serviceAccounts; - }); - }; - - const debouncedSearch = debounce(search, 300, { - leading: true, - trailing: true, - }); - - return ( - - ); -}