IAM: Move the teambinding code to a separate dir (#112405)
* move the teambinding code to a separate dir * validate pagination limit to fix codeql error * supress codeql warning * convert int to int64
This commit is contained in:
@@ -2,7 +2,6 @@ package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -412,296 +411,3 @@ func (s *legacySQLStore) DeleteTeam(ctx context.Context, ns claims.NamespaceInfo
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type ListTeamBindingsQuery struct {
|
||||
TeamID int64
|
||||
UserID int64
|
||||
OrgID int64
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamBindingsResult struct {
|
||||
Bindings []TeamMember
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
type TeamMember struct {
|
||||
ID int64
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// ListTeamsBindings implements LegacyIdentityStore.
|
||||
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.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 {
|
||||
TeamID int64
|
||||
TeamUID string
|
||||
UserID int64
|
||||
UserUID string
|
||||
OrgID int64
|
||||
Created DBTime
|
||||
Updated 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 = NewDBTime(now)
|
||||
cmd.Updated = 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,
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
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 len(res.Members) > int(query.Pagination.Limit)-1 {
|
||||
res.Continue = lastID
|
||||
res.Members = res.Members[0 : len(res.Members)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func scanMember(rows *sql.Rows) (TeamMember, error) {
|
||||
m := TeamMember{}
|
||||
err := rows.Scan(&m.ID, &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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
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 {
|
||||
TeamID int64
|
||||
UserID int64
|
||||
OrgID int64
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamBindingsResult struct {
|
||||
Bindings []TeamMember
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
type TeamMember struct {
|
||||
ID int64
|
||||
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.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 {
|
||||
TeamID int64
|
||||
TeamUID string
|
||||
UserID int64
|
||||
UserUID string
|
||||
OrgID int64
|
||||
Created DBTime
|
||||
Updated 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 = NewDBTime(now)
|
||||
cmd.Updated = 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,
|
||||
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
|
||||
}
|
||||
|
||||
func scanMember(rows *sql.Rows) (TeamMember, error) {
|
||||
m := TeamMember{}
|
||||
err := rows.Scan(&m.ID, &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
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/serviceaccount"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/sso"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/team"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/teambinding"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/user"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
gfauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
|
||||
@@ -192,7 +193,7 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *ge
|
||||
}
|
||||
|
||||
teamBindingResource := iamv0.TeamBindingResourceInfo
|
||||
teamBindingLegacyStore := team.NewLegacyBindingStore(b.store, enableAuthnMutation)
|
||||
teamBindingLegacyStore := teambinding.NewLegacyBindingStore(b.store, enableAuthnMutation)
|
||||
storage[teamBindingResource.StoragePath()] = teamBindingLegacyStore
|
||||
|
||||
if b.enableDualWriter {
|
||||
@@ -377,7 +378,7 @@ func (b *IdentityAccessManagementAPIBuilder) Validate(ctx context.Context, a adm
|
||||
case *iamv0.Team:
|
||||
return team.ValidateOnCreate(ctx, typedObj)
|
||||
case *iamv0.TeamBinding:
|
||||
return team.ValidateOnBindingCreate(ctx, typedObj)
|
||||
return teambinding.ValidateOnCreate(ctx, typedObj)
|
||||
case *iamv0.ResourcePermission:
|
||||
return resourcepermission.ValidateCreateAndUpdateInput(ctx, typedObj)
|
||||
}
|
||||
|
||||
@@ -107,6 +107,6 @@ func mapToTeamMember(m legacy.TeamMember) iamv0.TeamMember {
|
||||
InternalID: m.UserID,
|
||||
},
|
||||
External: m.External,
|
||||
Permission: mapPermisson(m.Permission),
|
||||
Permission: common.MapUserTeamPermission(m.Permission),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,24 +55,3 @@ func ValidateOnUpdate(ctx context.Context, obj, old *iamv0alpha1.Team) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateOnBindingCreate(ctx context.Context, obj *iamv0alpha1.TeamBinding) error {
|
||||
_, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return apierrors.NewUnauthorized("no identity found")
|
||||
}
|
||||
|
||||
if obj.Spec.Permission != iamv0alpha1.TeamBindingTeamPermissionAdmin && obj.Spec.Permission != iamv0alpha1.TeamBindingTeamPermissionMember {
|
||||
return apierrors.NewBadRequest("invalid permission")
|
||||
}
|
||||
|
||||
if obj.Spec.Subject.Name == "" {
|
||||
return apierrors.NewBadRequest("subject is required")
|
||||
}
|
||||
|
||||
if obj.Spec.TeamRef.Name == "" {
|
||||
return apierrors.NewBadRequest("teamRef is required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -325,113 +325,3 @@ func TestValidateOnUpdate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateOnBindingCreate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
requester *identity.StaticRequester
|
||||
obj *iamv0alpha1.TeamBinding
|
||||
want error
|
||||
}{
|
||||
{
|
||||
name: "valid team binding create",
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
OrgRole: identity.RoleAdmin,
|
||||
},
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "test-user",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "test-team",
|
||||
},
|
||||
Permission: iamv0alpha1.TeamBindingTeamPermissionAdmin,
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid team binding - invalid permission",
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
OrgRole: identity.RoleAdmin,
|
||||
},
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "test-user",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "test-team",
|
||||
},
|
||||
Permission: "invalid",
|
||||
},
|
||||
},
|
||||
want: apierrors.NewBadRequest("invalid permission"),
|
||||
},
|
||||
{
|
||||
name: "invalid team binding - no subject",
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
OrgRole: identity.RoleAdmin,
|
||||
},
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "test-team",
|
||||
},
|
||||
Permission: iamv0alpha1.TeamBindingTeamPermissionAdmin,
|
||||
},
|
||||
},
|
||||
want: apierrors.NewBadRequest("subject is required"),
|
||||
},
|
||||
{
|
||||
name: "invalid team binding - no teamRef",
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
OrgRole: identity.RoleAdmin,
|
||||
},
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "test-user",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "",
|
||||
},
|
||||
Permission: iamv0alpha1.TeamBindingTeamPermissionAdmin,
|
||||
},
|
||||
},
|
||||
want: apierrors.NewBadRequest("teamRef is required"),
|
||||
},
|
||||
{
|
||||
name: "invalid team binding - no requester in context",
|
||||
requester: nil,
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "test-user",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "test-team",
|
||||
},
|
||||
Permission: iamv0alpha1.TeamBindingTeamPermissionAdmin,
|
||||
},
|
||||
},
|
||||
want: apierrors.NewUnauthorized("no identity found"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := identity.WithRequester(context.Background(), test.requester)
|
||||
err := ValidateOnBindingCreate(ctx, test.obj)
|
||||
assert.Equal(t, test.want, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+6
-15
@@ -1,4 +1,4 @@
|
||||
package team
|
||||
package teambinding
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
claims "github.com/grafana/authlib/types"
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
@@ -74,20 +73,20 @@ func (l *LegacyBindingStore) ConvertToTable(ctx context.Context, object runtime.
|
||||
}
|
||||
|
||||
func (l *LegacyBindingStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "update")
|
||||
return nil, false, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "update")
|
||||
}
|
||||
|
||||
func (l *LegacyBindingStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
|
||||
return nil, false, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "delete")
|
||||
}
|
||||
|
||||
func (l *LegacyBindingStore) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) {
|
||||
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "deleteCollection")
|
||||
return nil, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "deleteCollection")
|
||||
}
|
||||
|
||||
func (l *LegacyBindingStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
if !l.enableAuthnMutation {
|
||||
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
|
||||
return nil, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "create")
|
||||
}
|
||||
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
@@ -168,7 +167,7 @@ func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *meta
|
||||
|
||||
if len(res.Bindings) != 1 {
|
||||
// FIXME: maybe empty result?
|
||||
return nil, resource.NewNotFound(name)
|
||||
return nil, bindingResource.NewNotFound(name)
|
||||
}
|
||||
|
||||
obj := mapToBindingObject(ns, res.Bindings[0])
|
||||
@@ -260,11 +259,3 @@ func mapFromBindingName(name string) (int64, int64) {
|
||||
|
||||
return teamID, userID
|
||||
}
|
||||
|
||||
func mapPermisson(p team.PermissionType) iamv0.TeamPermission {
|
||||
if p == team.PermissionTypeAdmin {
|
||||
return iamv0.TeamPermissionAdmin
|
||||
} else {
|
||||
return iamv0.TeamPermissionMember
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package teambinding
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
|
||||
func ValidateOnCreate(ctx context.Context, obj *iamv0alpha1.TeamBinding) error {
|
||||
_, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return apierrors.NewUnauthorized("no identity found")
|
||||
}
|
||||
|
||||
if obj.Spec.Permission != iamv0alpha1.TeamBindingTeamPermissionAdmin && obj.Spec.Permission != iamv0alpha1.TeamBindingTeamPermissionMember {
|
||||
return apierrors.NewBadRequest("invalid permission")
|
||||
}
|
||||
|
||||
if obj.Spec.Subject.Name == "" {
|
||||
return apierrors.NewBadRequest("subject is required")
|
||||
}
|
||||
|
||||
if obj.Spec.TeamRef.Name == "" {
|
||||
return apierrors.NewBadRequest("teamRef is required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package teambinding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
|
||||
func TestValidateOnCreate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
requester *identity.StaticRequester
|
||||
obj *iamv0alpha1.TeamBinding
|
||||
want error
|
||||
}{
|
||||
{
|
||||
name: "valid team binding create",
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
OrgRole: identity.RoleAdmin,
|
||||
},
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "test-user",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "test-team",
|
||||
},
|
||||
Permission: iamv0alpha1.TeamBindingTeamPermissionAdmin,
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid team binding - invalid permission",
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
OrgRole: identity.RoleAdmin,
|
||||
},
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "test-user",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "test-team",
|
||||
},
|
||||
Permission: "invalid",
|
||||
},
|
||||
},
|
||||
want: apierrors.NewBadRequest("invalid permission"),
|
||||
},
|
||||
{
|
||||
name: "invalid team binding - no subject",
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
OrgRole: identity.RoleAdmin,
|
||||
},
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "test-team",
|
||||
},
|
||||
Permission: iamv0alpha1.TeamBindingTeamPermissionAdmin,
|
||||
},
|
||||
},
|
||||
want: apierrors.NewBadRequest("subject is required"),
|
||||
},
|
||||
{
|
||||
name: "invalid team binding - no teamRef",
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
OrgRole: identity.RoleAdmin,
|
||||
},
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "test-user",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "",
|
||||
},
|
||||
Permission: iamv0alpha1.TeamBindingTeamPermissionAdmin,
|
||||
},
|
||||
},
|
||||
want: apierrors.NewBadRequest("teamRef is required"),
|
||||
},
|
||||
{
|
||||
name: "invalid team binding - no requester in context",
|
||||
requester: nil,
|
||||
obj: &iamv0alpha1.TeamBinding{
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: "test-user",
|
||||
},
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: "test-team",
|
||||
},
|
||||
Permission: iamv0alpha1.TeamBindingTeamPermissionAdmin,
|
||||
},
|
||||
},
|
||||
want: apierrors.NewUnauthorized("no identity found"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := identity.WithRequester(context.Background(), test.requester)
|
||||
err := ValidateOnCreate(ctx, test.obj)
|
||||
assert.Equal(t, test.want, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user