grafana/pkg/services/accesscontrol/database/database.go
Gabriel MABILLE 729f9a01a0
RBAC: Fix search user permissions (#74729)
Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
2023-09-13 15:19:19 +02:00

237 lines
7.0 KiB
Go

package database
import (
"context"
"strconv"
"strings"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol"
)
func ProvideService(sql db.DB) *AccessControlStore {
return &AccessControlStore{sql}
}
type AccessControlStore struct {
sql db.DB
}
func (s *AccessControlStore) GetUserPermissions(ctx context.Context, query accesscontrol.GetUserPermissionsQuery) ([]accesscontrol.Permission, error) {
result := make([]accesscontrol.Permission, 0)
err := s.sql.WithDbSession(ctx, func(sess *db.Session) error {
if query.UserID == 0 && len(query.TeamIDs) == 0 && len(query.Roles) == 0 {
// no permission to fetch
return nil
}
filter, params := accesscontrol.UserRolesFilter(query.OrgID, query.UserID, query.TeamIDs, query.Roles)
q := `
SELECT
permission.action,
permission.scope
FROM permission
INNER JOIN role ON role.id = permission.role_id
` + filter
if len(query.RolePrefixes) > 0 {
q += " WHERE ( " + strings.Repeat("role.name LIKE ? OR ", len(query.RolePrefixes))
q = q[:len(q)-4] + " )" // remove last " OR "
for i := range query.RolePrefixes {
params = append(params, query.RolePrefixes[i]+"%")
}
}
if err := sess.SQL(q, params...).Find(&result); err != nil {
return err
}
return nil
})
return result, err
}
// SearchUsersPermissions returns the list of user permissions indexed by UserID
func (s *AccessControlStore) SearchUsersPermissions(ctx context.Context, orgID int64, options accesscontrol.SearchOptions) (map[int64][]accesscontrol.Permission, error) {
type UserRBACPermission struct {
UserID int64 `xorm:"user_id"`
Action string `xorm:"action"`
Scope string `xorm:"scope"`
}
dbPerms := make([]UserRBACPermission, 0)
if err := s.sql.WithDbSession(ctx, func(sess *db.Session) error {
// Find permissions
q := `
SELECT
user_id,
action,
scope
FROM (
SELECT ur.user_id, ur.org_id, p.action, p.scope
FROM permission AS p
INNER JOIN user_role AS ur on ur.role_id = p.role_id
UNION ALL
SELECT tm.user_id, tr.org_id, p.action, p.scope
FROM permission AS p
INNER JOIN team_role AS tr ON tr.role_id = p.role_id
INNER JOIN team_member AS tm ON tm.team_id = tr.team_id
UNION ALL
SELECT ou.user_id, ou.org_id, p.action, p.scope
FROM permission AS p
INNER JOIN builtin_role AS br ON br.role_id = p.role_id
INNER JOIN org_user AS ou ON ou.role = br.role
UNION ALL
SELECT sa.user_id, br.org_id, p.action, p.scope
FROM permission AS p
INNER JOIN builtin_role AS br ON br.role_id = p.role_id
INNER JOIN (
SELECT u.id AS user_id
FROM ` + s.sql.GetDialect().Quote("user") + ` AS u WHERE u.is_admin
) AS sa ON 1 = 1
WHERE br.role = ?
) AS up
WHERE (org_id = ? OR org_id = ?)
`
params := []any{accesscontrol.RoleGrafanaAdmin, accesscontrol.GlobalOrgID, orgID}
if options.ActionPrefix != "" {
q += ` AND action LIKE ?`
params = append(params, options.ActionPrefix+"%")
}
if options.Action != "" {
q += ` AND action = ?`
params = append(params, options.Action)
}
if options.Scope != "" {
q += ` AND scope = ?`
params = append(params, options.Scope)
}
if options.UserID != 0 {
q += ` AND user_id = ?`
params = append(params, options.UserID)
}
return sess.SQL(q, params...).
Find(&dbPerms)
}); err != nil {
return nil, err
}
mapped := map[int64][]accesscontrol.Permission{}
for i := range dbPerms {
mapped[dbPerms[i].UserID] = append(mapped[dbPerms[i].UserID], accesscontrol.Permission{Action: dbPerms[i].Action, Scope: dbPerms[i].Scope})
}
return mapped, nil
}
// GetUsersBasicRoles returns the list of user basic roles (Admin, Editor, Viewer, Grafana Admin) indexed by UserID
func (s *AccessControlStore) GetUsersBasicRoles(ctx context.Context, userFilter []int64, orgID int64) (map[int64][]string, error) {
type UserOrgRole struct {
UserID int64 `xorm:"id"`
OrgRole string `xorm:"role"`
IsAdmin bool `xorm:"is_admin"`
}
dbRoles := make([]UserOrgRole, 0)
if err := s.sql.WithDbSession(ctx, func(sess *db.Session) error {
// Find roles
q := `
SELECT u.id, ou.role, u.is_admin
FROM ` + s.sql.GetDialect().Quote("user") + ` AS u
LEFT JOIN org_user AS ou ON u.id = ou.user_id
WHERE (u.is_admin OR ou.org_id = ?)
`
params := []any{orgID}
if len(userFilter) > 0 {
q += "AND u.id IN (?" + strings.Repeat(",?", len(userFilter)-1) + ")"
for _, u := range userFilter {
params = append(params, u)
}
}
return sess.SQL(q, params...).Find(&dbRoles)
}); err != nil {
return nil, err
}
roles := map[int64][]string{}
for i := range dbRoles {
if dbRoles[i].OrgRole != "" {
roles[dbRoles[i].UserID] = []string{dbRoles[i].OrgRole}
}
if dbRoles[i].IsAdmin {
roles[dbRoles[i].UserID] = append(roles[dbRoles[i].UserID], accesscontrol.RoleGrafanaAdmin)
}
}
return roles, nil
}
func (s *AccessControlStore) DeleteUserPermissions(ctx context.Context, orgID, userID int64) error {
err := s.sql.WithDbSession(ctx, func(sess *db.Session) error {
roleDeleteQuery := "DELETE FROM user_role WHERE user_id = ?"
roleDeleteParams := []any{roleDeleteQuery, userID}
if orgID != accesscontrol.GlobalOrgID {
roleDeleteQuery += " AND org_id = ?"
roleDeleteParams = []any{roleDeleteQuery, userID, orgID}
}
// Delete user role assignments
if _, err := sess.Exec(roleDeleteParams...); err != nil {
return err
}
// only delete scopes to user if all permissions is removed (i.e. user is removed)
if orgID == accesscontrol.GlobalOrgID {
// Delete permissions that are scoped to user
if _, err := sess.Exec("DELETE FROM permission WHERE scope = ?", accesscontrol.Scope("users", "id", strconv.FormatInt(userID, 10))); err != nil {
return err
}
}
roleQuery := "SELECT id FROM role WHERE name = ?"
roleParams := []any{accesscontrol.ManagedUserRoleName(userID)}
if orgID != accesscontrol.GlobalOrgID {
roleQuery += " AND org_id = ?"
roleParams = []any{accesscontrol.ManagedUserRoleName(userID), orgID}
}
var roleIDs []int64
if err := sess.SQL(roleQuery, roleParams...).Find(&roleIDs); err != nil {
return err
}
if len(roleIDs) == 0 {
return nil
}
permissionDeleteQuery := "DELETE FROM permission WHERE role_id IN(? " + strings.Repeat(",?", len(roleIDs)-1) + ")"
permissionDeleteParams := make([]any, 0, len(roleIDs)+1)
permissionDeleteParams = append(permissionDeleteParams, permissionDeleteQuery)
for _, id := range roleIDs {
permissionDeleteParams = append(permissionDeleteParams, id)
}
// Delete managed user permissions
if _, err := sess.Exec(permissionDeleteParams...); err != nil {
return err
}
managedRoleDeleteQuery := "DELETE FROM role WHERE id IN(? " + strings.Repeat(",?", len(roleIDs)-1) + ")"
managedRoleDeleteParams := []any{managedRoleDeleteQuery}
for _, id := range roleIDs {
managedRoleDeleteParams = append(managedRoleDeleteParams, id)
}
// Delete managed user roles
if _, err := sess.Exec(managedRoleDeleteParams...); err != nil {
return err
}
return nil
})
return err
}