Revert "Service accounts: Add service account to teams" (#52710)

* Revert "Service accounts: Add service account to teams (#51536)"

This reverts commit 0f919671e7.

* remove unneeded line

* fix test
This commit is contained in:
Ieva 2022-07-26 09:43:29 +01:00 committed by GitHub
parent 8f4d924531
commit b3a10202d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 151 additions and 410 deletions

View File

@ -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:reader` | `settings:read` | Read Grafana instance settings. |
| `fixed:settings:writer` | All permissions from `fixed:settings:reader` and<br>`settings:write` | Read and update Grafana instance settings. | | `fixed:settings:writer` | All permissions from `fixed:settings:reader` and<br>`settings:write` | Read and update Grafana instance settings. |
| `fixed:stats:reader` | `server.stats:read` | Read Grafana instance statistics. | | `fixed:stats:reader` | `server.stats:read` | Read Grafana instance statistics. |
| `fixed:teams:creator` | `teams:create`<br>`org.users:read`<br>`serviceaccounts:read` | Create a team and list organization users and service accounts (required to manage the created team). | | `fixed:teams:creator` | `teams:create`<br>`org.users:read` | Create a team and list organization users (required to manage the created team). |
| `fixed:teams:writer` | `teams:create`<br>`teams:delete`<br>`teams:read`<br>`teams:write`<br>`teams.permissions:read`<br>`teams.permissions:write` | Create, read, update and delete teams and manage team memberships. | | `fixed:teams:writer` | `teams:create`<br>`teams:delete`<br>`teams:read`<br>`teams:write`<br>`teams.permissions:read`<br>`teams.permissions:write` | Create, read, update and delete teams and manage team memberships. |
| `fixed:users:reader` | `users:read`<br>`users.quotas:read`<br>`users.authtoken:read`<br>` | Read all users and their information, such as team memberships, authentication tokens, and quotas. | | `fixed:users:reader` | `users:read`<br>`users.quotas:read`<br>`users.authtoken:read`<br>` | Read all users and their information, such as team memberships, authentication tokens, and quotas. |
| `fixed:users:writer` | All permissions from `fixed:users:reader` and <br>`users:write`<br>`users:create`<br>`users:delete`<br>`users:enable`<br>`users:disable`<br>`users.password:write`<br>`users.permissions:write`<br>`users:logout`<br>`users.authtoken:write`<br>`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 users authentication token, or update quotas for all users. | | `fixed:users:writer` | All permissions from `fixed:users:reader` and <br>`users:write`<br>`users:create`<br>`users:delete`<br>`users:enable`<br>`users:disable`<br>`users.password:write`<br>`users.permissions:write`<br>`users:logout`<br>`users.authtoken:write`<br>`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 users authentication token, or update quotas for all users. |

View File

@ -230,12 +230,11 @@ func (hs *HTTPServer) declareFixedRoles() error {
Role: ac.RoleDTO{ Role: ac.RoleDTO{
Name: "fixed:teams:creator", Name: "fixed:teams:creator",
DisplayName: "Team 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", Group: "Teams",
Permissions: []ac.Permission{ Permissions: []ac.Permission{
{Action: ac.ActionTeamsCreate}, {Action: ac.ActionTeamsCreate},
{Action: ac.ActionOrgUsersRead, Scope: ac.ScopeUsersAll}, {Action: ac.ActionOrgUsersRead, Scope: ac.ScopeUsersAll},
{Action: serviceaccounts.ActionRead, Scope: serviceaccounts.ScopeAll},
}, },
}, },
Grants: teamCreatorGrants, Grants: teamCreatorGrants,

View File

@ -9,26 +9,24 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions/types" "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/sqlstore"
"github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/services/user"
) )
type flatResourcePermission struct { type flatResourcePermission struct {
ID int64 `xorm:"id"` ID int64 `xorm:"id"`
RoleName string RoleName string
Action string Action string
Scope string Scope string
UserId int64 UserId int64
UserLogin string UserLogin string
UserEmail string UserEmail string
UserIsServiceAccount bool TeamId int64
TeamId int64 TeamEmail string
TeamEmail string Team string
Team string BuiltInRole string
BuiltInRole string Created time.Time
Created time.Time Updated time.Time
Updated time.Time
} }
func (p *flatResourcePermission) IsManaged(scope string) bool { 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, ur.user_id AS user_id,
u.login AS user_login, u.login AS user_login,
u.email AS user_email, u.email AS user_email,
u.is_service_account AS user_is_service_account,
0 AS team_id, 0 AS team_id,
'' AS team, '' AS team,
'' AS team_email, '' AS team_email,
@ -306,7 +303,6 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, or
0 AS user_id, 0 AS user_id,
'' AS user_login, '' AS user_login,
'' AS user_email, '' AS user_email,
false AS user_is_service_account,
tr.team_id AS team_id, tr.team_id AS team_id,
t.name AS team, t.name AS team,
t.email AS team_email, t.email AS team_email,
@ -317,7 +313,6 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, or
0 AS user_id, 0 AS user_id,
'' AS user_login, '' AS user_login,
'' AS user_email, '' AS user_email,
false AS user_is_service_account,
0 as team_id, 0 as team_id,
'' AS team, '' AS team,
'' AS team_email, '' AS team_email,
@ -376,13 +371,8 @@ func (s *AccessControlStore) getResourcePermissions(sess *sqlstore.DBSession, or
if err != nil { if err != nil {
return nil, err return nil, err
} }
serviceAccountFilter, err := accesscontrol.Filter(query.User, "u.id", "serviceaccounts:id:", serviceaccounts.ActionRead) user := userSelect + userFrom + where + " AND " + userFilter.Where
if err != nil {
return nil, err
}
user := userSelect + userFrom + where + " AND (" + userFilter.Where + " OR " + serviceAccountFilter.Where + ") "
args = append(args, userFilter.Args...) args = append(args, userFilter.Args...)
args = append(args, serviceAccountFilter.Args...)
teamFilter, err := accesscontrol.Filter(query.User, "t.id", "teams:id:", accesscontrol.ActionTeamsRead) teamFilter, err := accesscontrol.Filter(query.User, "t.id", "teams:id:", accesscontrol.ActionTeamsRead)
if err != nil { if err != nil {
@ -468,21 +458,20 @@ func flatPermissionsToResourcePermission(scope string, permissions []flatResourc
first := permissions[0] first := permissions[0]
return &accesscontrol.ResourcePermission{ return &accesscontrol.ResourcePermission{
ID: first.ID, ID: first.ID,
RoleName: first.RoleName, RoleName: first.RoleName,
Actions: actions, Actions: actions,
Scope: first.Scope, Scope: first.Scope,
UserId: first.UserId, UserId: first.UserId,
UserLogin: first.UserLogin, UserLogin: first.UserLogin,
UserEmail: first.UserEmail, UserEmail: first.UserEmail,
UserIsServiceAccount: first.UserIsServiceAccount, TeamId: first.TeamId,
TeamId: first.TeamId, TeamEmail: first.TeamEmail,
TeamEmail: first.TeamEmail, Team: first.Team,
Team: first.Team, BuiltInRole: first.BuiltInRole,
BuiltInRole: first.BuiltInRole, Created: first.Created,
Created: first.Created, Updated: first.Updated,
Updated: first.Updated, IsManaged: first.IsManaged(scope),
IsManaged: first.IsManaged(scope),
} }
} }
@ -593,7 +582,6 @@ func (s *AccessControlStore) getResourcePermissionsByIds(sess *sqlstore.DBSessio
ur.user_id AS user_id, ur.user_id AS user_id,
u.login AS user_login, u.login AS user_login,
u.email AS user_email, u.email AS user_email,
u.is_service_account AS user_is_service_account,
tr.team_id AS team_id, tr.team_id AS team_id,
t.name AS team, t.name AS team,
t.email AS team_email, t.email AS team_email,

View File

@ -222,21 +222,20 @@ type ScopeParams struct {
// ResourcePermission is structure that holds all actions that either a team / user / builtin-role // ResourcePermission is structure that holds all actions that either a team / user / builtin-role
// can perform against specific resource. // can perform against specific resource.
type ResourcePermission struct { type ResourcePermission struct {
ID int64 ID int64
RoleName string RoleName string
Actions []string Actions []string
Scope string Scope string
UserId int64 UserId int64
UserLogin string UserLogin string
UserEmail string UserEmail string
UserIsServiceAccount bool TeamId int64
TeamId int64 TeamEmail string
TeamEmail string Team string
Team string BuiltInRole string
BuiltInRole string IsManaged bool
IsManaged bool Created time.Time
Created time.Time Updated time.Time
Updated time.Time
} }
func (p *ResourcePermission) Contains(targetActions []string) bool { func (p *ResourcePermission) Contains(targetActions []string) bool {

View File

@ -59,10 +59,9 @@ func ProvideTeamPermissions(
return nil return nil
}, },
Assignments: resourcepermissions.Assignments{ Assignments: resourcepermissions.Assignments{
Users: true, Users: true,
Teams: false, Teams: false,
BuiltInRoles: false, BuiltInRoles: false,
ServiceAccounts: true,
}, },
PermissionsToActions: map[string][]string{ PermissionsToActions: map[string][]string{
"Member": TeamMemberActions, "Member": TeamMemberActions,
@ -151,10 +150,9 @@ func ProvideDashboardPermissions(
return []string{}, nil return []string{}, nil
}, },
Assignments: resourcepermissions.Assignments{ Assignments: resourcepermissions.Assignments{
Users: true, Users: true,
Teams: true, Teams: true,
BuiltInRoles: true, BuiltInRoles: true,
ServiceAccounts: false,
}, },
PermissionsToActions: map[string][]string{ PermissionsToActions: map[string][]string{
"View": DashboardViewActions, "View": DashboardViewActions,
@ -209,10 +207,9 @@ func ProvideFolderPermissions(
return nil return nil
}, },
Assignments: resourcepermissions.Assignments{ Assignments: resourcepermissions.Assignments{
Users: true, Users: true,
Teams: true, Teams: true,
BuiltInRoles: true, BuiltInRoles: true,
ServiceAccounts: false,
}, },
PermissionsToActions: map[string][]string{ PermissionsToActions: map[string][]string{
"View": append(DashboardViewActions, FolderViewActions...), "View": append(DashboardViewActions, FolderViewActions...),
@ -283,10 +280,9 @@ func ProvideServiceAccountPermissions(
return err return err
}, },
Assignments: resourcepermissions.Assignments{ Assignments: resourcepermissions.Assignments{
Users: true, Users: true,
Teams: true, Teams: true,
BuiltInRoles: false, BuiltInRoles: false,
ServiceAccounts: false,
}, },
PermissionsToActions: map[string][]string{ PermissionsToActions: map[string][]string{
"Edit": {serviceaccounts.ActionRead, serviceaccounts.ActionWrite}, "Edit": {serviceaccounts.ActionRead, serviceaccounts.ActionWrite},

View File

@ -40,7 +40,7 @@ func (a *api) registerEndpoints() {
scope := accesscontrol.Scope(a.service.options.Resource, a.service.options.ResourceAttribute, accesscontrol.Parameter(":resourceID")) 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("/description", auth(disable, accesscontrol.EvalPermission(actionRead)), routing.Wrap(a.getDescription))
r.Get("/:resourceID", inheritanceSolver, auth(disable, accesscontrol.EvalPermission(actionRead, scope)), routing.Wrap(a.getPermissions)) 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)) r.Post("/:resourceID/users/:userID", inheritanceSolver, auth(disable, accesscontrol.EvalPermission(actionWrite, scope)), routing.Wrap(a.setUserPermission))
} }
if a.service.options.Assignments.Teams { if a.service.options.Assignments.Teams {
@ -53,10 +53,9 @@ func (a *api) registerEndpoints() {
} }
type Assignments struct { type Assignments struct {
Users bool `json:"users"` Users bool `json:"users"`
Teams bool `json:"teams"` Teams bool `json:"teams"`
BuiltInRoles bool `json:"builtInRoles"` BuiltInRoles bool `json:"builtInRoles"`
ServiceAccounts bool `json:"serviceAccounts"`
} }
type Description struct { type Description struct {
@ -72,19 +71,18 @@ func (a *api) getDescription(c *models.ReqContext) response.Response {
} }
type resourcePermissionDTO struct { type resourcePermissionDTO struct {
ID int64 `json:"id"` ID int64 `json:"id"`
RoleName string `json:"roleName"` RoleName string `json:"roleName"`
IsManaged bool `json:"isManaged"` IsManaged bool `json:"isManaged"`
UserID int64 `json:"userId,omitempty"` UserID int64 `json:"userId,omitempty"`
UserLogin string `json:"userLogin,omitempty"` UserLogin string `json:"userLogin,omitempty"`
UserAvatarUrl string `json:"userAvatarUrl,omitempty"` UserAvatarUrl string `json:"userAvatarUrl,omitempty"`
UserIsServiceAccount bool `json:"userIsServiceAccount,omitempty"` Team string `json:"team,omitempty"`
Team string `json:"team,omitempty"` TeamID int64 `json:"teamId,omitempty"`
TeamID int64 `json:"teamId,omitempty"` TeamAvatarUrl string `json:"teamAvatarUrl,omitempty"`
TeamAvatarUrl string `json:"teamAvatarUrl,omitempty"` BuiltInRole string `json:"builtInRole,omitempty"`
BuiltInRole string `json:"builtInRole,omitempty"` Actions []string `json:"actions"`
Actions []string `json:"actions"` Permission string `json:"permission"`
Permission string `json:"permission"`
} }
func (a *api) getPermissions(c *models.ReqContext) response.Response { 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{ dto = append(dto, resourcePermissionDTO{
ID: p.ID, ID: p.ID,
RoleName: p.RoleName, RoleName: p.RoleName,
UserID: p.UserId, UserID: p.UserId,
UserLogin: p.UserLogin, UserLogin: p.UserLogin,
UserAvatarUrl: dtos.GetGravatarUrl(p.UserEmail), UserAvatarUrl: dtos.GetGravatarUrl(p.UserEmail),
UserIsServiceAccount: p.UserIsServiceAccount, Team: p.Team,
Team: p.Team, TeamID: p.TeamId,
TeamID: p.TeamId, TeamAvatarUrl: teamAvatarUrl,
TeamAvatarUrl: teamAvatarUrl, BuiltInRole: p.BuiltInRole,
BuiltInRole: p.BuiltInRole, Actions: p.Actions,
Actions: p.Actions, Permission: permission,
Permission: permission, IsManaged: p.IsManaged,
IsManaged: p.IsManaged,
}) })
} }
} }

View File

@ -18,7 +18,6 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey" "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/sqlstore"
"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"
@ -160,12 +159,12 @@ func TestApi_getPermissions(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) { 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) 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) 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) assert.Equal(t, tt.expectedStatus, recorder.Code)
if tt.expectedStatus == http.StatusOK { if tt.expectedStatus == http.StatusOK {
@ -237,14 +236,14 @@ func TestApi_setBuiltinRolePermission(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) { 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) 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) assert.Equal(t, tt.expectedStatus, recorder.Code)
if tt.expectedStatus == http.StatusOK { 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) require.Len(t, permissions, 1)
assert.Equal(t, tt.permission, permissions[0].Permission) assert.Equal(t, tt.permission, permissions[0].Permission)
assert.Equal(t, tt.builtInRole, permissions[0].BuiltInRole) assert.Equal(t, tt.builtInRole, permissions[0].BuiltInRole)
@ -315,19 +314,19 @@ func TestApi_setTeamPermission(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) { 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) server := setupTestServer(t, &models.SignedInUser{OrgId: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}}, service)
// seed team // seed team
_, err := sql.CreateTeam("test", "test@test.com", 1) _, err := sql.CreateTeam("test", "test@test.com", 1)
require.NoError(t, err) 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)
assert.Equal(t, tt.expectedStatus, recorder.Code) assert.Equal(t, tt.expectedStatus, recorder.Code)
if tt.expectedStatus == http.StatusOK { 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) require.Len(t, permissions, 1)
assert.Equal(t, tt.permission, permissions[0].Permission) assert.Equal(t, tt.permission, permissions[0].Permission)
assert.Equal(t, tt.teamID, permissions[0].TeamID) assert.Equal(t, tt.teamID, permissions[0].TeamID)
@ -398,18 +397,19 @@ func TestApi_setUserPermission(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) { 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) server := setupTestServer(t, &models.SignedInUser{OrgId: 1, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}}, service)
// seed user // seed user
_, err := sql.CreateUser(context.Background(), user.CreateUserCommand{Login: "test", OrgID: 1}) _, err := sql.CreateUser(context.Background(), user.CreateUserCommand{Login: "test", OrgID: 1})
require.NoError(t, err) 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)
assert.Equal(t, tt.expectedStatus, recorder.Code)
if tt.expectedStatus == http.StatusOK { 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) require.Len(t, permissions, 1)
assert.Equal(t, tt.permission, permissions[0].Permission) assert.Equal(t, tt.permission, permissions[0].Permission)
assert.Equal(t, tt.userID, permissions[0].UserID) 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 { func setupTestServer(t *testing.T, user *models.SignedInUser, service *Service) *web.Mux {
server := web.New() server := web.New()
server.UseMiddleware(web.Renderer(path.Join(setting.StaticRootPath, "views"), "[[", "]]")) 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", Resource: "dashboards",
ResourceAttribute: "id", ResourceAttribute: "id",
Assignments: Assignments{ Assignments: Assignments{
Users: true, Users: true,
Teams: true, Teams: true,
BuiltInRoles: true, BuiltInRoles: true,
ServiceAccounts: true,
}, },
PermissionsToActions: map[string][]string{ PermissionsToActions: map[string][]string{
"View": {"dashboards:read"}, "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) { 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) req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("/api/access-control/%s/%s", resource, resourceID), nil)
require.NoError(t, err) require.NoError(t, err)

View File

@ -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 { 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 return ErrInvalidAssignment
} }

View File

@ -38,7 +38,7 @@ func TestService_SetUserPermission(t *testing.T) {
t.Run(tt.desc, func(t *testing.T) { t.Run(tt.desc, func(t *testing.T) {
service, sql := setupTestEnvironment(t, []accesscontrol.Permission{}, Options{ service, sql := setupTestEnvironment(t, []accesscontrol.Permission{}, Options{
Resource: "dashboards", Resource: "dashboards",
Assignments: Assignments{Users: true, ServiceAccounts: true}, Assignments: Assignments{Users: true},
PermissionsToActions: nil, PermissionsToActions: nil,
}) })
@ -159,10 +159,9 @@ func TestService_SetPermissions(t *testing.T) {
options: Options{ options: Options{
Resource: "dashboards", Resource: "dashboards",
Assignments: Assignments{ Assignments: Assignments{
Users: true, Users: true,
Teams: true, Teams: true,
BuiltInRoles: true, BuiltInRoles: true,
ServiceAccounts: true,
}, },
PermissionsToActions: map[string][]string{ PermissionsToActions: map[string][]string{
"View": {"dashboards:read"}, "View": {"dashboards:read"},
@ -179,10 +178,9 @@ func TestService_SetPermissions(t *testing.T) {
options: Options{ options: Options{
Resource: "dashboards", Resource: "dashboards",
Assignments: Assignments{ Assignments: Assignments{
Users: true, Users: true,
Teams: true, Teams: true,
BuiltInRoles: true, BuiltInRoles: true,
ServiceAccounts: true,
}, },
PermissionsToActions: map[string][]string{ PermissionsToActions: map[string][]string{
"View": {"dashboards:read"}, "View": {"dashboards:read"},

View File

@ -177,10 +177,12 @@ func TestServiceAccountsAPI_CreateServiceAccount(t *testing.T) {
assert.Equal(t, tc.body["name"], sa.Name) assert.Equal(t, tc.body["name"], sa.Name)
assert.Equal(t, tc.wantID, sa.Login) assert.Equal(t, tc.wantID, sa.Login)
tempUser := &models.SignedInUser{ tempUser := &models.SignedInUser{
OrgId: 1, OrgId: 1,
UserId: 1,
Permissions: map[int64]map[string][]string{ Permissions: map[int64]map[string][]string{
1: { 1: {
serviceaccounts.ActionRead: []string{serviceaccounts.ScopeAll}, serviceaccounts.ActionRead: []string{serviceaccounts.ScopeAll},
accesscontrol.ActionOrgUsersRead: []string{accesscontrol.ScopeUsersAll},
}, },
}, },
} }

View File

@ -9,7 +9,6 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
ac "github.com/grafana/grafana/pkg/services/accesscontrol" ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/serviceaccounts"
) )
type TeamStore interface { 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 the signed in user is not set no member will be returned
if !ac.IsDisabled(ss.Cfg) { if !ac.IsDisabled(ss.Cfg) {
sqlID := fmt.Sprintf("%s.%s", ss.engine.Dialect().Quote("user"), ss.engine.Dialect().Quote("id")) 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) *acFilter, err = ac.Filter(query.SignedInUser, sqlID, "users:id:", ac.ActionOrgUsersRead)
if err != nil { if err != nil {
return err 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) return ss.getTeamMembers(ctx, query, acFilter)
} }
// getTeamMembers return a list of members for the specified team // 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 { return ss.WithDbSession(ctx, func(dbSess *DBSession) error {
query.Result = make([]*models.TeamMemberDTO, 0) query.Result = make([]*models.TeamMemberDTO, 0)
sess := dbSess.Table("team_member") 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")), fmt.Sprintf("team_member.user_id=%s.%s", ss.Dialect.Quote("user"), ss.Dialect.Quote("id")),
) )
if acFilter != nil { // explicitly check for serviceaccounts
sess.Where(acFilter.Where, acFilter.Args...) 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 // Join with only most recent auth module

View File

@ -24,8 +24,9 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) {
OrgId: 1, OrgId: 1,
Permissions: map[int64]map[string][]string{ Permissions: map[int64]map[string][]string{
1: { 1: {
ac.ActionTeamsRead: []string{ac.ScopeTeamsAll}, ac.ActionTeamsRead: []string{ac.ScopeTeamsAll},
ac.ActionOrgUsersRead: []string{ac.ScopeUsersAll}, 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) 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) sqlStore = InitTestDB(t)
setup() 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{ userCmd = user.CreateUserCommand{
Email: fmt.Sprint("sa", 1, "@test.com"), Email: fmt.Sprint("sa", 1, "@test.com"),
Name: fmt.Sprint("sa", 1), Name: fmt.Sprint("sa", 1),
@ -396,23 +385,24 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) {
serviceAccount, err := sqlStore.CreateUser(context.Background(), userCmd) serviceAccount, err := sqlStore.CreateUser(context.Background(), userCmd)
require.NoError(t, err) require.NoError(t, err)
teamId := team1.Id groupId := team2.Id
err = sqlStore.AddTeamMember(serviceAccount.ID, testOrgID, teamId, false, 0) // add service account to team
err = sqlStore.AddTeamMember(serviceAccount.ID, testOrgID, groupId, false, 0)
require.NoError(t, err) require.NoError(t, err)
getTeamMembersQuery := &models.GetTeamMembersQuery{OrgId: testOrgID, TeamId: teamId, SignedInUser: signedInUser} // add user to team
err = sqlStore.GetTeamMembers(context.Background(), getTeamMembersQuery) err = sqlStore.AddTeamMember(userIds[0], testOrgID, groupId, false, 0)
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)
require.NoError(t, err) 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.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") t.Skip("skipping integration test")
} }
testOrgID := int64(2) testOrgID := int64(2)
userIds := make([]int64, 5) userIds := make([]int64, 4)
// Seed 2 teams with 2 members // Seed 2 teams with 2 members
setup := func(store *SQLStore) { 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) team2, errCreateTeam := store.CreateTeam("group2 name", "test2@example.org", testOrgID)
require.NoError(t, errCreateTeam) require.NoError(t, errCreateTeam)
for i := 0; i < 5; i++ { for i := 0; i < 4; i++ {
userCmd := user.CreateUserCommand{ userCmd := user.CreateUserCommand{
Email: fmt.Sprint("user", i, "@example.org"), Email: fmt.Sprint("user", i, "@example.org"),
Name: fmt.Sprint("user", i), Name: fmt.Sprint("user", i),
Login: fmt.Sprint("loginuser", i), Login: fmt.Sprint("loginuser", i),
} }
if i >= 3 {
userCmd.IsServiceAccount = true
}
user, errCreateUser := store.CreateUser(context.Background(), userCmd) user, errCreateUser := store.CreateUser(context.Background(), userCmd)
require.NoError(t, errCreateUser) require.NoError(t, errCreateUser)
userIds[i] = user.ID userIds[i] = user.ID
@ -530,8 +517,6 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) {
require.NoError(t, errAddMember) require.NoError(t, errAddMember)
errAddMember = store.AddTeamMember(userIds[3], testOrgID, team2.Id, false, 0) errAddMember = store.AddTeamMember(userIds[3], testOrgID, team2.Id, false, 0)
require.NoError(t, errAddMember) require.NoError(t, errAddMember)
errAddMember = store.AddTeamMember(userIds[4], testOrgID, team2.Id, false, 0)
require.NoError(t, errAddMember)
} }
store := InitTestDB(t, InitTestDBOpt{}) store := InitTestDB(t, InitTestDBOpt{})
@ -549,14 +534,11 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) {
query: &models.GetTeamMembersQuery{ query: &models.GetTeamMembersQuery{
OrgId: testOrgID, OrgId: testOrgID,
SignedInUser: &models.SignedInUser{ SignedInUser: &models.SignedInUser{
OrgId: testOrgID, OrgId: testOrgID,
Permissions: map[int64]map[string][]string{testOrgID: { Permissions: map[int64]map[string][]string{testOrgID: {ac.ActionOrgUsersRead: {ac.ScopeUsersAll}}},
ac.ActionOrgUsersRead: {ac.ScopeUsersAll},
serviceaccounts.ActionRead: {serviceaccounts.ScopeAll},
}},
}, },
}, },
expectedNumUsers: 5, expectedNumUsers: 4,
}, },
{ {
desc: "should return no team members", desc: "should return no team members",
@ -576,15 +558,11 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) {
OrgId: testOrgID, OrgId: testOrgID,
SignedInUser: &models.SignedInUser{ SignedInUser: &models.SignedInUser{
OrgId: testOrgID, OrgId: testOrgID,
Permissions: map[int64]map[string][]string{testOrgID: { Permissions: map[int64]map[string][]string{testOrgID: {ac.ActionOrgUsersRead: {
ac.ActionOrgUsersRead: { ac.Scope("users", "id", fmt.Sprintf("%d", userIds[0])),
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[2])), ac.Scope("users", "id", fmt.Sprintf("%d", userIds[3])),
}, }}},
serviceaccounts.ActionRead: {
ac.Scope("serviceaccounts", "id", fmt.Sprintf("%d", userIds[3])),
},
}},
}, },
}, },
expectedNumUsers: 3, expectedNumUsers: 3,
@ -598,20 +576,10 @@ func TestIntegrationSQLStore_GetTeamMembers_ACFilter(t *testing.T) {
if !hasWildcardScope(tt.query.SignedInUser, ac.ActionOrgUsersRead) { if !hasWildcardScope(tt.query.SignedInUser, ac.ActionOrgUsersRead) {
for _, member := range tt.query.Result { for _, member := range tt.query.Result {
hasPermission := false assert.Contains(t,
for _, scope := range tt.query.SignedInUser.Permissions[tt.query.SignedInUser.OrgId][ac.ActionOrgUsersRead] { tt.query.SignedInUser.Permissions[tt.query.SignedInUser.OrgId][ac.ActionOrgUsersRead],
if scope == ac.Scope("users", "id", fmt.Sprintf("%d", member.UserId)) { 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)
} }
} }
}) })

View File

@ -2,7 +2,6 @@ import React, { useEffect, useMemo, useState } from 'react';
import { Button, Form, HorizontalGroup, Select } from '@grafana/ui'; import { Button, Form, HorizontalGroup, Select } from '@grafana/ui';
import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; 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 { TeamPicker } from 'app/core/components/Select/TeamPicker';
import { UserPicker } from 'app/core/components/Select/UserPicker'; import { UserPicker } from 'app/core/components/Select/UserPicker';
import { OrgRole } from 'app/types/acl'; import { OrgRole } from 'app/types/acl';
@ -29,9 +28,6 @@ export const AddPermission = ({ title = 'Add Permission For', permissions, assig
if (assignments.users) { if (assignments.users) {
options.push({ value: PermissionTarget.User, label: 'User' }); options.push({ value: PermissionTarget.User, label: 'User' });
} }
if (assignments.serviceAccounts) {
options.push({ value: PermissionTarget.ServiceAccount, label: 'Service Account' });
}
if (assignments.teams) { if (assignments.teams) {
options.push({ value: PermissionTarget.Team, label: 'Team' }); options.push({ value: PermissionTarget.Team, label: 'Team' });
} }
@ -50,7 +46,6 @@ export const AddPermission = ({ title = 'Add Permission For', permissions, assig
const isValid = () => const isValid = () =>
(target === PermissionTarget.Team && teamId > 0) || (target === PermissionTarget.Team && teamId > 0) ||
(target === PermissionTarget.User && userId > 0) || (target === PermissionTarget.User && userId > 0) ||
(target === PermissionTarget.ServiceAccount && userId > 0) ||
(PermissionTarget.BuiltInRole && OrgRole.hasOwnProperty(builtInRole)); (PermissionTarget.BuiltInRole && OrgRole.hasOwnProperty(builtInRole));
return ( return (
@ -77,10 +72,6 @@ export const AddPermission = ({ title = 'Add Permission For', permissions, assig
<UserPicker onSelected={(u) => setUserId(u.value || 0)} className={'width-20'} /> <UserPicker onSelected={(u) => setUserId(u.value || 0)} className={'width-20'} />
)} )}
{target === PermissionTarget.ServiceAccount && (
<ServiceAccountPicker onSelected={(s) => setUserId(s.value?.id || 0)} className={'width-20'} />
)}
{target === PermissionTarget.Team && ( {target === PermissionTarget.Team && (
<TeamPicker onSelected={(t) => setTeamId(t.value?.id || 0)} className={'width-20'} /> <TeamPicker onSelected={(t) => setTeamId(t.value?.id || 0)} className={'width-20'} />
)} )}

View File

@ -16,7 +16,6 @@ const INITIAL_DESCRIPTION: Description = {
assignments: { assignments: {
teams: false, teams: false,
users: false, users: false,
serviceAccounts: false,
builtInRoles: false, builtInRoles: false,
}, },
}; };
@ -58,7 +57,7 @@ export const Permissions = ({
const onAdd = (state: SetPermission) => { const onAdd = (state: SetPermission) => {
let promise: Promise<void> | null = null; let promise: Promise<void> | null = null;
if (state.target === PermissionTarget.User || state.target === PermissionTarget.ServiceAccount) { if (state.target === PermissionTarget.User) {
promise = setUserPermission(resource, resourceId, state.userId!, state.permission); promise = setUserPermission(resource, resourceId, state.userId!, state.permission);
} else if (state.target === PermissionTarget.Team) { } else if (state.target === PermissionTarget.Team) {
promise = setTeamPermission(resource, resourceId, state.teamId!, state.permission); promise = setTeamPermission(resource, resourceId, state.teamId!, state.permission);
@ -110,15 +109,7 @@ export const Permissions = ({
const users = useMemo( const users = useMemo(
() => () =>
sortBy( sortBy(
items.filter((i) => i.userId && !i.userIsServiceAccount), items.filter((i) => i.userId),
['userLogin']
),
[items]
);
const serviceAccounts = useMemo(
() =>
sortBy(
items.filter((i) => i.userId && i.userIsServiceAccount),
['userLogin'] ['userLogin']
), ),
[items] [items]
@ -170,14 +161,6 @@ export const Permissions = ({
onRemove={onRemove} onRemove={onRemove}
canSet={canSetPermissions} canSet={canSetPermissions}
/> />
<PermissionList
title="Service Account"
items={serviceAccounts}
permissionLevels={desc.permissions}
onChange={onChange}
onRemove={onRemove}
canSet={canSetPermissions}
/>
<PermissionList <PermissionList
title="Team" title="Team"
items={teams} items={teams}

View File

@ -5,7 +5,6 @@ export type ResourcePermission = {
userId?: number; userId?: number;
userLogin?: string; userLogin?: string;
userAvatarUrl?: string; userAvatarUrl?: string;
userIsServiceAccount?: boolean;
team?: string; team?: string;
teamId?: number; teamId?: number;
teamAvatarUrl?: string; teamAvatarUrl?: string;
@ -26,7 +25,6 @@ export enum PermissionTarget {
None = 'None', None = 'None',
Team = 'Team', Team = 'Team',
User = 'User', User = 'User',
ServiceAccount = 'Service Account',
BuiltInRole = 'builtInRole', BuiltInRole = 'builtInRole',
} }
export type Description = { export type Description = {
@ -37,6 +35,5 @@ export type Description = {
export type Assignments = { export type Assignments = {
users: boolean; users: boolean;
teams: boolean; teams: boolean;
serviceAccounts: boolean;
builtInRoles: boolean; builtInRoles: boolean;
}; };

View File

@ -1,69 +0,0 @@
import { debounce, isNil } from 'lodash';
import React, { useEffect, useState } from 'react';
import { SelectableValue } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { AsyncSelect } from '@grafana/ui';
import { ServiceAccount } from 'app/types';
export interface Props {
onSelected: (serviceAccount: SelectableValue<ServiceAccount>) => 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<SelectableValue<ServiceAccount>> = 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 (
<AsyncSelect
inputId={inputId}
className={className}
isLoading={loadingServiceAccounts}
defaultOptions={true}
isSearchable={true}
loadOptions={debouncedSearch}
onChange={onSelected}
placeholder="Start typing to search for service accounts"
noOptionsMessage="No service accounts found"
/>
);
}