Skip to content

Commit

Permalink
fix: Resolve subgroup creation and assignment issues (#95)
Browse files Browse the repository at this point in the history
- Fix subgroup creation for the main group
- Fix subgroup assignment to users
- Add integration tests for KeycloakRealmGroup
- Add integration tests for KeycloakRealmUser with subgroup assignments
  • Loading branch information
zmotso authored and SergK committed Oct 15, 2024
1 parent 4bc7be0 commit 5d22cc4
Show file tree
Hide file tree
Showing 17 changed files with 833 additions and 361 deletions.
8 changes: 5 additions & 3 deletions controllers/keycloakclient/chain/put_client_scope_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package chain

import (
"context"
keycloakApi "github.com/epam/edp-keycloak-operator/api/v1"
"github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks"
"testing"

"github.com/go-logr/logr"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
Expand All @@ -13,7 +13,9 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"testing"

keycloakApi "github.com/epam/edp-keycloak-operator/api/v1"
"github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks"
)

func TestPutClientScope_Serve(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (r *ReconcileKeycloakRealmGroup) tryReconcile(ctx context.Context, keycloak
return fmt.Errorf("unable to get keycloak realm from ref: %w", err)
}

id, err := kClient.SyncRealmGroup(gocloak.PString(realm.Realm), &keycloakRealmGroup.Spec)
id, err := kClient.SyncRealmGroup(ctx, gocloak.PString(realm.Realm), &keycloakRealmGroup.Spec)
if err != nil {
return fmt.Errorf("unable to sync realm group: %w", err)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package keycloakrealmgroup

import (
"time"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/Nerzal/gocloak/v12"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"

"github.com/epam/edp-keycloak-operator/api/common"
keycloakApi "github.com/epam/edp-keycloak-operator/api/v1"
"github.com/epam/edp-keycloak-operator/controllers/helper"
"github.com/epam/edp-keycloak-operator/pkg/client/keycloak/adapter"
"github.com/epam/edp-keycloak-operator/pkg/objectmeta"
)

var _ = Describe("KeycloakRealmGroup controller", Ordered, func() {
const (
groupCR = "test-keycloak-realm-group"
)
It("Should create KeycloakRealmGroup", func() {
By("Creating role for the group")
_, err := keycloakApiClient.CreateRealmRole(ctx, getKeyCloakToken(), KeycloakRealmCR, gocloak.Role{
Name: gocloak.StringP("test-group-role"),
})
Expect(adapter.SkipAlreadyExistsErr(err)).ShouldNot(HaveOccurred())

By("Creating a KeycloakRealmGroup subgroup")
group := &keycloakApi.KeycloakRealmGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "test-subgroup",
Namespace: ns,
},
Spec: keycloakApi.KeycloakRealmGroupSpec{
Name: "test-subgroup",
RealmRef: common.RealmRef{
Kind: keycloakApi.KeycloakRealmKind,
Name: KeycloakRealmCR,
},
Path: "/test-subgroup",
},
}
Expect(k8sClient.Create(ctx, group)).Should(Succeed())
Eventually(func(g Gomega) {
createdGroup := &keycloakApi.KeycloakRealmGroup{}
err = k8sClient.Get(ctx, types.NamespacedName{Name: "test-subgroup", Namespace: ns}, createdGroup)
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(createdGroup.Status.Value).Should(Equal(helper.StatusOK))
}).WithTimeout(time.Second * 20).WithPolling(time.Second).Should(Succeed())

By("Creating a KeycloakRealmGroup")
group = &keycloakApi.KeycloakRealmGroup{
ObjectMeta: metav1.ObjectMeta{
Name: groupCR,
Namespace: ns,
},
Spec: keycloakApi.KeycloakRealmGroupSpec{
Name: "test-group",
RealmRef: common.RealmRef{
Kind: keycloakApi.KeycloakRealmKind,
Name: KeycloakRealmCR,
},
Path: "/test-group",
RealmRoles: []string{"test-group-role"},
SubGroups: []string{"test-subgroup"},
},
}
Expect(k8sClient.Create(ctx, group)).Should(Succeed())
Eventually(func(g Gomega) {
createdGroup := &keycloakApi.KeycloakRealmGroup{}
err = k8sClient.Get(ctx, types.NamespacedName{Name: groupCR, Namespace: ns}, createdGroup)
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(createdGroup.Status.Value).Should(Equal(helper.StatusOK))
}).WithTimeout(time.Second * 20).WithPolling(time.Second).Should(Succeed())
})
It("Should update KeycloakRealmGroup", func() {
By("Getting KeycloakRealmGroup")
group := &keycloakApi.KeycloakRealmGroup{}
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: groupCR}, group)).Should(Succeed())

By("Updating a parent KeycloakRealmGroup")
group.Spec.SubGroups = []string{}

Expect(k8sClient.Update(ctx, group)).Should(Succeed())
Eventually(func(g Gomega) {
updatedGroup := &keycloakApi.KeycloakRealmGroup{}
err := k8sClient.Get(ctx, types.NamespacedName{Name: group.Name, Namespace: ns}, updatedGroup)
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(updatedGroup.Status.Value).Should(Equal(helper.StatusOK))
}, time.Minute, time.Second*5).Should(Succeed())
})
It("Should delete KeycloakRealmGroup", func() {
By("Getting KeycloakRealmGroup")
group := &keycloakApi.KeycloakRealmGroup{}
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: ns, Name: groupCR}, group)).Should(Succeed())
By("Deleting KeycloakRealmGroup")
Expect(k8sClient.Delete(ctx, group)).Should(Succeed())
Eventually(func(g Gomega) {
deletedGroup := &keycloakApi.KeycloakRealmGroup{}
err := k8sClient.Get(ctx, types.NamespacedName{Name: group.Name, Namespace: ns}, deletedGroup)

g.Expect(k8sErrors.IsNotFound(err)).Should(BeTrue())
}, timeout, interval).Should(Succeed())
})
It("Should preserve group with annotation", func() {
By("Creating a KeycloakRealmGroup")
group := &keycloakApi.KeycloakRealmGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "test-keycloak-realm-group-preserve",
Namespace: ns,
Annotations: map[string]string{
objectmeta.PreserveResourcesOnDeletionAnnotation: "true",
},
},
Spec: keycloakApi.KeycloakRealmGroupSpec{
RealmRef: common.RealmRef{
Kind: keycloakApi.KeycloakRealmKind,
Name: KeycloakRealmCR,
},
Name: "test-group-preserve",
},
}
Expect(k8sClient.Create(ctx, group)).Should(Succeed())
Eventually(func(g Gomega) {
createdGroup := &keycloakApi.KeycloakRealmGroup{}
err := k8sClient.Get(ctx, types.NamespacedName{Name: group.Name, Namespace: ns}, createdGroup)
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(createdGroup.Status.Value).Should(Equal(helper.StatusOK))
}).WithTimeout(time.Second * 20).WithPolling(time.Second).Should(Succeed())

By("Deleting KeycloakRealmGroup")
Expect(k8sClient.Delete(ctx, group)).Should(Succeed())
Eventually(func(g Gomega) {
groups, err := keycloakApiClient.GetGroups(ctx, getKeyCloakToken(), KeycloakRealmCR, gocloak.GetGroupsParams{
Search: gocloak.StringP("test-group-preserve"),
})
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(groups).Should(HaveLen(1))
}, time.Minute, time.Second*5).Should(Succeed())
})
It("Should fail to create KeycloakRealmGroup with not existing subgroup", func() {
By("Creating a KeycloakRealmGroup")
group := &keycloakApi.KeycloakRealmGroup{
ObjectMeta: metav1.ObjectMeta{
Name: "test-keycloak-realm-group-with-invalid-subgroup",
Namespace: ns,
},
Spec: keycloakApi.KeycloakRealmGroupSpec{
RealmRef: common.RealmRef{
Kind: keycloakApi.KeycloakRealmKind,
Name: KeycloakRealmCR,
},
Name: "test-group-with-invalid-subgroup",
SubGroups: []string{"not-existing-subgroup"},
},
}
Expect(k8sClient.Create(ctx, group)).Should(Succeed())

By("Waiting for KeycloakRealmGroup to be processed")
time.Sleep(time.Second * 3)

By("Checking KeycloakRealmGroup status")
Consistently(func(g Gomega) {
createdGroup := &keycloakApi.KeycloakRealmGroup{}
err := k8sClient.Get(ctx, types.NamespacedName{Name: group.Name, Namespace: ns}, createdGroup)
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(createdGroup.Status.Value).Should(ContainSubstring("unable to sync realm group"))
}, time.Second*3, time.Second).Should(Succeed())
})
})

This file was deleted.

Loading

0 comments on commit 5d22cc4

Please sign in to comment.