Chore: restructure legacy store for identity (#92572)
* Restructure user queries * restructure display query * restructure team queries * restructure team bindings query * Restructure team members * Restructure store
This commit is contained in:
1 parent
965a9e2d79
commit
1eb49e1b0f
55 files changed
+375
-349
No files matched your search
@@ -0,0 +1,53 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
type ListDisplayQuery struct {
|
||||
OrgID int64
|
||||
UIDs []string
|
||||
IDs []int64
|
||||
}
|
||||
|
||||
var sqlQueryDisplayTemplate = mustTemplate("display_query.sql")
|
||||
|
||||
func newListDisplay(sql *legacysql.LegacyDatabaseHelper, q *ListDisplayQuery) listDisplayQuery {
|
||||
return listDisplayQuery{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
OrgUserTable: sql.Table("org_user"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
type listDisplayQuery struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListDisplayQuery
|
||||
UserTable string
|
||||
OrgUserTable string
|
||||
}
|
||||
|
||||
func (r listDisplayQuery) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
// GetDisplay implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListDisplay(ctx context.Context, ns claims.NamespaceInfo, query ListDisplayQuery) (*ListUserResult, error) {
|
||||
query.OrgID = ns.OrgID
|
||||
if ns.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero org id")
|
||||
}
|
||||
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.queryUsers(ctx, sql, sqlQueryDisplayTemplate, newListDisplay(sql, &query), 10000)
|
||||
}
|
||||
File renamed without changes.
@@ -1,136 +0,0 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
// Templates setup.
|
||||
var (
|
||||
//go:embed *.sql
|
||||
sqlTemplatesFS embed.FS
|
||||
|
||||
sqlTemplates = template.Must(template.New("sql").ParseFS(sqlTemplatesFS, `*.sql`))
|
||||
)
|
||||
|
||||
func mustTemplate(filename string) *template.Template {
|
||||
if t := sqlTemplates.Lookup(filename); t != nil {
|
||||
return t
|
||||
}
|
||||
panic(fmt.Sprintf("template file not found: %s", filename))
|
||||
}
|
||||
|
||||
// Templates.
|
||||
var (
|
||||
sqlQueryTeams = mustTemplate("query_teams.sql")
|
||||
sqlQueryUsers = mustTemplate("query_users.sql")
|
||||
sqlQueryDisplay = mustTemplate("query_display.sql")
|
||||
sqlQueryTeamBindings = mustTemplate("query_team_bindings.sql")
|
||||
sqlQueryTeamMembers = mustTemplate("query_team_members.sql")
|
||||
)
|
||||
|
||||
type sqlQueryListUsers struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListUserQuery
|
||||
UserTable string
|
||||
OrgUserTable string
|
||||
}
|
||||
|
||||
func newListUser(sql *legacysql.LegacyDatabaseHelper, q *ListUserQuery) sqlQueryListUsers {
|
||||
return sqlQueryListUsers{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
OrgUserTable: sql.Table("org_user"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
func (r sqlQueryListUsers) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
type sqlQueryListTeams struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListTeamQuery
|
||||
TeamTable string
|
||||
}
|
||||
|
||||
func newListTeams(sql *legacysql.LegacyDatabaseHelper, q *ListTeamQuery) sqlQueryListTeams {
|
||||
return sqlQueryListTeams{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
TeamTable: sql.Table("team"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
func (r sqlQueryListTeams) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
type sqlQueryGetDisplay struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *GetUserDisplayQuery
|
||||
UserTable string
|
||||
OrgUserTable string
|
||||
}
|
||||
|
||||
func newGetDisplay(sql *legacysql.LegacyDatabaseHelper, q *GetUserDisplayQuery) sqlQueryGetDisplay {
|
||||
return sqlQueryGetDisplay{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
OrgUserTable: sql.Table("org_user"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
func (r sqlQueryGetDisplay) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
type sqlQueryListTeamBindings struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListTeamBindingsQuery
|
||||
UserTable string
|
||||
TeamTable string
|
||||
TeamMemberTable string
|
||||
}
|
||||
|
||||
func (r sqlQueryListTeamBindings) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func newListTeamBindings(sql *legacysql.LegacyDatabaseHelper, q *ListTeamBindingsQuery) sqlQueryListTeamBindings {
|
||||
return sqlQueryListTeamBindings{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
TeamTable: sql.Table("team"),
|
||||
TeamMemberTable: sql.Table("team_member"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
type sqlQueryListTeamMembers struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListTeamMembersQuery
|
||||
UserTable string
|
||||
TeamTable string
|
||||
TeamMemberTable string
|
||||
}
|
||||
|
||||
func (r sqlQueryListTeamMembers) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func newListTeamMembers(sql *legacysql.LegacyDatabaseHelper, q *ListTeamMembersQuery) sqlQueryListTeamMembers {
|
||||
return sqlQueryListTeamMembers{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
TeamTable: sql.Table("team"),
|
||||
TeamMemberTable: sql.Table("team_member"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
)
|
||||
|
||||
// In every case, RBAC should be applied before calling, or before returning results to the requester
|
||||
type LegacyIdentityStore interface {
|
||||
ListDisplay(ctx context.Context, ns claims.NamespaceInfo, query ListDisplayQuery) (*ListUserResult, error)
|
||||
|
||||
ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error)
|
||||
|
||||
ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error)
|
||||
ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error)
|
||||
ListTeamMembers(ctx context.Context, ns claims.NamespaceInfo, query ListTeamMembersQuery) (*ListTeamMembersResult, error)
|
||||
|
||||
GetUserTeams(ctx context.Context, ns claims.NamespaceInfo, uid string) ([]team.Team, error)
|
||||
}
|
||||
|
||||
var (
|
||||
_ LegacyIdentityStore = (*legacySQLStore)(nil)
|
||||
)
|
||||
|
||||
func NewLegacySQLStores(sql legacysql.LegacyDatabaseProvider) LegacyIdentityStore {
|
||||
return &legacySQLStore{
|
||||
sql: sql,
|
||||
}
|
||||
}
|
||||
|
||||
type legacySQLStore struct {
|
||||
sql legacysql.LegacyDatabaseProvider
|
||||
}
|
||||
|
||||
// Templates setup.
|
||||
var (
|
||||
//go:embed *.sql
|
||||
sqlTemplatesFS embed.FS
|
||||
|
||||
sqlTemplates = template.Must(template.New("sql").ParseFS(sqlTemplatesFS, `*.sql`))
|
||||
)
|
||||
|
||||
func mustTemplate(filename string) *template.Template {
|
||||
if t := sqlTemplates.Lookup(filename); t != nil {
|
||||
return t
|
||||
}
|
||||
panic(fmt.Sprintf("template file not found: %s", filename))
|
||||
}
|
||||
+10
-10
@@ -18,8 +18,8 @@ func TestIdentityQueries(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
getDisplay := func(q *GetUserDisplayQuery) sqltemplate.SQLTemplate {
|
||||
v := newGetDisplay(nodb, q)
|
||||
getDisplay := func(q *ListDisplayQuery) sqltemplate.SQLTemplate {
|
||||
v := newListDisplay(nodb, q)
|
||||
v.SQLTemplate = mocks.NewTestingSQLTemplate()
|
||||
return &v
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func TestIdentityQueries(t *testing.T) {
|
||||
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
|
||||
RootDir: "testdata",
|
||||
Templates: map[*template.Template][]mocks.TemplateTestCase{
|
||||
sqlQueryTeams: {
|
||||
sqlQueryTeamsTemplate: {
|
||||
{
|
||||
Name: "teams_uid",
|
||||
Data: listTeams(&ListTeamQuery{
|
||||
@@ -75,7 +75,7 @@ func TestIdentityQueries(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlQueryUsers: {
|
||||
sqlQueryUsersTemplate: {
|
||||
{
|
||||
Name: "users_uid",
|
||||
Data: listUsers(&ListUserQuery{
|
||||
@@ -99,31 +99,31 @@ func TestIdentityQueries(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlQueryDisplay: {
|
||||
sqlQueryDisplayTemplate: {
|
||||
{
|
||||
Name: "display_uids",
|
||||
Data: getDisplay(&GetUserDisplayQuery{
|
||||
Data: getDisplay(&ListDisplayQuery{
|
||||
OrgID: 2,
|
||||
UIDs: []string{"a", "b"},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "display_ids",
|
||||
Data: getDisplay(&GetUserDisplayQuery{
|
||||
Data: getDisplay(&ListDisplayQuery{
|
||||
OrgID: 2,
|
||||
IDs: []int64{1, 2},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "display_ids_uids",
|
||||
Data: getDisplay(&GetUserDisplayQuery{
|
||||
Data: getDisplay(&ListDisplayQuery{
|
||||
OrgID: 2,
|
||||
UIDs: []string{"a", "b"},
|
||||
IDs: []int64{1, 2},
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlQueryTeamBindings: {
|
||||
sqlQueryTeamBindingsTemplate: {
|
||||
{
|
||||
Name: "team_1_bindings",
|
||||
Data: listTeamBindings(&ListTeamBindingsQuery{
|
||||
@@ -150,7 +150,7 @@ func TestIdentityQueries(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlQueryTeamMembers: {
|
||||
sqlQueryTeamMembersTemplate: {
|
||||
{
|
||||
Name: "team_1_members_page_1",
|
||||
Data: listTeamMembers(&ListTeamMembersQuery{
|
||||
+126
-86
@@ -4,27 +4,47 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
var (
|
||||
_ LegacyIdentityStore = (*legacySQLStore)(nil)
|
||||
)
|
||||
type ListTeamQuery struct {
|
||||
OrgID int64
|
||||
UID string
|
||||
|
||||
func NewLegacySQLStores(sql legacysql.LegacyDatabaseProvider) LegacyIdentityStore {
|
||||
return &legacySQLStore{
|
||||
sql: sql,
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamResult struct {
|
||||
Teams []team.Team
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
var sqlQueryTeamsTemplate = mustTemplate("teams_query.sql")
|
||||
|
||||
type listTeamsQuery struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListTeamQuery
|
||||
TeamTable string
|
||||
}
|
||||
|
||||
func newListTeams(sql *legacysql.LegacyDatabaseHelper, q *ListTeamQuery) listTeamsQuery {
|
||||
return listTeamsQuery{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
TeamTable: sql.Table("team"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
type legacySQLStore struct {
|
||||
sql legacysql.LegacyDatabaseProvider
|
||||
func (r listTeamsQuery) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
// ListTeams implements LegacyIdentityStore.
|
||||
@@ -42,9 +62,9 @@ func (s *legacySQLStore) ListTeams(ctx context.Context, ns claims.NamespaceInfo,
|
||||
}
|
||||
|
||||
req := newListTeams(sql, &query)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeams, req)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeamsTemplate, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeams.Name(), err)
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeamsTemplate.Name(), err)
|
||||
}
|
||||
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
@@ -83,81 +103,65 @@ func (s *legacySQLStore) ListTeams(ctx context.Context, ns claims.NamespaceInfo,
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ListUsers implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error) {
|
||||
// for continue
|
||||
limit := int(query.Pagination.Limit)
|
||||
query.Pagination.Limit += 1
|
||||
|
||||
query.OrgID = ns.OrgID
|
||||
if ns.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero orgID")
|
||||
}
|
||||
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := s.queryUsers(ctx, sql, sqlQueryUsers, newListUser(sql, &query), limit)
|
||||
if err == nil && query.UID != "" {
|
||||
res.RV, err = sql.GetResourceVersion(ctx, "user", "updated")
|
||||
}
|
||||
|
||||
return res, err
|
||||
type ListTeamBindingsQuery struct {
|
||||
// UID is team uid to list bindings for. If not set store should list bindings for all teams
|
||||
UID string
|
||||
OrgID int64
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
// GetDisplay implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) GetDisplay(ctx context.Context, ns claims.NamespaceInfo, query GetUserDisplayQuery) (*ListUserResult, error) {
|
||||
query.OrgID = ns.OrgID
|
||||
if ns.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero orgID")
|
||||
}
|
||||
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.queryUsers(ctx, sql, sqlQueryDisplay, newGetDisplay(sql, &query), 10000)
|
||||
type ListTeamBindingsResult struct {
|
||||
Bindings []TeamBinding
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
func (s *legacySQLStore) queryUsers(ctx context.Context, sql *legacysql.LegacyDatabaseHelper, t *template.Template, req sqltemplate.Args, limit int) (*ListUserResult, error) {
|
||||
q, err := sqltemplate.Execute(t, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryUsers.Name(), err)
|
||||
type TeamMember struct {
|
||||
ID int64
|
||||
TeamID int64
|
||||
TeamUID string
|
||||
UserID int64
|
||||
UserUID string
|
||||
Name string
|
||||
Email string
|
||||
Username string
|
||||
External bool
|
||||
Updated time.Time
|
||||
Created time.Time
|
||||
Permission team.PermissionType
|
||||
}
|
||||
|
||||
func (m TeamMember) MemberID() string {
|
||||
return identity.NewTypedIDString(claims.TypeUser, m.UserUID)
|
||||
}
|
||||
|
||||
type TeamBinding struct {
|
||||
TeamUID string
|
||||
Members []TeamMember
|
||||
}
|
||||
|
||||
var sqlQueryTeamBindingsTemplate = mustTemplate("team_bindings_query.sql")
|
||||
|
||||
type listTeamBindingsQuery struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListTeamBindingsQuery
|
||||
UserTable string
|
||||
TeamTable string
|
||||
TeamMemberTable string
|
||||
}
|
||||
|
||||
func (r listTeamBindingsQuery) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func newListTeamBindings(sql *legacysql.LegacyDatabaseHelper, q *ListTeamBindingsQuery) listTeamBindingsQuery {
|
||||
return listTeamBindingsQuery{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
TeamTable: sql.Table("team"),
|
||||
TeamMemberTable: sql.Table("team_member"),
|
||||
Query: q,
|
||||
}
|
||||
|
||||
res := &ListUserResult{}
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
defer func() {
|
||||
if rows != nil {
|
||||
_ = rows.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
var lastID int64
|
||||
for rows.Next() {
|
||||
u := user.User{}
|
||||
err = rows.Scan(&u.OrgID, &u.ID, &u.UID, &u.Login, &u.Email, &u.Name,
|
||||
&u.Created, &u.Updated, &u.IsServiceAccount, &u.IsDisabled, &u.IsAdmin,
|
||||
)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
lastID = u.ID
|
||||
res.Users = append(res.Users, u)
|
||||
if len(res.Users) > limit {
|
||||
res.Users = res.Users[0 : len(res.Users)-1]
|
||||
res.Continue = lastID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ListTeamsBindings implements LegacyIdentityStore.
|
||||
@@ -175,9 +179,9 @@ func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.Namespa
|
||||
}
|
||||
|
||||
req := newListTeamBindings(sql, &query)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeamBindings, req)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeamBindingsTemplate, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeams.Name(), err)
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeamsTemplate.Name(), err)
|
||||
}
|
||||
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
@@ -233,6 +237,42 @@ func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.Namespa
|
||||
return res, err
|
||||
}
|
||||
|
||||
type ListTeamMembersQuery struct {
|
||||
UID string
|
||||
OrgID int64
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamMembersResult struct {
|
||||
Continue int64
|
||||
Members []TeamMember
|
||||
}
|
||||
|
||||
// Templates.
|
||||
var sqlQueryTeamMembersTemplate = mustTemplate("team_members_query.sql")
|
||||
|
||||
type listTeamMembersQuery struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListTeamMembersQuery
|
||||
UserTable string
|
||||
TeamTable string
|
||||
TeamMemberTable string
|
||||
}
|
||||
|
||||
func (r listTeamMembersQuery) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func newListTeamMembers(sql *legacysql.LegacyDatabaseHelper, q *ListTeamMembersQuery) listTeamMembersQuery {
|
||||
return listTeamMembersQuery{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
TeamTable: sql.Table("team"),
|
||||
TeamMemberTable: sql.Table("team_member"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
// ListTeamMembers implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListTeamMembers(ctx context.Context, ns claims.NamespaceInfo, query ListTeamMembersQuery) (*ListTeamMembersResult, error) {
|
||||
query.Pagination.Limit += 1
|
||||
@@ -247,9 +287,9 @@ func (s *legacySQLStore) ListTeamMembers(ctx context.Context, ns claims.Namespac
|
||||
}
|
||||
|
||||
req := newListTeamMembers(sql, &query)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeamMembers, req)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeamMembersTemplate, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeams.Name(), err)
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeamsTemplate.Name(), err)
|
||||
}
|
||||
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM {{ .Ident .TeamMemberTable }} tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM {{ .Ident .TeamMemberTable }} tm
|
||||
INNER JOIN {{ .Ident .TeamTable }} t ON tm.team_id = t.id
|
||||
INNER JOIN {{ .Ident .UserTable }} u ON tm.user_id = u.id
|
||||
WHERE
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM `grafana`.`team_member` tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM `grafana`.`team_member` tm
|
||||
INNER JOIN `grafana`.`team` t ON tm.team_id = t.id
|
||||
INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM `grafana`.`team_member` tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM `grafana`.`team_member` tm
|
||||
INNER JOIN `grafana`.`team` t ON tm.team_id = t.id
|
||||
INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM `grafana`.`team_member` tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM `grafana`.`team_member` tm
|
||||
INNER JOIN `grafana`.`team` t ON tm.team_id = t.id
|
||||
INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM "grafana"."team_member" tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM "grafana"."team_member" tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM "grafana"."team_member" tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM "grafana"."team_member" tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM "grafana"."team_member" tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission FROM "grafana"."team_member" tm
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,106 +0,0 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
type ListUserQuery struct {
|
||||
OrgID int64
|
||||
UID string
|
||||
IsServiceAccount bool
|
||||
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListUserResult struct {
|
||||
Users []user.User
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
type GetUserDisplayQuery struct {
|
||||
OrgID int64
|
||||
UIDs []string
|
||||
IDs []int64
|
||||
}
|
||||
|
||||
type ListTeamQuery struct {
|
||||
OrgID int64
|
||||
UID string
|
||||
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamResult struct {
|
||||
Teams []team.Team
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
type TeamMember struct {
|
||||
ID int64
|
||||
TeamID int64
|
||||
TeamUID string
|
||||
UserID int64
|
||||
UserUID string
|
||||
Name string
|
||||
Email string
|
||||
Username string
|
||||
External bool
|
||||
Updated time.Time
|
||||
Created time.Time
|
||||
Permission team.PermissionType
|
||||
}
|
||||
|
||||
func (m TeamMember) MemberID() string {
|
||||
return identity.NewTypedIDString(claims.TypeUser, m.UserUID)
|
||||
}
|
||||
|
||||
type TeamBinding struct {
|
||||
TeamUID string
|
||||
Members []TeamMember
|
||||
}
|
||||
|
||||
type ListTeamBindingsQuery struct {
|
||||
// UID is team uid to list bindings for. If not set store should list bindings for all teams
|
||||
UID string
|
||||
OrgID int64
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamBindingsResult struct {
|
||||
Bindings []TeamBinding
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
type ListTeamMembersQuery struct {
|
||||
UID string
|
||||
OrgID int64
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamMembersResult struct {
|
||||
Continue int64
|
||||
Members []TeamMember
|
||||
}
|
||||
|
||||
// In every case, RBAC should be applied before calling, or before returning results to the requester
|
||||
type LegacyIdentityStore interface {
|
||||
GetDisplay(ctx context.Context, ns claims.NamespaceInfo, query GetUserDisplayQuery) (*ListUserResult, error)
|
||||
|
||||
ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error)
|
||||
|
||||
ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error)
|
||||
ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error)
|
||||
ListTeamMembers(ctx context.Context, ns claims.NamespaceInfo, query ListTeamMembersQuery) (*ListTeamMembersResult, error)
|
||||
|
||||
GetUserTeams(ctx context.Context, ns claims.NamespaceInfo, uid string) ([]team.Team, error)
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
)
|
||||
|
||||
type ListUserQuery struct {
|
||||
OrgID int64
|
||||
UID string
|
||||
IsServiceAccount bool
|
||||
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListUserResult struct {
|
||||
Users []user.User
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
var sqlQueryUsersTemplate = mustTemplate("users_query.sql")
|
||||
|
||||
func newListUser(sql *legacysql.LegacyDatabaseHelper, q *ListUserQuery) listUsersQuery {
|
||||
return listUsersQuery{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
OrgUserTable: sql.Table("org_user"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
type listUsersQuery struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListUserQuery
|
||||
UserTable string
|
||||
OrgUserTable string
|
||||
}
|
||||
|
||||
func (r listUsersQuery) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
// ListUsers implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error) {
|
||||
// for continue
|
||||
limit := int(query.Pagination.Limit)
|
||||
query.Pagination.Limit += 1
|
||||
|
||||
query.OrgID = ns.OrgID
|
||||
if ns.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero orgID")
|
||||
}
|
||||
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := s.queryUsers(ctx, sql, sqlQueryUsersTemplate, newListUser(sql, &query), limit)
|
||||
if err == nil && query.UID != "" {
|
||||
res.RV, err = sql.GetResourceVersion(ctx, "user", "updated")
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (s *legacySQLStore) queryUsers(ctx context.Context, sql *legacysql.LegacyDatabaseHelper, t *template.Template, req sqltemplate.Args, limit int) (*ListUserResult, error) {
|
||||
q, err := sqltemplate.Execute(t, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", t.Name(), err)
|
||||
}
|
||||
|
||||
res := &ListUserResult{}
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
defer func() {
|
||||
if rows != nil {
|
||||
_ = rows.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
var lastID int64
|
||||
for rows.Next() {
|
||||
u := user.User{}
|
||||
err = rows.Scan(&u.OrgID, &u.ID, &u.UID, &u.Login, &u.Email, &u.Name,
|
||||
&u.Created, &u.Updated, &u.IsServiceAccount, &u.IsDisabled, &u.IsAdmin,
|
||||
)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
lastID = u.ID
|
||||
res.Users = append(res.Users, u)
|
||||
if len(res.Users) > limit {
|
||||
res.Users = res.Users[0 : len(res.Users)-1]
|
||||
res.Continue = lastID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
File renamed without changes.
@@ -81,7 +81,7 @@ func (r *LegacyDisplayStore) Connect(ctx context.Context, name string, _ runtime
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
keys := parseKeys(req.URL.Query()["key"])
|
||||
users, err := r.store.GetDisplay(ctx, ns, legacy.GetUserDisplayQuery{
|
||||
users, err := r.store.ListDisplay(ctx, ns, legacy.ListDisplayQuery{
|
||||
OrgID: ns.OrgID,
|
||||
UIDs: keys.uids,
|
||||
IDs: keys.ids,
|
||||
|
||||
Reference in new issue
Block a user