mirror of https://github.com/grafana/grafana
Chore: restructure legacy store for identity (#92572)
* Restructure user queries * restructure display query * restructure team queries * restructure team bindings query * Restructure team members * Restructure storepull/92585/head
parent
965a9e2d79
commit
1eb49e1b0f
@ -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, |
||||
} |
||||
} |
@ -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)) |
||||
} |
@ -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) |
||||
} |
@ -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 |
||||
} |
Loading…
Reference in new issue