The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/services/accesscontrol/database/database.go

237 lines
7.0 KiB

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
}