* implement the delete method for team bindings * add integration test * remove team binding search from legacy store
431 lines
10 KiB
Go
431 lines
10 KiB
Go
package legacy
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
claims "github.com/grafana/authlib/types"
|
|
"github.com/grafana/grafana/pkg/registry/apis/iam/common"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/session"
|
|
"github.com/grafana/grafana/pkg/services/team"
|
|
"github.com/grafana/grafana/pkg/storage/legacysql"
|
|
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
|
)
|
|
|
|
type ListTeamBindingsQuery struct {
|
|
UID string
|
|
OrgID int64
|
|
Pagination common.Pagination
|
|
}
|
|
|
|
type ListTeamBindingsResult struct {
|
|
Bindings []TeamMember
|
|
Continue int64
|
|
RV int64
|
|
}
|
|
|
|
type TeamMember struct {
|
|
ID int64
|
|
UID string
|
|
TeamID int64
|
|
TeamUID string
|
|
UserID int64
|
|
UserUID string
|
|
OrgID int64
|
|
Name string
|
|
Email string
|
|
Username string
|
|
External bool
|
|
Updated time.Time
|
|
Created time.Time
|
|
Permission team.PermissionType
|
|
}
|
|
|
|
func (m TeamMember) MemberID() string {
|
|
return claims.NewTypeID(claims.TypeUser, m.UserUID)
|
|
}
|
|
|
|
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,
|
|
}
|
|
}
|
|
|
|
func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error) {
|
|
// for continue
|
|
query.Pagination.Limit += 1
|
|
query.OrgID = ns.OrgID
|
|
if query.OrgID == 0 {
|
|
return nil, fmt.Errorf("expected non zero orgID")
|
|
}
|
|
|
|
sql, err := s.sql(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req := newListTeamBindings(sql, &query)
|
|
q, err := sqltemplate.Execute(sqlQueryTeamBindingsTemplate, req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeamBindingsTemplate.Name(), err)
|
|
}
|
|
|
|
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
|
defer func() {
|
|
if rows != nil {
|
|
_ = rows.Close()
|
|
}
|
|
}()
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := &ListTeamBindingsResult{
|
|
Bindings: make([]TeamMember, 0, int(query.Pagination.Limit)),
|
|
}
|
|
|
|
var lastID int64
|
|
|
|
for rows.Next() {
|
|
m := TeamMember{}
|
|
err = rows.Scan(&m.ID, &m.UID, &m.TeamUID, &m.TeamID, &m.UserUID, &m.UserID, &m.Created, &m.Updated, &m.Permission, &m.External)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
|
|
res.Bindings = append(res.Bindings, m)
|
|
|
|
lastID = m.ID
|
|
|
|
if len(res.Bindings) >= int(query.Pagination.Limit)-1 {
|
|
res.Continue = lastID
|
|
}
|
|
}
|
|
|
|
return res, err
|
|
}
|
|
|
|
type CreateTeamMemberCommand struct {
|
|
UID string
|
|
TeamID int64
|
|
TeamUID string
|
|
UserID int64
|
|
UserUID string
|
|
OrgID int64
|
|
Created legacysql.DBTime
|
|
Updated legacysql.DBTime
|
|
External bool
|
|
Permission team.PermissionType
|
|
}
|
|
|
|
type CreateTeamMemberResult struct {
|
|
TeamMember TeamMember
|
|
}
|
|
|
|
var sqlCreateTeamMemberQuery = mustTemplate("create_team_member_query.sql")
|
|
|
|
func newCreateTeamMember(sql *legacysql.LegacyDatabaseHelper, cmd *CreateTeamMemberCommand) createTeamMemberQuery {
|
|
return createTeamMemberQuery{
|
|
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
|
TeamMemberTable: sql.Table("team_member"),
|
|
Command: cmd,
|
|
}
|
|
}
|
|
|
|
type createTeamMemberQuery struct {
|
|
sqltemplate.SQLTemplate
|
|
TeamMemberTable string
|
|
Command *CreateTeamMemberCommand
|
|
}
|
|
|
|
func (r createTeamMemberQuery) Validate() error {
|
|
return nil
|
|
}
|
|
|
|
func (s *legacySQLStore) CreateTeamMember(ctx context.Context, ns claims.NamespaceInfo, cmd CreateTeamMemberCommand) (*CreateTeamMemberResult, error) {
|
|
now := time.Now().UTC()
|
|
cmd.Created = legacysql.NewDBTime(now)
|
|
cmd.Updated = legacysql.NewDBTime(now)
|
|
cmd.OrgID = ns.OrgID
|
|
|
|
if cmd.OrgID == 0 {
|
|
return nil, fmt.Errorf("expected non zero org id")
|
|
}
|
|
|
|
sql, err := s.sql(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req := newCreateTeamMember(sql, &cmd)
|
|
|
|
var createdTeamMember TeamMember
|
|
err = sql.DB.GetSqlxSession().WithTransaction(ctx, func(st *session.SessionTx) error {
|
|
teamMemberQuery, err := sqltemplate.Execute(sqlCreateTeamMemberQuery, req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute team member template %q: %w", sqlCreateTeamMemberQuery.Name(), err)
|
|
}
|
|
|
|
teamMemberID, err := st.ExecWithReturningId(ctx, teamMemberQuery, req.GetArgs()...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create team member: %w", err)
|
|
}
|
|
|
|
createdTeamMember = TeamMember{
|
|
ID: teamMemberID,
|
|
UID: cmd.UID,
|
|
TeamID: cmd.TeamID,
|
|
TeamUID: cmd.TeamUID,
|
|
UserID: cmd.UserID,
|
|
UserUID: cmd.UserUID,
|
|
OrgID: cmd.OrgID,
|
|
Created: cmd.Created.Time,
|
|
Updated: cmd.Updated.Time,
|
|
External: cmd.External,
|
|
Permission: cmd.Permission,
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &CreateTeamMemberResult{TeamMember: createdTeamMember}, nil
|
|
}
|
|
|
|
type ListTeamMembersQuery struct {
|
|
UID string
|
|
OrgID int64
|
|
Pagination common.Pagination
|
|
}
|
|
|
|
type ListTeamMembersResult struct {
|
|
Continue int64
|
|
Members []TeamMember
|
|
}
|
|
|
|
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,
|
|
}
|
|
}
|
|
|
|
func (s *legacySQLStore) ListTeamMembers(ctx context.Context, ns claims.NamespaceInfo, query ListTeamMembersQuery) (*ListTeamMembersResult, error) {
|
|
query.Pagination.Limit += 1
|
|
query.OrgID = ns.OrgID
|
|
if query.OrgID == 0 {
|
|
return nil, fmt.Errorf("expected non zero org id")
|
|
}
|
|
|
|
sql, err := s.sql(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req := newListTeamMembers(sql, &query)
|
|
q, err := sqltemplate.Execute(sqlQueryTeamMembersTemplate, req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeamsTemplate.Name(), err)
|
|
}
|
|
|
|
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
|
defer func() {
|
|
if rows != nil {
|
|
_ = rows.Close()
|
|
}
|
|
}()
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := &ListTeamMembersResult{}
|
|
var lastID int64
|
|
for rows.Next() {
|
|
m, err := scanMember(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lastID = m.ID
|
|
res.Members = append(res.Members, m)
|
|
|
|
if int64(len(res.Members)) > query.Pagination.Limit-1 {
|
|
res.Continue = lastID
|
|
res.Members = res.Members[0 : len(res.Members)-1]
|
|
break
|
|
}
|
|
}
|
|
|
|
return res, err
|
|
}
|
|
|
|
type UpdateTeamMemberCommand struct {
|
|
UID string
|
|
Permission team.PermissionType
|
|
Updated legacysql.DBTime
|
|
}
|
|
|
|
type UpdateTeamMemberResult struct {
|
|
UID string
|
|
Permission team.PermissionType
|
|
Updated legacysql.DBTime
|
|
}
|
|
|
|
var sqlUpdateTeamMemberQuery = mustTemplate("update_team_member_query.sql")
|
|
|
|
func newUpdateTeamMember(sql *legacysql.LegacyDatabaseHelper, cmd *UpdateTeamMemberCommand) updateTeamMemberQuery {
|
|
return updateTeamMemberQuery{
|
|
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
|
TeamMemberTable: sql.Table("team_member"),
|
|
Command: cmd,
|
|
}
|
|
}
|
|
|
|
type updateTeamMemberQuery struct {
|
|
sqltemplate.SQLTemplate
|
|
TeamMemberTable string
|
|
Command *UpdateTeamMemberCommand
|
|
}
|
|
|
|
func (r updateTeamMemberQuery) Validate() error {
|
|
return nil
|
|
}
|
|
|
|
func (s *legacySQLStore) UpdateTeamMember(ctx context.Context, ns claims.NamespaceInfo, cmd UpdateTeamMemberCommand) (*UpdateTeamMemberResult, error) {
|
|
now := time.Now().UTC()
|
|
cmd.Updated = legacysql.NewDBTime(now)
|
|
|
|
sql, err := s.sql(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
req := newUpdateTeamMember(sql, &cmd)
|
|
|
|
var result UpdateTeamMemberResult
|
|
err = sql.DB.GetSqlxSession().WithTransaction(ctx, func(st *session.SessionTx) error {
|
|
teamMemberQuery, err := sqltemplate.Execute(sqlUpdateTeamMemberQuery, req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute team member template %q: %w", sqlUpdateTeamMemberQuery.Name(), err)
|
|
}
|
|
|
|
_, err = st.Exec(ctx, teamMemberQuery, req.GetArgs()...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update team member: %w", err)
|
|
}
|
|
|
|
result = UpdateTeamMemberResult(cmd)
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
type DeleteTeamMemberCommand struct {
|
|
UID string
|
|
}
|
|
|
|
var sqlDeleteTeamMemberQuery = mustTemplate("delete_team_member_query.sql")
|
|
|
|
func newDeleteTeamMember(sql *legacysql.LegacyDatabaseHelper, cmd *DeleteTeamMemberCommand) deleteTeamMemberQuery {
|
|
return deleteTeamMemberQuery{
|
|
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
|
TeamMemberTable: sql.Table("team_member"),
|
|
Command: cmd,
|
|
}
|
|
}
|
|
|
|
type deleteTeamMemberQuery struct {
|
|
sqltemplate.SQLTemplate
|
|
TeamMemberTable string
|
|
Command *DeleteTeamMemberCommand
|
|
}
|
|
|
|
func (r deleteTeamMemberQuery) Validate() error {
|
|
return nil
|
|
}
|
|
|
|
func (s *legacySQLStore) DeleteTeamMember(ctx context.Context, ns claims.NamespaceInfo, cmd DeleteTeamMemberCommand) error {
|
|
sql, err := s.sql(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req := newDeleteTeamMember(sql, &cmd)
|
|
if err := req.Validate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
err = sql.DB.GetSqlxSession().WithTransaction(ctx, func(st *session.SessionTx) error {
|
|
teamMemberQuery, err := sqltemplate.Execute(sqlDeleteTeamMemberQuery, req)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute team member template %q: %w", sqlDeleteTeamMemberQuery.Name(), err)
|
|
}
|
|
|
|
_, err = st.Exec(ctx, teamMemberQuery, req.GetArgs()...)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete team member: %w", err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func scanMember(rows *sql.Rows) (TeamMember, error) {
|
|
m := TeamMember{}
|
|
err := rows.Scan(&m.ID, &m.UID, &m.TeamUID, &m.TeamID, &m.UserUID, &m.UserID, &m.Name, &m.Email, &m.Username, &m.External, &m.Created, &m.Updated, &m.Permission)
|
|
return m, err
|
|
}
|