IAM: Implement delete in team API (#111367)
* implement delete in team API * add unit tests for legacy sql * add integration tests * remove comment * remove org_id from delete team sql command * fetch team ID before deleting it
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
DELETE FROM {{ .Ident .TeamTable }}
|
||||
WHERE uid = {{ .Arg .Command.UID }}
|
||||
@@ -32,6 +32,7 @@ type LegacyIdentityStore interface {
|
||||
GetTeamInternalID(ctx context.Context, ns claims.NamespaceInfo, query GetTeamInternalIDQuery) (*GetTeamInternalIDResult, error)
|
||||
CreateTeam(ctx context.Context, ns claims.NamespaceInfo, cmd CreateTeamCommand) (*CreateTeamResult, error)
|
||||
ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error)
|
||||
DeleteTeam(ctx context.Context, ns claims.NamespaceInfo, cmd DeleteTeamCommand) error
|
||||
ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error)
|
||||
ListTeamMembers(ctx context.Context, ns claims.NamespaceInfo, query ListTeamMembersQuery) (*ListTeamMembersResult, error)
|
||||
}
|
||||
|
||||
@@ -78,6 +78,12 @@ func TestIdentityQueries(t *testing.T) {
|
||||
return &v
|
||||
}
|
||||
|
||||
deleteTeam := func(q *DeleteTeamCommand) sqltemplate.SQLTemplate {
|
||||
v := newDeleteTeam(nodb, q)
|
||||
v.SQLTemplate = mocks.NewTestingSQLTemplate()
|
||||
return &v
|
||||
}
|
||||
|
||||
listUserTeams := func(q *ListUserTeamsQuery) sqltemplate.SQLTemplate {
|
||||
v := newListUserTeams(nodb, q)
|
||||
v.SQLTemplate = mocks.NewTestingSQLTemplate()
|
||||
@@ -387,6 +393,14 @@ func TestIdentityQueries(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlDeleteTeamTemplate: {
|
||||
{
|
||||
Name: "delete_team_basic",
|
||||
Data: deleteTeam(&DeleteTeamCommand{
|
||||
UID: "team-1",
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlCreateOrgUserTemplate: {
|
||||
{
|
||||
Name: "create_org_user_basic",
|
||||
|
||||
@@ -265,6 +265,69 @@ func (s *legacySQLStore) CreateTeam(ctx context.Context, ns claims.NamespaceInfo
|
||||
return &CreateTeamResult{Team: createdTeam}, nil
|
||||
}
|
||||
|
||||
type DeleteTeamCommand struct {
|
||||
UID string
|
||||
}
|
||||
|
||||
var sqlDeleteTeamTemplate = mustTemplate("delete_team.sql")
|
||||
|
||||
func newDeleteTeam(sql *legacysql.LegacyDatabaseHelper, cmd *DeleteTeamCommand) deleteTeamQuery {
|
||||
return deleteTeamQuery{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
TeamTable: sql.Table("team"),
|
||||
Command: cmd,
|
||||
}
|
||||
}
|
||||
|
||||
type deleteTeamQuery struct {
|
||||
sqltemplate.SQLTemplate
|
||||
TeamTable string
|
||||
Command *DeleteTeamCommand
|
||||
}
|
||||
|
||||
func (r deleteTeamQuery) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *legacySQLStore) DeleteTeam(ctx context.Context, ns claims.NamespaceInfo, cmd DeleteTeamCommand) error {
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := newDeleteTeam(sql, &cmd)
|
||||
if err := req.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sql.DB.GetSqlxSession().WithTransaction(ctx, func(st *session.SessionTx) error {
|
||||
_, err := s.GetTeamInternalID(ctx, ns, GetTeamInternalIDQuery{
|
||||
OrgID: ns.OrgID,
|
||||
UID: cmd.UID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
teamDeleteReq := newDeleteTeam(sql, &cmd)
|
||||
if err := teamDeleteReq.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
teamDeleteQuery, err := sqltemplate.Execute(sqlDeleteTeamTemplate, teamDeleteReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error executing team delete template: %w", err)
|
||||
}
|
||||
|
||||
_, err = st.Exec(ctx, teamDeleteQuery, teamDeleteReq.GetArgs()...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete team: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type ListTeamBindingsQuery struct {
|
||||
// UID is team uid to list bindings for. If not set store should list bindings for all teams
|
||||
UID string
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
DELETE FROM `grafana`.`team`
|
||||
WHERE uid = 'team-1'
|
||||
Vendored
Executable
+2
@@ -0,0 +1,2 @@
|
||||
DELETE FROM "grafana"."team"
|
||||
WHERE uid = 'team-1'
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
DELETE FROM "grafana"."team"
|
||||
WHERE uid = 'team-1'
|
||||
@@ -74,7 +74,40 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
|
||||
|
||||
// Delete implements rest.GracefulDeleter.
|
||||
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
|
||||
}
|
||||
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
toBeDeleted, err := s.Get(ctx, name, nil)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if deleteValidation != nil {
|
||||
if err := deleteValidation(ctx, toBeDeleted); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
}
|
||||
|
||||
err = s.store.DeleteTeam(ctx, ns, legacy.DeleteTeamCommand{
|
||||
UID: name,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return &iamv0alpha1.Team{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: ns.Value,
|
||||
},
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
// Update implements rest.Updater.
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestIntegrationTeams(t *testing.T) {
|
||||
}
|
||||
|
||||
func doTeamCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
t.Run("should create team and get it using the new APIs as a GrafanaAdmin", func(t *testing.T) {
|
||||
t.Run("should create/get/delete team using the new APIs as a GrafanaAdmin", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
teamClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
@@ -80,6 +80,16 @@ func doTeamCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
|
||||
require.Equal(t, createdUID, fetched.GetName())
|
||||
require.Equal(t, "default", fetched.GetNamespace())
|
||||
|
||||
err = teamClient.Resource.Delete(ctx, createdUID, metav1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = teamClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
require.Error(t, err)
|
||||
var statusErr *errors.StatusError
|
||||
require.ErrorAs(t, err, &statusErr)
|
||||
require.Equal(t, "Failure", statusErr.ErrStatus.Status)
|
||||
require.Contains(t, statusErr.ErrStatus.Message, "team not found")
|
||||
})
|
||||
|
||||
t.Run("should not be able to create team when using a user with insufficient permissions", func(t *testing.T) {
|
||||
@@ -193,7 +203,7 @@ func doTeamCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
}
|
||||
|
||||
func doTeamCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
t.Run("should create team using legacy APIs and get it using the new APIs", func(t *testing.T) {
|
||||
t.Run("should create team using legacy APIs and get/delete it using the new APIs", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
teamClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
@@ -232,5 +242,15 @@ func doTeamCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTestHelper)
|
||||
|
||||
require.Equal(t, rsp.Result.UID, team.GetName())
|
||||
require.Equal(t, "default", team.GetNamespace())
|
||||
|
||||
err = teamClient.Resource.Delete(ctx, rsp.Result.UID, metav1.DeleteOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = teamClient.Resource.Get(ctx, rsp.Result.UID, metav1.GetOptions{})
|
||||
require.Error(t, err)
|
||||
var statusErr *errors.StatusError
|
||||
require.ErrorAs(t, err, &statusErr)
|
||||
require.Equal(t, "Failure", statusErr.ErrStatus.Status)
|
||||
require.Contains(t, statusErr.ErrStatus.Message, "team not found")
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user