221 lines
5.4 KiB
Go
221 lines
5.4 KiB
Go
package legacy
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"slices"
|
|
"strconv"
|
|
"time"
|
|
|
|
authlib "github.com/grafana/authlib/types"
|
|
preferences "github.com/grafana/grafana/apps/preferences/pkg/apis/preferences/v1alpha1"
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
pref "github.com/grafana/grafana/pkg/services/preference"
|
|
"github.com/grafana/grafana/pkg/storage/legacysql"
|
|
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
|
)
|
|
|
|
type preferenceModel struct {
|
|
ID int64
|
|
OrgID int64
|
|
UserUID sql.NullString
|
|
TeamUID sql.NullString
|
|
JSONData *pref.PreferenceJSONData
|
|
HomeDashboardUID sql.NullString
|
|
Timezone sql.NullString
|
|
Theme sql.NullString
|
|
WeekStart sql.NullString
|
|
Created time.Time
|
|
Updated time.Time
|
|
}
|
|
|
|
type LegacySQL struct {
|
|
db legacysql.LegacyDatabaseProvider
|
|
startup time.Time
|
|
}
|
|
|
|
func NewLegacySQL(db legacysql.LegacyDatabaseProvider) *LegacySQL {
|
|
return &LegacySQL{db: db, startup: time.Now()}
|
|
}
|
|
|
|
// List all defined preferences in an org (valid for admin users only)
|
|
func (s *LegacySQL) listPreferences(ctx context.Context,
|
|
ns string, orgId int64,
|
|
cb func(req *preferencesQuery) (bool, error),
|
|
access func(p *preferenceModel) bool,
|
|
) ([]preferences.Preferences, int64, error) {
|
|
var results []preferences.Preferences
|
|
var rv sql.NullTime
|
|
var max sql.NullString
|
|
|
|
sql, err := s.db(ctx)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
req := newPreferencesQueryReq(sql, orgId)
|
|
needsRV, err := cb(&req)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
q, err := sqltemplate.Execute(sqlPreferencesQuery, req)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("execute template %q: %w", sqlPreferencesQuery.Name(), err)
|
|
}
|
|
|
|
sess := sql.DB.GetSqlxSession()
|
|
rows, err := sess.Query(ctx, q, req.GetArgs()...)
|
|
defer func() {
|
|
if rows != nil {
|
|
_ = rows.Close()
|
|
}
|
|
}()
|
|
|
|
for rows.Next() {
|
|
// SELECT p.id, p.org_id,
|
|
// p.json_data,
|
|
// p.timezone,
|
|
// p.theme,
|
|
// p.week_start,
|
|
// p.home_dashboard_uid,
|
|
// u.uid as user_uid,
|
|
// t.uid as team_uid,
|
|
// p.created, p.updated
|
|
|
|
pref := preferenceModel{}
|
|
err := rows.Scan(&pref.ID, &pref.OrgID,
|
|
&pref.JSONData,
|
|
&pref.Timezone,
|
|
&pref.Theme,
|
|
&pref.WeekStart,
|
|
&pref.HomeDashboardUID,
|
|
&pref.UserUID, &pref.TeamUID,
|
|
&pref.Created, &pref.Updated)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if pref.Updated.After(rv.Time) {
|
|
rv.Time = pref.Updated
|
|
}
|
|
if !access(&pref) {
|
|
continue // user does not have access
|
|
}
|
|
results = append(results, asPreferencesResource(ns, &pref))
|
|
}
|
|
|
|
if needsRV {
|
|
req.Reset()
|
|
q, err = sqltemplate.Execute(sqlPreferencesRV, req)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("execute template %q: %w", sqlPreferencesRV.Name(), err)
|
|
}
|
|
err = sess.Get(ctx, &max, q)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("unable to get RV %w", err)
|
|
}
|
|
if max.Valid && max.String != "" {
|
|
t, _ := time.Parse(time.RFC3339, max.String)
|
|
if !t.IsZero() {
|
|
rv.Time = t
|
|
}
|
|
} else {
|
|
rv.Time = s.startup
|
|
}
|
|
}
|
|
return results, rv.Time.UnixMilli(), err
|
|
}
|
|
|
|
func (s *LegacySQL) ListPreferences(ctx context.Context, ns string, user identity.Requester, needsRV bool) (*preferences.PreferencesList, error) {
|
|
if ns == "" {
|
|
return nil, fmt.Errorf("namespace is required")
|
|
}
|
|
|
|
info, err := authlib.ParseNamespace(ns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// when the user is nil, it is actually admin and can see everything
|
|
var teams []string
|
|
found, rv, err := s.listPreferences(ctx, ns, info.OrgID,
|
|
func(req *preferencesQuery) (bool, error) {
|
|
if user != nil {
|
|
req.UserUID = user.GetIdentifier()
|
|
teams, err = s.GetTeams(ctx, &identity.StaticRequester{
|
|
OrgID: info.OrgID,
|
|
UserUID: req.UserUID,
|
|
}, false)
|
|
req.UserTeams = teams
|
|
}
|
|
return needsRV, err
|
|
},
|
|
func(p *preferenceModel) bool {
|
|
if user == nil || user.GetIsGrafanaAdmin() {
|
|
return true
|
|
}
|
|
if p.UserUID.String != "" {
|
|
return user.GetIdentifier() == p.UserUID.String
|
|
}
|
|
if p.TeamUID.String != "" {
|
|
return slices.Contains(teams, p.TeamUID.String)
|
|
}
|
|
return true
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
list := &preferences.PreferencesList{
|
|
Items: found,
|
|
}
|
|
if rv > 0 {
|
|
list.ResourceVersion = strconv.FormatInt(rv, 10)
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
func (s *LegacySQL) InTeam(ctx context.Context, id authlib.AuthInfo, team string, admin bool) (bool, error) {
|
|
// Could be faster, but find for now
|
|
teams, err := s.GetTeams(ctx, id, admin)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return slices.Contains(teams, team), nil
|
|
}
|
|
|
|
func (s *LegacySQL) GetTeams(ctx context.Context, id authlib.AuthInfo, admin bool) ([]string, error) {
|
|
sql, err := s.db(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
xid, ok := id.(identity.Requester)
|
|
if !ok {
|
|
return nil, fmt.Errorf("expected identity.Requester")
|
|
}
|
|
req := newTeamsQueryReq(sql, xid.GetOrgID(), id.GetUID(), admin)
|
|
|
|
q, err := sqltemplate.Execute(sqlTeams, req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("execute template %q: %w", sqlTeams.Name(), err)
|
|
}
|
|
teams := []string{}
|
|
sess := sql.DB.GetSqlxSession()
|
|
err = sess.Select(ctx, &teams, q, req.GetArgs()...)
|
|
return teams, err
|
|
}
|
|
|
|
func (s *LegacySQL) getLegacyTeamID(ctx context.Context, orgId int64, team string) (int64, error) {
|
|
sql, err := s.db(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var id int64
|
|
sess := sql.DB.GetSqlxSession()
|
|
err = sess.Select(ctx, &id, "SELECT id FROM team WHERE org_id=? AND uid=?", orgId, team)
|
|
return id, err
|
|
}
|