mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-22 15:32:15 +00:00
feat: extend DeleteProvider to include Zitadel IdP (#12396)
# Which Problems Are Solved This PR extends `DeleteProvider` to include Zitadel provider enabling the deletion of Zitadel IdP. # How the Problems Are Solved - Extend org/instance IDP remove write models to include `ZitadelIDPAddedEvent` in event appends and queries. - Extend command-side IDP reduction/type handling for Zitadel IDP add events. - Add management/admin integration tests for deleting Zitadel providers - Add error translation key in all language locales for org-level “IDP config not existing”. # Additional Changes N/A # Additional Context. Closes https://github.com/zitadel/zitadel/issues/12397 --------- Co-authored-by: Copilot Autofix powered by AI <[email protected]> Co-authored-by: Livio Spring <[email protected]>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI
Livio Spring
parent
a97999e5ca
commit
5bdfd96be5
@@ -223,6 +223,10 @@ func Test_AddZitadelProvider(t *testing.T) {
|
||||
assert.NotEmpty(t, got.GetId())
|
||||
assert.WithinRange(t, got.GetDetails().GetCreationDate().AsTime(), before, after)
|
||||
assert.Equal(t, tt.wantResponse.GetDetails().GetResourceOwner(), got.GetDetails().GetResourceOwner())
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(AdminCTX, &admin_pb.DeleteProviderRequest{Id: got.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -414,7 +418,10 @@ func Test_UpdateZitadelProvider(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// create a new provider per subtest
|
||||
existingProvider := Instance.AddZitadelProvider(AdminCTX, integration.IDPName())
|
||||
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(AdminCTX, &admin_pb.DeleteProviderRequest{Id: existingProvider.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
// build request using this provider ID
|
||||
tt.args.req.Id = existingProvider.GetId()
|
||||
|
||||
@@ -445,7 +452,11 @@ func Test_UpdateZitadelProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_UpdateZitadelProvider_MissingID(t *testing.T) {
|
||||
_ = Instance.AddZitadelProvider(AdminCTX, integration.IDPName())
|
||||
existingProvider := Instance.AddZitadelProvider(AdminCTX, integration.IDPName())
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(AdminCTX, &admin_pb.DeleteProviderRequest{Id: existingProvider.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
// Attempt to update the provider without specifying the ID
|
||||
updateResp, err := Client.UpdateZitadelProvider(AdminCTX, &admin_pb.UpdateZitadelProviderRequest{})
|
||||
require.Error(t, err)
|
||||
@@ -459,6 +470,10 @@ func Test_UpdateZitadelProvider_MissingID(t *testing.T) {
|
||||
func Test_GetProviderByID(t *testing.T) {
|
||||
providerName := integration.IDPName()
|
||||
existingProvider := Instance.AddZitadelProvider(AdminCTX, providerName)
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(AdminCTX, &admin_pb.DeleteProviderRequest{Id: existingProvider.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -557,6 +572,12 @@ func Test_ListProviders(t *testing.T) {
|
||||
|
||||
provider2Name := integration.IDPName()
|
||||
provider2 := Instance.AddZitadelProvider(AdminCTX, provider2Name)
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(AdminCTX, &admin_pb.DeleteProviderRequest{Id: provider1.GetId()})
|
||||
require.NoError(t, err)
|
||||
_, err = Client.DeleteProvider(AdminCTX, &admin_pb.DeleteProviderRequest{Id: provider2.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -577,6 +598,82 @@ func Test_ListProviders(t *testing.T) {
|
||||
req: &admin_pb.ListProvidersRequest{},
|
||||
wantErr: status.Error(codes.PermissionDenied, "No matching permissions found (AUTH-5mWD2)"),
|
||||
},
|
||||
{
|
||||
name: "list all providers",
|
||||
ctx: AdminCTX,
|
||||
req: &admin_pb.ListProvidersRequest{
|
||||
Query: &object_pb.ListQuery{
|
||||
Asc: false,
|
||||
},
|
||||
},
|
||||
wantResp: &admin_pb.ListProvidersResponse{
|
||||
Details: &object_pb.ListDetails{
|
||||
TotalResult: 2,
|
||||
},
|
||||
Result: []*idp_pb.Provider{
|
||||
{
|
||||
Id: provider1.GetId(),
|
||||
Details: &object_pb.ObjectDetails{
|
||||
CreationDate: provider1.GetDetails().GetCreationDate(),
|
||||
ChangeDate: provider1.GetDetails().GetChangeDate(),
|
||||
ResourceOwner: provider1.GetDetails().GetResourceOwner(),
|
||||
},
|
||||
State: idp_pb.IDPState_IDP_STATE_ACTIVE,
|
||||
Name: provider1Name,
|
||||
Owner: idp_pb.IDPOwnerType_IDP_OWNER_TYPE_SYSTEM,
|
||||
Type: idp_pb.ProviderType_PROVIDER_TYPE_ZITADEL,
|
||||
Config: &idp_pb.ProviderConfig{
|
||||
Options: &idp_pb.Options{
|
||||
IsCreationAllowed: true,
|
||||
},
|
||||
Config: &idp_pb.ProviderConfig_Zitadel{
|
||||
Zitadel: &idp_pb.ZitadelConfig{
|
||||
Issuer: "zitadel.example.com",
|
||||
ClientId: "test-client",
|
||||
Scopes: []string{"email", "profile"},
|
||||
InstanceRolesInfo: []*idp_pb.InstanceRolesInfo{
|
||||
{
|
||||
OrganizationId: "org1",
|
||||
OrganizationDomain: "org1.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Id: provider2.GetId(),
|
||||
Details: &object_pb.ObjectDetails{
|
||||
CreationDate: provider2.GetDetails().GetCreationDate(),
|
||||
ChangeDate: provider2.GetDetails().GetChangeDate(),
|
||||
ResourceOwner: provider2.GetDetails().GetResourceOwner(),
|
||||
},
|
||||
State: idp_pb.IDPState_IDP_STATE_ACTIVE,
|
||||
Name: provider2Name,
|
||||
Owner: idp_pb.IDPOwnerType_IDP_OWNER_TYPE_SYSTEM,
|
||||
Type: idp_pb.ProviderType_PROVIDER_TYPE_ZITADEL,
|
||||
Config: &idp_pb.ProviderConfig{
|
||||
Options: &idp_pb.Options{
|
||||
IsCreationAllowed: true,
|
||||
},
|
||||
Config: &idp_pb.ProviderConfig_Zitadel{
|
||||
Zitadel: &idp_pb.ZitadelConfig{
|
||||
Issuer: "zitadel.example.com",
|
||||
ClientId: "test-client",
|
||||
Scopes: []string{"email", "profile"},
|
||||
InstanceRolesInfo: []*idp_pb.InstanceRolesInfo{
|
||||
{
|
||||
OrganizationId: "org1",
|
||||
OrganizationDomain: "org1.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list by id",
|
||||
ctx: AdminCTX,
|
||||
@@ -702,11 +799,82 @@ func Test_ListProviders(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, got)
|
||||
assert.Equal(t, tt.wantResp.GetDetails().GetTotalResult(), got.GetDetails().GetTotalResult())
|
||||
for i, want := range tt.wantResp.GetResult() {
|
||||
assert.Equal(t, want.GetDetails().GetCreationDate().AsTime(), got.GetResult()[i].GetDetails().GetCreationDate().AsTime())
|
||||
assert.Equal(t, Instance.ID(), got.GetResult()[i].GetDetails().GetResourceOwner())
|
||||
assertProvider(t, want, got.GetResult()[i])
|
||||
gotByID := make(map[string]*idp_pb.Provider)
|
||||
for _, p := range got.GetResult() {
|
||||
gotByID[p.GetId()] = p
|
||||
}
|
||||
for _, want := range tt.wantResp.GetResult() {
|
||||
actual, ok := gotByID[want.GetId()]
|
||||
require.True(t, ok, "expected provider %s not found in results", want.GetId())
|
||||
assert.Equal(t, want.GetDetails().GetCreationDate().AsTime(), actual.GetDetails().GetCreationDate().AsTime())
|
||||
assert.Equal(t, Instance.ID(), actual.GetDetails().GetResourceOwner())
|
||||
assertProvider(t, want, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_DeleteZitadelProvider(t *testing.T) {
|
||||
existingProvider := Instance.AddZitadelProvider(AdminCTX, integration.IDPName())
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(AdminCTX, &admin_pb.DeleteProviderRequest{Id: existingProvider.GetId()})
|
||||
if err != nil && status.Code(err) != codes.NotFound {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
req *admin_pb.DeleteProviderRequest
|
||||
wantResponse *admin_pb.DeleteProviderResponse
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "no permissions, error",
|
||||
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
|
||||
req: &admin_pb.DeleteProviderRequest{Id: "idp-id"},
|
||||
wantErr: status.Error(codes.NotFound, "membership not found (AUTHZ-cdgFk)"),
|
||||
},
|
||||
{
|
||||
name: "insufficient permissions, error", // no iam.idp.write permission
|
||||
ctx: integration.WithSystemUserWithNoPermissionsAuthorization(CTX),
|
||||
req: &admin_pb.DeleteProviderRequest{Id: "idp-id"},
|
||||
wantErr: status.Error(codes.PermissionDenied, "No matching permissions found (AUTH-5mWD2)"),
|
||||
},
|
||||
{
|
||||
name: "not found, error",
|
||||
ctx: AdminCTX,
|
||||
req: &admin_pb.DeleteProviderRequest{Id: "idp-id"},
|
||||
wantErr: status.Error(codes.NotFound, "Identity Provider Configuration doesn't exist (INST-Se3tg)"),
|
||||
},
|
||||
{
|
||||
name: "delete, ok",
|
||||
ctx: AdminCTX,
|
||||
req: &admin_pb.DeleteProviderRequest{Id: existingProvider.GetId()},
|
||||
wantResponse: &admin_pb.DeleteProviderResponse{
|
||||
Details: &object_pb.ObjectDetails{
|
||||
ResourceOwner: Instance.Instance.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.DeleteProvider(tt.ctx, tt.req)
|
||||
after := time.Now()
|
||||
if tt.wantErr != nil {
|
||||
require.Error(t, err)
|
||||
grpcStatus, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, status.Code(tt.wantErr), grpcStatus.Code())
|
||||
assert.Equal(t, status.Convert(tt.wantErr).Message(), grpcStatus.Message())
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, got)
|
||||
assert.Equal(t, tt.wantResponse.GetDetails().GetResourceOwner(), got.GetDetails().GetResourceOwner())
|
||||
assert.WithinRange(t, got.GetDetails().GetChangeDate().AsTime(), existingProvider.GetDetails().GetCreationDate().AsTime(), after)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +229,10 @@ func Test_AddZitadelProvider(t *testing.T) {
|
||||
assert.NotEmpty(t, got.GetId())
|
||||
assert.WithinRange(t, got.GetDetails().GetCreationDate().AsTime(), before, after)
|
||||
assert.Equal(t, tt.wantResponse.GetDetails().GetResourceOwner(), got.GetDetails().GetResourceOwner())
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(OrgCTX, &mgmt_pb.DeleteProviderRequest{Id: got.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -454,6 +458,10 @@ func Test_UpdateZitadelProvider(t *testing.T) {
|
||||
// create a new provider per subtest and set the ID in the request
|
||||
zitadelProvider := Instance.AddOrgZitadelProvider(OrgCTX, integration.IDPName())
|
||||
tt.args.req.Id = zitadelProvider.Id
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(OrgCTX, &mgmt_pb.DeleteProviderRequest{Id: zitadelProvider.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
before := time.Now()
|
||||
updateResp, err := Client.UpdateZitadelProvider(tt.args.ctx, tt.args.req)
|
||||
@@ -482,7 +490,11 @@ func Test_UpdateZitadelProvider(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_UpdateZitadelProvider_MissingID(t *testing.T) {
|
||||
_ = Instance.AddOrgZitadelProvider(OrgCTX, integration.IDPName())
|
||||
existingProvider := Instance.AddOrgZitadelProvider(OrgCTX, integration.IDPName())
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(OrgCTX, &mgmt_pb.DeleteProviderRequest{Id: existingProvider.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
// Attempt to update the provider without specifying the ID
|
||||
updateResp, err := Client.UpdateZitadelProvider(OrgCTX, &mgmt_pb.UpdateZitadelProviderRequest{})
|
||||
require.Error(t, err)
|
||||
@@ -597,7 +609,12 @@ func Test_ListProviders(t *testing.T) {
|
||||
provider1 := Instance.AddOrgZitadelProvider(orgCtx, provider1Name)
|
||||
provider2Name := integration.IDPName()
|
||||
provider2 := Instance.AddOrgZitadelProvider(orgCtx, provider2Name)
|
||||
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(orgCtx, &mgmt_pb.DeleteProviderRequest{Id: provider1.GetId()})
|
||||
require.NoError(t, err)
|
||||
_, err = Client.DeleteProvider(orgCtx, &mgmt_pb.DeleteProviderRequest{Id: provider2.GetId()})
|
||||
require.NoError(t, err)
|
||||
})
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
@@ -818,11 +835,82 @@ func Test_ListProviders(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, got)
|
||||
assert.Equal(t, tt.wantResp.GetDetails().GetTotalResult(), got.GetDetails().GetTotalResult())
|
||||
for i, want := range tt.wantResp.GetResult() {
|
||||
assert.Equal(t, want.GetDetails().GetCreationDate().AsTime(), got.GetResult()[i].GetDetails().GetCreationDate().AsTime())
|
||||
assert.Equal(t, org.GetOrganizationId(), got.GetResult()[i].GetDetails().GetResourceOwner())
|
||||
assertProvider(t, want, got.GetResult()[i])
|
||||
gotByID := make(map[string]*idp_pb.Provider)
|
||||
for _, p := range got.GetResult() {
|
||||
gotByID[p.GetId()] = p
|
||||
}
|
||||
for _, want := range tt.wantResp.GetResult() {
|
||||
actual, ok := gotByID[want.GetId()]
|
||||
require.True(t, ok, "expected provider %s not found in results", want.GetId())
|
||||
assert.Equal(t, want.GetDetails().GetCreationDate().AsTime(), actual.GetDetails().GetCreationDate().AsTime())
|
||||
assert.Equal(t, org.GetOrganizationId(), actual.GetDetails().GetResourceOwner())
|
||||
assertProvider(t, want, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_DeleteZitadelProvider(t *testing.T) {
|
||||
existingProvider := Instance.AddOrgZitadelProvider(OrgCTX, integration.IDPName())
|
||||
t.Cleanup(func() {
|
||||
_, err := Client.DeleteProvider(OrgCTX, &mgmt_pb.DeleteProviderRequest{Id: existingProvider.GetId()})
|
||||
if err != nil && status.Code(err) != codes.NotFound {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
req *mgmt_pb.DeleteProviderRequest
|
||||
wantResponse *mgmt_pb.DeleteProviderResponse
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "no permissions, error",
|
||||
ctx: Instance.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
|
||||
req: &mgmt_pb.DeleteProviderRequest{Id: "idp-id"},
|
||||
wantErr: status.Error(codes.NotFound, "membership not found (AUTHZ-cdgFk)"),
|
||||
},
|
||||
{
|
||||
name: "insufficient permissions, error", // no iam.idp.write permission
|
||||
ctx: integration.WithSystemUserWithNoPermissionsAuthorization(CTX),
|
||||
req: &mgmt_pb.DeleteProviderRequest{Id: "idp-id"},
|
||||
wantErr: status.Error(codes.PermissionDenied, "No matching permissions found (AUTH-5mWD2)"),
|
||||
},
|
||||
{
|
||||
name: "not found, error",
|
||||
ctx: OrgCTX,
|
||||
req: &mgmt_pb.DeleteProviderRequest{Id: "idp-id"},
|
||||
wantErr: status.Error(codes.NotFound, "Identity Provider Configuration doesn't exist (ORG-Se3tg)"),
|
||||
},
|
||||
{
|
||||
name: "delete, ok",
|
||||
ctx: OrgCTX,
|
||||
req: &mgmt_pb.DeleteProviderRequest{Id: existingProvider.GetId()},
|
||||
wantResponse: &mgmt_pb.DeleteProviderResponse{
|
||||
Details: &object_pb.ObjectDetails{
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.DeleteProvider(tt.ctx, tt.req)
|
||||
after := time.Now()
|
||||
if tt.wantErr != nil {
|
||||
require.Error(t, err)
|
||||
grpcStatus, ok := status.FromError(err)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, status.Code(tt.wantErr), grpcStatus.Code())
|
||||
assert.Equal(t, status.Convert(tt.wantErr).Message(), grpcStatus.Message())
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, got)
|
||||
assert.Equal(t, tt.wantResponse.GetDetails().GetResourceOwner(), got.GetDetails().GetResourceOwner())
|
||||
assert.WithinRange(t, got.GetDetails().GetChangeDate().AsTime(), existingProvider.GetDetails().GetCreationDate().AsTime(), after)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user