mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
965a9e2d79
commit
1eb49e1b0f
53
pkg/registry/apis/identity/legacy/display.go
Normal file
53
pkg/registry/apis/identity/legacy/display.go
Normal file
@ -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)
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
54
pkg/registry/apis/identity/legacy/sql.go
Normal file
54
pkg/registry/apis/identity/legacy/sql.go
Normal file
@ -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))
|
||||
}
|
@ -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{
|
@ -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()...)
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
||||
}
|
111
pkg/registry/apis/identity/legacy/user.go
Normal file
111
pkg/registry/apis/identity/legacy/user.go
Normal file
@ -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
|
||||
}
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user