mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
8f4d924531
commit
b3a10202d4
@ -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 user’s 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 user’s authentication token, or update quotas for all users. |
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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},
|
||||||
|
@ -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,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"},
|
||||||
|
@ -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},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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'} />
|
||||||
)}
|
)}
|
||||||
|
@ -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}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user