├── .chglog ├── CHANGELOG.tpl.md ├── config.yml └── release.tpl.md ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── codecov.yaml │ ├── e2e.yaml │ ├── pr.yaml │ └── release.yaml ├── .gitignore ├── .golangci.yaml ├── .mockery.yaml ├── CHANGELOG-pre-1.17.0.md ├── CHANGELOG.md ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE-2.0 ├── Makefile ├── PROJECT ├── README.md ├── SECURITY.md ├── api ├── common │ ├── realm.go │ ├── ref.go │ └── zz_generated.deepcopy.go ├── v1 │ ├── client_authorization.go │ ├── groupversion_info.go │ ├── keycloak_types.go │ ├── keycloak_types_test.go │ ├── keycloakauthflow_types.go │ ├── keycloakclient_types.go │ ├── keycloakclientscope_types.go │ ├── keycloakcomponent_types.go │ ├── keycloakrealm_types.go │ ├── keycloakrealmgroup_types.go │ ├── keycloakrealmidentityprovider_types.go │ ├── keycloakrealmrole_types.go │ ├── keycloakrealmrolebatch_types.go │ ├── keycloakrealmuser_types.go │ └── zz_generated.deepcopy.go └── v1alpha1 │ ├── clusterkeycloak_types.go │ ├── clusterkeycloakrealm_types.go │ ├── groupversion_info.go │ └── zz_generated.deepcopy.go ├── bundle.Dockerfile ├── bundle ├── manifests │ ├── edp-keycloak-operator-clusterkeycloak-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-clusterkeycloak-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-clusterkeycloakrealm-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-clusterkeycloakrealm-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-controller-manager-metrics-service_v1_service.yaml │ ├── edp-keycloak-operator-keycloak-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloak-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakauthflow-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakauthflow-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakclient-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakclient-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakclientscope-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakclientscope-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealm-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealm-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmcomponent-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmcomponent-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmgroup-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmgroup-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmidentityprovider-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmidentityprovider-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmrole-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmrole-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmrolebatch-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmrolebatch-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmuser-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-keycloakrealmuser-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── edp-keycloak-operator.clusterserviceversion.yaml │ ├── v1.edp.epam.com_clusterkeycloakrealms.yaml │ ├── v1.edp.epam.com_clusterkeycloaks.yaml │ ├── v1.edp.epam.com_keycloakauthflows.yaml │ ├── v1.edp.epam.com_keycloakclients.yaml │ ├── v1.edp.epam.com_keycloakclientscopes.yaml │ ├── v1.edp.epam.com_keycloakrealmcomponents.yaml │ ├── v1.edp.epam.com_keycloakrealmgroups.yaml │ ├── v1.edp.epam.com_keycloakrealmidentityproviders.yaml │ ├── v1.edp.epam.com_keycloakrealmrolebatches.yaml │ ├── v1.edp.epam.com_keycloakrealmroles.yaml │ ├── v1.edp.epam.com_keycloakrealms.yaml │ ├── v1.edp.epam.com_keycloakrealmusers.yaml │ └── v1.edp.epam.com_keycloaks.yaml ├── metadata │ └── annotations.yaml └── tests │ └── scorecard │ └── config.yaml ├── cmd └── main.go ├── codecov.yaml ├── config ├── certmanager │ ├── certificate.yaml │ ├── kustomization.yaml │ └── kustomizeconfig.yaml ├── crd │ ├── bases │ │ ├── v1.edp.epam.com_clusterkeycloakrealms.yaml │ │ ├── v1.edp.epam.com_clusterkeycloaks.yaml │ │ ├── v1.edp.epam.com_keycloakauthflows.yaml │ │ ├── v1.edp.epam.com_keycloakclients.yaml │ │ ├── v1.edp.epam.com_keycloakclientscopes.yaml │ │ ├── v1.edp.epam.com_keycloakrealmcomponents.yaml │ │ ├── v1.edp.epam.com_keycloakrealmgroups.yaml │ │ ├── v1.edp.epam.com_keycloakrealmidentityproviders.yaml │ │ ├── v1.edp.epam.com_keycloakrealmrolebatches.yaml │ │ ├── v1.edp.epam.com_keycloakrealmroles.yaml │ │ ├── v1.edp.epam.com_keycloakrealms.yaml │ │ ├── v1.edp.epam.com_keycloakrealmusers.yaml │ │ └── v1.edp.epam.com_keycloaks.yaml │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── patches │ │ ├── cainjection_in_clusterkeycloakrealms.yaml │ │ ├── cainjection_in_clusterkeycloaks.yaml │ │ ├── cainjection_in_keycloakauthflows.yaml │ │ ├── cainjection_in_keycloakclients.yaml │ │ ├── cainjection_in_keycloakclientscopes.yaml │ │ ├── cainjection_in_keycloakrealmcomponents.yaml │ │ ├── cainjection_in_keycloakrealmgroups.yaml │ │ ├── cainjection_in_keycloakrealmidentityproviders.yaml │ │ ├── cainjection_in_keycloakrealmrolebatches.yaml │ │ ├── cainjection_in_keycloakrealmroles.yaml │ │ ├── cainjection_in_keycloakrealms.yaml │ │ ├── cainjection_in_keycloakrealmusers.yaml │ │ ├── cainjection_in_keycloaks.yaml │ │ ├── webhook_in_clusterkeycloakrealms.yaml │ │ ├── webhook_in_clusterkeycloaks.yaml │ │ ├── webhook_in_keycloakauthflows.yaml │ │ ├── webhook_in_keycloakclients.yaml │ │ ├── webhook_in_keycloakclientscopes.yaml │ │ ├── webhook_in_keycloakrealmcomponents.yaml │ │ ├── webhook_in_keycloakrealmgroups.yaml │ │ ├── webhook_in_keycloakrealmidentityproviders.yaml │ │ ├── webhook_in_keycloakrealmrolebatches.yaml │ │ ├── webhook_in_keycloakrealmroles.yaml │ │ ├── webhook_in_keycloakrealms.yaml │ │ ├── webhook_in_keycloakrealmusers.yaml │ │ └── webhook_in_keycloaks.yaml ├── default │ ├── kustomization.yaml │ ├── manager_metrics_patch.yaml │ ├── manager_webhook_patch.yaml │ ├── metrics_service.yaml │ └── webhookcainjection_patch.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── manifests │ ├── bases │ │ └── edp-keycloak-operator.clusterserviceversion.yaml │ └── kustomization.yaml ├── network-policy │ ├── allow-metrics-traffic.yaml │ ├── allow-webhook-traffic.yaml │ └── kustomization.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── clusterkeycloak_editor_role.yaml │ ├── clusterkeycloak_viewer_role.yaml │ ├── clusterkeycloakrealm_editor_role.yaml │ ├── clusterkeycloakrealm_viewer_role.yaml │ ├── keycloak_editor_role.yaml │ ├── keycloak_viewer_role.yaml │ ├── keycloakauthflow_editor_role.yaml │ ├── keycloakauthflow_viewer_role.yaml │ ├── keycloakclient_editor_role.yaml │ ├── keycloakclient_viewer_role.yaml │ ├── keycloakclientscope_editor_role.yaml │ ├── keycloakclientscope_viewer_role.yaml │ ├── keycloakrealm_editor_role.yaml │ ├── keycloakrealm_viewer_role.yaml │ ├── keycloakrealmcomponent_editor_role.yaml │ ├── keycloakrealmcomponent_viewer_role.yaml │ ├── keycloakrealmgroup_editor_role.yaml │ ├── keycloakrealmgroup_viewer_role.yaml │ ├── keycloakrealmidentityprovider_editor_role.yaml │ ├── keycloakrealmidentityprovider_viewer_role.yaml │ ├── keycloakrealmrole_editor_role.yaml │ ├── keycloakrealmrole_viewer_role.yaml │ ├── keycloakrealmrolebatch_editor_role.yaml │ ├── keycloakrealmrolebatch_viewer_role.yaml │ ├── keycloakrealmuser_editor_role.yaml │ ├── keycloakrealmuser_viewer_role.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── metrics_auth_role.yaml │ ├── metrics_auth_role_binding.yaml │ ├── metrics_reader_role.yaml │ ├── role.yaml │ ├── role_binding.yaml │ └── service_account.yaml ├── samples │ ├── kustomization.yaml │ ├── v1_v1_keycloak.yaml │ ├── v1_v1_keycloakauthflow.yaml │ ├── v1_v1_keycloakclient.yaml │ ├── v1_v1_keycloakclientscope.yaml │ ├── v1_v1_keycloakrealm.yaml │ ├── v1_v1_keycloakrealmcomponent.yaml │ ├── v1_v1_keycloakrealmgroup.yaml │ ├── v1_v1_keycloakrealmidentityprovider.yaml │ ├── v1_v1_keycloakrealmrole.yaml │ ├── v1_v1_keycloakrealmrolebatch.yaml │ ├── v1_v1_keycloakrealmuser.yaml │ ├── v1_v1alpha1_clusterkeycloak.yaml │ └── v1_v1alpha1_clusterkeycloakrealm.yaml └── scorecard │ ├── bases │ └── config.yaml │ ├── kustomization.yaml │ └── patches │ ├── basic.config.yaml │ └── olm.config.yaml ├── ct-configs ├── chart_schema.yaml ├── ct.yaml └── lintconf.yaml ├── deploy-templates ├── .helmignore ├── Chart.yaml ├── README.md ├── README.md.gotmpl ├── _crd_examples │ ├── clusterkeycloak.yaml │ ├── clusterkeycloakrealm.yaml │ ├── keycloak.yaml │ ├── keycloakauthflow.yaml │ ├── keycloakclient.yaml │ ├── keycloakclientscope.yaml │ ├── keycloakrealm.yaml │ ├── keycloakrealmcomponent.yaml │ ├── keycloakrealmgroup.yaml │ ├── keycloakrealmidentityprovider.yaml │ ├── keycloakrealmrole.yaml │ ├── keycloakrealmrolebatch.yaml │ ├── keycloakrealmuser.yaml │ └── keycloakrealmuser_password.yaml ├── crds │ ├── v1.edp.epam.com_clusterkeycloakrealms.yaml │ ├── v1.edp.epam.com_clusterkeycloaks.yaml │ ├── v1.edp.epam.com_keycloakauthflows.yaml │ ├── v1.edp.epam.com_keycloakclients.yaml │ ├── v1.edp.epam.com_keycloakclientscopes.yaml │ ├── v1.edp.epam.com_keycloakrealmcomponents.yaml │ ├── v1.edp.epam.com_keycloakrealmgroups.yaml │ ├── v1.edp.epam.com_keycloakrealmidentityproviders.yaml │ ├── v1.edp.epam.com_keycloakrealmrolebatches.yaml │ ├── v1.edp.epam.com_keycloakrealmroles.yaml │ ├── v1.edp.epam.com_keycloakrealms.yaml │ ├── v1.edp.epam.com_keycloakrealmusers.yaml │ └── v1.edp.epam.com_keycloaks.yaml ├── templates │ ├── _helpers.tpl │ ├── clusterrole.yaml │ ├── clusterrolebinding.yaml │ ├── deployment.yaml │ ├── leader_election_role.yaml │ ├── leader_election_rolebinding.yaml │ ├── operator_role.yaml │ ├── operator_rolebinding.yaml │ └── serviceaccount.yaml └── values.yaml ├── docs ├── api.md ├── arch.md └── puml │ └── arch.puml ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt ├── install-kuttl.sh ├── kind-1.30.yaml └── kind-1.31.yaml ├── internal └── controller │ ├── clusterkeycloak │ ├── clusterkeycloak_controller.go │ ├── clusterkeycloak_controller_integration_test.go │ └── suite_test.go │ ├── clusterkeycloakrealm │ ├── chain │ │ ├── auth_flow.go │ │ ├── auth_flow_test.go │ │ ├── chain.go │ │ ├── configure_email.go │ │ ├── configure_email_test.go │ │ ├── factory.go │ │ ├── put_realm.go │ │ ├── put_realm_settings.go │ │ ├── user_profile.go │ │ └── user_profile_test.go │ ├── clusterkeycloakrealm_controller.go │ ├── clusterkeycloakrealm_controller_integration_test.go │ ├── suite_test.go │ └── terminator.go │ ├── helper │ ├── controller_helper.go │ ├── controller_helper_auth.go │ ├── controller_helper_auth_test.go │ ├── controller_helper_failure_counter.go │ ├── controller_helper_test.go │ ├── k8s_client_mock.go │ └── mocks │ │ └── helper_mock.go │ ├── keycloak │ ├── keycloak_controller.go │ ├── keycloak_controller_integration_test.go │ └── suite_test.go │ ├── keycloakauthflow │ ├── keycloakauthflow_controller.go │ ├── keycloakrauthflow_controller_integration_test.go │ ├── suite_test.go │ ├── terminator.go │ └── terminator_test.go │ ├── keycloakclient │ ├── chain │ │ ├── chain.go │ │ ├── process_permissions.go │ │ ├── process_permissions_test.go │ │ ├── process_policy.go │ │ ├── process_policy_test.go │ │ ├── process_resources.go │ │ ├── process_resources_test.go │ │ ├── process_scope.go │ │ ├── process_scope_test.go │ │ ├── put_client.go │ │ ├── put_client_admin_fine_grained_perms.go │ │ ├── put_client_admin_fine_grained_perms_test.go │ │ ├── put_client_role.go │ │ ├── put_client_scope.go │ │ ├── put_client_scope_test.go │ │ ├── put_client_test.go │ │ ├── put_protocol_mappers.go │ │ ├── put_realm_role.go │ │ ├── service_account.go │ │ └── service_account_test.go │ ├── keycloakclient_controller.go │ ├── keycloakclient_controller_integration_test.go │ ├── suite_test.go │ ├── terminator.go │ └── terminator_test.go │ ├── keycloakclientscope │ ├── keycloakclientscope_controller.go │ ├── keycloakclientscope_controller_integration_test.go │ ├── suite_test.go │ ├── terminator.go │ └── terminator_test.go │ ├── keycloakrealm │ ├── chain │ │ ├── auth_flow.go │ │ ├── auth_flow_test.go │ │ ├── chain_test.go │ │ ├── configure_email.go │ │ ├── configure_email_test.go │ │ ├── factory.go │ │ ├── handler │ │ │ ├── mock_realm_handler.go │ │ │ └── realm_handler.go │ │ ├── realm.go │ │ ├── realm_settings.go │ │ ├── realm_settings_test.go │ │ ├── set_labels.go │ │ ├── user_profile.go │ │ ├── user_profile_test.go │ │ ├── users.go │ │ └── users_roles.go │ ├── keycloakrealm_controller.go │ ├── keycloakrealm_controller_integration_test.go │ ├── suite_test.go │ └── terminator.go │ ├── keycloakrealmcomponent │ ├── keycloakrealmcomponent_controller.go │ ├── keycloakrealmcomponent_controller_integration_test.go │ ├── keycloakrealmcomponent_controller_test.go │ ├── suite_test.go │ ├── terminator.go │ └── terminator_test.go │ ├── keycloakrealmgroup │ ├── keycloakrealmgroup_controller.go │ ├── keycloakrealmgroup_controller_integration_test.go │ ├── suite_test.go │ └── terminator.go │ ├── keycloakrealmidentityprovider │ ├── keycloakrealmidentityprovider_controller.go │ ├── keycloakrealmidentityprovider_controller_integration_test.go │ ├── suite_test.go │ ├── terminator.go │ └── terminator_test.go │ ├── keycloakrealmrole │ ├── keycloakrealmrole_controller.go │ ├── keycloakrealmrole_controller_integration_test.go │ ├── suite_test.go │ ├── terminator.go │ └── terminator_test.go │ ├── keycloakrealmrolebatch │ ├── keycloakrealmrolebatch_controller.go │ ├── keycloakrealmrolebatch_controller_test.go │ └── terminator.go │ └── keycloakrealmuser │ ├── keycloakrealmuser_controller.go │ ├── keycloakrealmuser_controller_integration_test.go │ ├── suite_test.go │ └── terminator.go ├── kuttl-test.yaml ├── pkg ├── client │ └── keycloak │ │ ├── adapter │ │ ├── component.go │ │ ├── component_test.go │ │ ├── errors.go │ │ ├── errors_test.go │ │ ├── gocloack_adapter_realm_event_test.go │ │ ├── gocloak.go │ │ ├── gocloak_adapter.go │ │ ├── gocloak_adapter_auth_flow.go │ │ ├── gocloak_adapter_auth_flow_test.go │ │ ├── gocloak_adapter_client.go │ │ ├── gocloak_adapter_client_scope.go │ │ ├── gocloak_adapter_client_scope_test.go │ │ ├── gocloak_adapter_client_test.go │ │ ├── gocloak_adapter_groups.go │ │ ├── gocloak_adapter_groups_test.go │ │ ├── gocloak_adapter_realm_event.go │ │ ├── gocloak_adapter_realms.go │ │ ├── gocloak_adapter_realms_test.go │ │ ├── gocloak_adapter_resty.go │ │ ├── gocloak_adapter_roles.go │ │ ├── gocloak_adapter_roles_test.go │ │ ├── gocloak_adapter_service_account.go │ │ ├── gocloak_adapter_service_account_test.go │ │ ├── gocloak_adapter_sync_entity_roles.go │ │ ├── gocloak_adapter_test.go │ │ ├── gocloak_adapter_user.go │ │ ├── gocloak_adapter_user_test.go │ │ ├── http.go │ │ ├── identity_provider.go │ │ ├── identity_provider_test.go │ │ └── mocks │ │ │ └── gocloak_mock.go │ │ ├── api │ │ └── identity_provider.go │ │ ├── dto │ │ └── keycloak_dto.go │ │ ├── keycloak_client.go │ │ ├── mock │ │ └── mock_logger.go │ │ └── mocks │ │ └── client_mock.go ├── fakehttp │ ├── server.go │ └── server_test.go ├── objectmeta │ ├── deletion.go │ └── deletion_test.go ├── secretref │ ├── mocks │ │ └── ref_mock.go │ ├── secretref.go │ ├── secretref_test.go │ ├── sourceref.go │ └── sourceref_test.go └── util │ └── cluster.go ├── pull_request_template.md ├── sonar-project.properties └── tests └── e2e ├── helm-success-path ├── 00-assert-operator.yaml ├── 00-install-operator.yaml ├── 01-assert-install-keycloak-server.yaml ├── 01-install-keycloak-server.yaml ├── 02-assert-keycloak-resource.yaml ├── 02-install-keycloak-resource.yaml ├── 03-assert-keycloak-user.yaml ├── 03-install-keycloak-user.yaml ├── 04-assert-component.yaml ├── 04-install-component.yaml ├── 05-assert-identityprovider.yaml ├── 05-install-identityprovider.yaml ├── 06-assert-group.yaml ├── 06-install-group.yaml ├── 07-assert-client.yaml ├── 07-install-client.yaml ├── 08-assert-role.yaml ├── 08-install-role.yaml └── 99-cleanup.yaml └── keycloak-selfsigned-certificates ├── 00-assert-operator.yaml ├── 00-install-operator.yaml ├── 01-assert-install-keycloak-server.yaml ├── 01-install-keycloak-server.yaml ├── 02-assert-keycloak-resource.yaml ├── 02-install-keycloak-resource.yaml ├── 03-assert-keycloak-realm-resource.yaml ├── 03-install-keycloak-realm-resource.yaml └── 99-cleanup.yaml /.chglog/CHANGELOG.tpl.md: -------------------------------------------------------------------------------- 1 | {{ if .Versions -}} 2 | 3 | ## [Unreleased] 4 | 5 | {{ if .Unreleased.CommitGroups -}} 6 | {{ range .Unreleased.CommitGroups -}} 7 | ### {{ .Title }} 8 | {{ range .Commits -}} 9 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} 10 | {{ end }} 11 | {{ end -}} 12 | {{ end -}} 13 | {{ end -}} 14 | 15 | {{ range .Versions }} 16 | 17 | ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} 18 | {{ range .CommitGroups -}} 19 | ### {{ .Title }} 20 | {{ range .Commits -}} 21 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} 22 | {{ end }} 23 | {{ end -}} 24 | 25 | {{- if .RevertCommits -}} 26 | ### Reverts 27 | {{ range .RevertCommits -}} 28 | - {{ .Revert.Header }} 29 | {{ end }} 30 | {{ end -}} 31 | 32 | {{- if .MergeCommits -}} 33 | ### Pull Requests 34 | {{ range .MergeCommits -}} 35 | - {{ .Header }} 36 | {{ end }} 37 | {{ end -}} 38 | 39 | {{- if .NoteGroups -}} 40 | {{ range .NoteGroups -}} 41 | ### {{ .Title }} 42 | {{ range .Notes }} 43 | {{ .Body }} 44 | {{ end }} 45 | {{ end -}} 46 | {{ end -}} 47 | {{ end -}} 48 | 49 | {{- if .Versions }} 50 | [Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD 51 | {{ range .Versions -}} 52 | {{ if .Tag.Previous -}} 53 | [{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} 54 | {{ end -}} 55 | {{ end -}} 56 | {{ end -}} 57 | -------------------------------------------------------------------------------- /.chglog/config.yml: -------------------------------------------------------------------------------- 1 | style: none 2 | template: CHANGELOG.tpl.md 3 | info: 4 | title: CHANGELOG 5 | repository_url: https://github.com/epam/edp-keycloak-operator 6 | 7 | options: 8 | tag_filter_pattern: '^v' 9 | sort: "semVer" 10 | 11 | commits: 12 | filters: 13 | Type: 14 | - chore 15 | - docs 16 | - feat 17 | - fix 18 | - refactor 19 | - style 20 | - test 21 | 22 | commit_groups: 23 | group_by: Type 24 | sort_by: Custom 25 | title_order: 26 | - feat 27 | - fix 28 | - refactor 29 | - style 30 | - test 31 | - chore 32 | - docs 33 | title_maps: 34 | chore: Routine 35 | docs: Documentation 36 | feat: Features 37 | fix: Bug Fixes 38 | refactor: Code Refactoring 39 | style: Formatting 40 | test: Testing 41 | 42 | header: 43 | pattern: "^(feat|fix|docs|style|refactor|test|chore)+!?:\\s(.*)$" 44 | pattern_maps: 45 | - Type 46 | - Subject 47 | 48 | notes: 49 | keywords: 50 | - "BREAKING CHANGE:" 51 | -------------------------------------------------------------------------------- /.chglog/release.tpl.md: -------------------------------------------------------------------------------- 1 | {{ range .Versions }} 2 | 3 | ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} 4 | {{ range .CommitGroups -}} 5 | ### {{ .Title }} 6 | 7 | {{ range .Commits -}} 8 | - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} 9 | {{ end }} 10 | {{ end -}} 11 | 12 | {{- if .NoteGroups -}} 13 | {{ range .NoteGroups -}} 14 | ### {{ .Title }} 15 | 16 | {{ range .Notes }} 17 | {{ .Body }} 18 | {{ end }} 19 | {{ end -}} 20 | {{ end -}} 21 | {{ end -}} 22 | 23 | {{- if .Versions }} 24 | {{ range .Versions -}} 25 | {{ if .Tag.Previous -}} 26 | [{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} 27 | {{ end -}} 28 | {{ end -}} 29 | {{ end -}} 30 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore build and test binaries. 3 | bin/ 4 | testbin/ 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | 15 | #### What did you do? 16 | 17 | 18 | 19 | #### What did you expect to see? 20 | 21 | 22 | 23 | #### What did you see instead? Under which circumstances? 24 | 25 | 26 | 27 | **Kubernetes cluster type:** 28 | 29 | 30 | 31 | `$ operator-sdk version` 32 | 33 | 34 | 35 | `$ go version` 36 | 37 | 38 | 39 | `$ kubectl version` 40 | 41 | 42 | 43 | **Screenshots** 44 | If applicable, add screenshots to help explain your problem. 45 | 46 | **Additional context** 47 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/workflows/codecov.yaml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | env: 8 | GOLANG_VERSION: '1.24' 9 | 10 | jobs: 11 | 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Set up Go 18 | uses: actions/setup-go@v5 19 | with: 20 | go-version: ${{ env.GOLANG_VERSION }} 21 | 22 | - name: Build 23 | run: make build 24 | 25 | - name: Test 26 | run: make test 27 | 28 | - name: Upload codecov 29 | uses: codecov/codecov-action@v3.1.4 30 | with: 31 | file: coverage.out 32 | -------------------------------------------------------------------------------- /.github/workflows/e2e.yaml: -------------------------------------------------------------------------------- 1 | name: "Run End-to-end tests" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | concurrency: 8 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 9 | cancel-in-progress: true 10 | 11 | env: 12 | GOLANG_VERSION: '1.24' 13 | 14 | jobs: 15 | e2e-tests: 16 | name: End-to-end tests 17 | runs-on: ubuntu-22.04 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | # The e2e tests are run on the lowest and highest supported k8s version. 22 | # All Kubernetes version in between expose the same APIs, hence the operator 23 | # should be compatible with them. 24 | kube-version: 25 | - "1.30" 26 | - "1.31" 27 | 28 | steps: 29 | - name: Check out code into the Go module directory 30 | uses: actions/checkout@v4 31 | 32 | - name: Set up Go 33 | uses: actions/setup-go@v5 34 | with: 35 | go-version: ${{ env.GOLANG_VERSION }} 36 | 37 | - name: "install kuttl" 38 | run: ./hack/install-kuttl.sh 39 | 40 | - name: "run tests" 41 | env: 42 | KUBE_VERSION: ${{ matrix.kube-version }} 43 | run: make start-kind KUBE_VERSION=$KUBE_VERSION && make e2e 44 | 45 | e2e-tests-check: 46 | runs-on: ubuntu-22.04 47 | if: always() 48 | needs: [e2e-tests] 49 | steps: 50 | - name: Print result 51 | run: echo ${{ needs.e2e-tests.result }} 52 | - name: Interpret result 53 | run: | 54 | if [[ success == ${{ needs.e2e-tests.result }} ]] 55 | then 56 | echo "All matrix jobs passed!" 57 | else 58 | echo "One or more matrix jobs failed." 59 | false 60 | fi 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary Build Files 2 | build/_output 3 | build/_test 4 | bin/ 5 | # Created by https://www.gitignore.io/api/go,vim,emacs,visualstudiocode 6 | ### Emacs ### 7 | # -*- mode: gitignore; -*- 8 | *~ 9 | \#*\# 10 | /.emacs.desktop 11 | /.emacs.desktop.lock 12 | *.elc 13 | auto-save-list 14 | tramp 15 | .\#* 16 | # Org-mode 17 | .org-id-locations 18 | *_archive 19 | # flymake-mode 20 | *_flymake.* 21 | # eshell files 22 | /eshell/history 23 | /eshell/lastdir 24 | # elpa packages 25 | /elpa/ 26 | # reftex files 27 | *.rel 28 | # AUCTeX auto folder 29 | /auto/ 30 | # cask packages 31 | .cask/ 32 | dist/ 33 | # Flycheck 34 | flycheck_*.el 35 | # server auth directory 36 | /server/ 37 | # projectiles files 38 | .projectile 39 | projectile-bookmarks.eld 40 | # directory configuration 41 | .dir-locals.el 42 | # saveplace 43 | places 44 | # url cache 45 | url/cache/ 46 | # cedet 47 | ede-projects.el 48 | # smex 49 | smex-items 50 | # company-statistics 51 | company-statistics-cache.el 52 | # anaconda-mode 53 | anaconda-mode/ 54 | ### Go ### 55 | # Binaries for programs and plugins 56 | *.exe 57 | *.exe~ 58 | *.dll 59 | *.so 60 | *.dylib 61 | # Test binary, build with 'go test -c' 62 | *.test 63 | # Output of the go coverage tool, specifically when used with LiteIDE 64 | *.out 65 | ### Vim ### 66 | # swap 67 | .sw[a-p] 68 | .*.sw[a-p] 69 | # session 70 | Session.vim 71 | # temporary 72 | .netrwhist 73 | # auto-generated tag files 74 | tags 75 | ### VisualStudioCode ### 76 | .vscode/* 77 | .history 78 | # End of https://www.gitignore.io/api/go,vim,emacs,visualstudiocode 79 | /vendor/ 80 | /.idea/ 81 | kubeconfig 82 | -------------------------------------------------------------------------------- /.mockery.yaml: -------------------------------------------------------------------------------- 1 | with-expecter: True 2 | inpackage: False 3 | dir: "{{.InterfaceDir}}/mocks" 4 | mockname: "Mock{{.InterfaceName}}" 5 | outpkg: "mocks" 6 | filename: "{{.InterfaceName | lower}}_mock.go" 7 | issue-845-fix: True 8 | packages: 9 | github.com/epam/edp-keycloak-operator/pkg/client/keycloak: 10 | interfaces: 11 | Client: 12 | github.com/epam/edp-keycloak-operator/pkg/client/keycloak/adapter: 13 | interfaces: 14 | GoCloak: 15 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # All 2 | ** @epam/edp-admin @zmotso 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Our community is built on a foundation of respect, inclusiveness, and openness. We believe that everyone should be able to participate and contribute, regardless of their background or identity. 4 | 5 | To ensure a positive experience for all, we have established this Code of Conduct. All members, participants, and contributors are expected to abide by these rules. 6 | 7 | ## Expectations 8 | - Be respectful of others, their opinions, and their work. 9 | - Avoid demeaning, discriminatory, or harassing behavior and speech. 10 | - Refrain from any form of bullying, trolling, or flaming. 11 | - Use inclusive language and be mindful of the impact your words may have on others. 12 | - Be open to constructive criticism and willing to learn from it. 13 | - Respect the privacy and confidentiality of others. 14 | 15 | ## Consequences 16 | - Participants who violate the Code of Conduct may be warned, asked to leave the community, or banned at the discretion of the community organizers. 17 | - If you experience any form of harassment, discrimination, or other Code of Conduct violation, please report it to the community organizers. 18 | 19 | ## Changes to the Code of Conduct 20 | - The Code of Conduct may be updated from time to time to better reflect the needs and expectations of the community. 21 | - All members, participants, and contributors are encouraged to periodically review the Code of Conduct to stay informed of any updates. 22 | 23 | ## Contact Information 24 | - If you have any questions or concerns about the Code of Conduct, please reach out to the community organizers at [SupportEPMD-EDP@epam.com](mailto:SupportEPMD-EDP@epam.com) -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use distroless as minimal base image to package the manager binary 2 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 3 | FROM gcr.io/distroless/static:nonroot 4 | ARG TARGETARCH 5 | WORKDIR / 6 | COPY ./dist/manager-${TARGETARCH} /manager 7 | 8 | USER 65532:65532 9 | 10 | ENTRYPOINT ["/manager"] 11 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | The Keycloak operator project maintains release branches for the three most recent minor releases. Applicable fixes, including security fixes, may be backported to those three release branches, depending on severity and feasibility. Please refer to CHANGELOG.md for details. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | Please report (suspected) security vulnerabilities to SupportEPMD-EDP@epam.com. You will receive a response from us within 48 hours. If the issue is confirmed, we will release a patch as soon as possible depending on complexity. 10 | -------------------------------------------------------------------------------- /api/v1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1 contains API Schema definitions for the v1 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=v1.edp.epam.com 4 | package v1 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects. 13 | GroupVersion = schema.GroupVersion{Group: "v1.edp.epam.com", Version: "v1"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme. 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | AddToScheme = SchemeBuilder.AddToScheme 19 | ) 20 | 21 | const ( 22 | // KeycloakRealmKind is a string value of the kind of KeycloakClient CR. 23 | KeycloakRealmKind = "KeycloakRealm" 24 | // KeycloakRealmComponentKind is a string value of the kind of KeycloakClient CR. 25 | KeycloakRealmComponentKind = "KeycloakRealmComponent" 26 | KeycloakKind = "Keycloak" 27 | ) 28 | -------------------------------------------------------------------------------- /api/v1/keycloak_types_test.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "testing" 4 | 5 | func TestKeycloak_GetAdminType(t *testing.T) { 6 | kc := Keycloak{} 7 | if kc.GetAdminType() != KeycloakAdminTypeUser { 8 | t.Fatal("wrong admin type returned") 9 | } 10 | 11 | kc.Spec.AdminType = KeycloakAdminTypeServiceAccount 12 | if kc.GetAdminType() != "serviceAccount" { 13 | t.Fatal("wring admin type returned") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // NOTE: Boilerplate only. Ignore this file. 2 | 3 | // Package v1alpha1 contains API Schema definitions for the v1 v1alpha1 API group 4 | // +kubebuilder:object:generate=true 5 | // +groupName=v1.edp.epam.com 6 | package v1alpha1 7 | 8 | import ( 9 | "k8s.io/apimachinery/pkg/runtime/schema" 10 | "sigs.k8s.io/controller-runtime/pkg/scheme" 11 | ) 12 | 13 | var ( 14 | // GroupVersion is group version used to register these objects. 15 | GroupVersion = schema.GroupVersion{Group: "v1.edp.epam.com", Version: "v1alpha1"} 16 | 17 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme. 18 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 19 | 20 | AddToScheme = SchemeBuilder.AddToScheme 21 | ) 22 | 23 | const ( 24 | ClusterKeycloakKind = "ClusterKeycloak" 25 | ClusterKeycloakRealmKind = "ClusterKeycloakRealm" 26 | ) 27 | -------------------------------------------------------------------------------- /bundle.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | # Core bundle labels. 4 | LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 5 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ 6 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ 7 | LABEL operators.operatorframework.io.bundle.package.v1=edp-keycloak-operator 8 | LABEL operators.operatorframework.io.bundle.channels.v1=stable 9 | LABEL operators.operatorframework.io.bundle.channel.default.v1=stable 10 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.39.2 11 | LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 12 | LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v4 13 | 14 | # Labels for testing. 15 | LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 16 | LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ 17 | 18 | # Copy files to locations specified by labels. 19 | COPY bundle/manifests /manifests/ 20 | COPY bundle/metadata /metadata/ 21 | COPY bundle/tests/scorecard /tests/scorecard/ 22 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-clusterkeycloak-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-clusterkeycloak-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - clusterkeycloaks 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - clusterkeycloaks/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-clusterkeycloak-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-clusterkeycloak-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - clusterkeycloaks 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - clusterkeycloaks/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-clusterkeycloakrealm-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-clusterkeycloakrealm-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - clusterkeycloakrealms 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - clusterkeycloakrealms/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-clusterkeycloakrealm-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-clusterkeycloakrealm-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - clusterkeycloakrealms 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - clusterkeycloakrealms/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-controller-manager-metrics-service_v1_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | control-plane: controller-manager 9 | name: edp-keycloak-operator-controller-manager-metrics-service 10 | spec: 11 | ports: 12 | - name: https 13 | port: 8443 14 | protocol: TCP 15 | targetPort: 8443 16 | selector: 17 | control-plane: controller-manager 18 | status: 19 | loadBalancer: {} 20 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloak-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloak-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloaks 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloaks/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloak-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloak-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloaks 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloaks/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakauthflow-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakauthflow-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakauthflows 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakauthflows/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakauthflow-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakauthflow-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakauthflows 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakauthflows/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakclient-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakclient-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakclients 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakclients/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakclient-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakclient-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakclients 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakclients/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakclientscope-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakclientscope-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakclientscopes 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakclientscopes/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakclientscope-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakclientscope-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakclientscopes 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakclientscopes/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealm-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealm-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealms 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealms/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealm-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealm-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealms 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealms/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmcomponent-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmcomponent-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmcomponents 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmcomponents/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmcomponent-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmcomponent-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmcomponents 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmcomponents/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmgroup-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmgroup-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmgroups 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmgroups/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmgroup-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmgroup-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmgroups 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmgroups/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmidentityprovider-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmidentityprovider-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmidentityproviders 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmidentityproviders/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmidentityprovider-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmidentityprovider-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmidentityproviders 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmidentityproviders/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmrole-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmrole-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmroles 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmroles/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmrole-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmrole-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmroles 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmroles/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmrolebatch-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmrolebatch-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmrolebatches 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmrolebatches/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmrolebatch-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmrolebatch-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmrolebatches 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmrolebatches/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmuser-editor-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmuser-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmusers 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmusers/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-keycloakrealmuser-viewer-role_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app.kubernetes.io/managed-by: kustomize 7 | app.kubernetes.io/name: keycloak-operator 8 | name: edp-keycloak-operator-keycloakrealmuser-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmusers 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmusers/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /bundle/manifests/edp-keycloak-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | name: edp-keycloak-operator-metrics-reader 6 | rules: 7 | - nonResourceURLs: 8 | - /metrics 9 | verbs: 10 | - get 11 | -------------------------------------------------------------------------------- /bundle/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | # Core bundle annotations. 3 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 6 | operators.operatorframework.io.bundle.package.v1: edp-keycloak-operator 7 | operators.operatorframework.io.bundle.channels.v1: stable 8 | operators.operatorframework.io.bundle.channel.default.v1: stable 9 | operators.operatorframework.io.metrics.builder: operator-sdk-v1.39.2 10 | operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 11 | operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v4 12 | 13 | # Annotations for OpenShift. 14 | com.redhat.openshift.versions: "v4.7-v4.17" 15 | 16 | # Annotations for testing. 17 | operators.operatorframework.io.test.mediatype.v1: scorecard+v1 18 | operators.operatorframework.io.test.config.v1: tests/scorecard/ 19 | -------------------------------------------------------------------------------- /bundle/tests/scorecard/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: 8 | - entrypoint: 9 | - scorecard-test 10 | - basic-check-spec 11 | image: quay.io/operator-framework/scorecard-test:v1.39.2 12 | labels: 13 | suite: basic 14 | test: basic-check-spec-test 15 | storage: 16 | spec: 17 | mountPath: {} 18 | - entrypoint: 19 | - scorecard-test 20 | - olm-bundle-validation 21 | image: quay.io/operator-framework/scorecard-test:v1.39.2 22 | labels: 23 | suite: olm 24 | test: olm-bundle-validation-test 25 | storage: 26 | spec: 27 | mountPath: {} 28 | - entrypoint: 29 | - scorecard-test 30 | - olm-crds-have-validation 31 | image: quay.io/operator-framework/scorecard-test:v1.39.2 32 | labels: 33 | suite: olm 34 | test: olm-crds-have-validation-test 35 | storage: 36 | spec: 37 | mountPath: {} 38 | - entrypoint: 39 | - scorecard-test 40 | - olm-crds-have-resources 41 | image: quay.io/operator-framework/scorecard-test:v1.39.2 42 | labels: 43 | suite: olm 44 | test: olm-crds-have-resources-test 45 | storage: 46 | spec: 47 | mountPath: {} 48 | - entrypoint: 49 | - scorecard-test 50 | - olm-spec-descriptors 51 | image: quay.io/operator-framework/scorecard-test:v1.39.2 52 | labels: 53 | suite: olm 54 | test: olm-spec-descriptors-test 55 | storage: 56 | spec: 57 | mountPath: {} 58 | - entrypoint: 59 | - scorecard-test 60 | - olm-status-descriptors 61 | image: quay.io/operator-framework/scorecard-test:v1.39.2 62 | labels: 63 | suite: olm 64 | test: olm-status-descriptors-test 65 | storage: 66 | spec: 67 | mountPath: {} 68 | storage: 69 | spec: 70 | mountPath: {} 71 | -------------------------------------------------------------------------------- /codecov.yaml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "**/*_test.go" 3 | - "**/*generated.*.go" 4 | - "**/factory.go" 5 | - "**/mock_*.go" 6 | -------------------------------------------------------------------------------- /config/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. 4 | apiVersion: cert-manager.io/v1 5 | kind: Issuer 6 | metadata: 7 | labels: 8 | app.kubernetes.io/name: keycloak-operator 9 | app.kubernetes.io/managed-by: kustomize 10 | name: selfsigned-issuer 11 | namespace: system 12 | spec: 13 | selfSigned: {} 14 | --- 15 | apiVersion: cert-manager.io/v1 16 | kind: Certificate 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: certificate 20 | app.kubernetes.io/instance: serving-cert 21 | app.kubernetes.io/component: certificate 22 | app.kubernetes.io/created-by: keycloak-operator 23 | app.kubernetes.io/part-of: keycloak-operator 24 | app.kubernetes.io/managed-by: kustomize 25 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 26 | namespace: system 27 | spec: 28 | # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize 29 | dnsNames: 30 | - SERVICE_NAME.SERVICE_NAMESPACE.svc 31 | - SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local 32 | issuerRef: 33 | kind: Issuer 34 | name: selfsigned-issuer 35 | secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize 36 | -------------------------------------------------------------------------------- /config/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /config/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_clusterkeycloakrealms.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: clusterkeycloakrealms.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_clusterkeycloaks.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: clusterkeycloaks.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakauthflows.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakauthflows.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakclients.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakclients.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakclientscopes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakclientscopes.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakrealmcomponents.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakrealmcomponents.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakrealmgroups.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakrealmgroups.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakrealmidentityproviders.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakrealmidentityproviders.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakrealmrolebatches.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakrealmrolebatches.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakrealmroles.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakrealmroles.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakrealms.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakrealms.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloakrealmusers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloakrealmusers.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_keycloaks.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 7 | name: keycloaks.v1.edp.epam.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_clusterkeycloakrealms.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: clusterkeycloakrealms.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_clusterkeycloaks.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: clusterkeycloaks.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakauthflows.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakauthflows.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakclients.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakclients.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakclientscopes.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakclientscopes.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakrealmcomponents.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakrealmcomponents.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakrealmgroups.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakrealmgroups.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakrealmidentityproviders.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakrealmidentityproviders.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakrealmrolebatches.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakrealmrolebatches.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakrealmroles.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakrealmroles.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakrealms.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakrealms.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloakrealmusers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloakrealmusers.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_keycloaks.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: keycloaks.v1.edp.epam.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/default/manager_metrics_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch adds the args to allow exposing the metrics endpoint using HTTPS 2 | - op: add 3 | path: /spec/template/spec/containers/0/args/0 4 | value: --metrics-bind-address=:8443 5 | -------------------------------------------------------------------------------- /config/default/manager_webhook_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | labels: 7 | app.kubernetes.io/name: keycloak-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | spec: 10 | template: 11 | spec: 12 | containers: 13 | - name: manager 14 | ports: 15 | - containerPort: 9443 16 | name: webhook-server 17 | protocol: TCP 18 | volumeMounts: 19 | - mountPath: /tmp/k8s-webhook-server/serving-certs 20 | name: cert 21 | readOnly: true 22 | volumes: 23 | - name: cert 24 | secret: 25 | defaultMode: 420 26 | secretName: webhook-server-cert 27 | -------------------------------------------------------------------------------- /config/default/metrics_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: controller-manager-metrics-service 9 | namespace: system 10 | spec: 11 | ports: 12 | - name: https 13 | port: 8443 14 | protocol: TCP 15 | targetPort: 8443 16 | selector: 17 | control-plane: controller-manager 18 | -------------------------------------------------------------------------------- /config/default/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config and 2 | # CERTIFICATE_NAMESPACE and CERTIFICATE_NAME will be substituted by kustomize 3 | apiVersion: admissionregistration.k8s.io/v1 4 | kind: MutatingWebhookConfiguration 5 | metadata: 6 | labels: 7 | app.kubernetes.io/name: keycloak-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | name: mutating-webhook-configuration 10 | annotations: 11 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 12 | --- 13 | apiVersion: admissionregistration.k8s.io/v1 14 | kind: ValidatingWebhookConfiguration 15 | metadata: 16 | labels: 17 | app.kubernetes.io/name: validatingwebhookconfiguration 18 | app.kubernetes.io/instance: validating-webhook-configuration 19 | app.kubernetes.io/component: webhook 20 | app.kubernetes.io/created-by: keycloak-operator 21 | app.kubernetes.io/part-of: keycloak-operator 22 | app.kubernetes.io/managed-by: kustomize 23 | name: validating-webhook-configuration 24 | annotations: 25 | cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME 26 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | images: 6 | - name: controller 7 | newName: docker.io/epamedp/keycloak-operator 8 | newTag: 1.27.1 9 | -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases/edp-keycloak-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | 9 | # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix. 10 | # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager. 11 | # These patches remove the unnecessary "cert" volume and its manager container volumeMount. 12 | #patches: 13 | #- target: 14 | # group: apps 15 | # version: v1 16 | # kind: Deployment 17 | # name: controller-manager 18 | # namespace: system 19 | # patch: |- 20 | # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. 21 | # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. 22 | # - op: remove 23 | 24 | # path: /spec/template/spec/containers/0/volumeMounts/0 25 | # # Remove the "cert" volume, since OLM will create and mount a set of certs. 26 | # # Update the indices in this path if adding or removing volumes in the manager's Deployment. 27 | # - op: remove 28 | # path: /spec/template/spec/volumes/0 29 | -------------------------------------------------------------------------------- /config/network-policy/allow-metrics-traffic.yaml: -------------------------------------------------------------------------------- 1 | # This NetworkPolicy allows ingress traffic 2 | # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those 3 | # namespaces are able to gathering data from the metrics endpoint. 4 | apiVersion: networking.k8s.io/v1 5 | kind: NetworkPolicy 6 | metadata: 7 | labels: 8 | app.kubernetes.io/name: keycloak-operator 9 | app.kubernetes.io/managed-by: kustomize 10 | name: allow-metrics-traffic 11 | namespace: system 12 | spec: 13 | podSelector: 14 | matchLabels: 15 | control-plane: controller-manager 16 | policyTypes: 17 | - Ingress 18 | ingress: 19 | # This allows ingress traffic from any namespace with the label metrics: enabled 20 | - from: 21 | - namespaceSelector: 22 | matchLabels: 23 | metrics: enabled # Only from namespaces with this label 24 | ports: 25 | - port: 8443 26 | protocol: TCP 27 | -------------------------------------------------------------------------------- /config/network-policy/allow-webhook-traffic.yaml: -------------------------------------------------------------------------------- 1 | # This NetworkPolicy allows ingress traffic to your webhook server running 2 | # as part of the controller-manager from specific namespaces and pods. CR(s) which uses webhooks 3 | # will only work when applied in namespaces labeled with 'webhook: enabled' 4 | apiVersion: networking.k8s.io/v1 5 | kind: NetworkPolicy 6 | metadata: 7 | labels: 8 | app.kubernetes.io/name: keycloak-operator 9 | app.kubernetes.io/managed-by: kustomize 10 | name: allow-webhook-traffic 11 | namespace: system 12 | spec: 13 | podSelector: 14 | matchLabels: 15 | control-plane: controller-manager 16 | policyTypes: 17 | - Ingress 18 | ingress: 19 | # This allows ingress traffic from any namespace with the label webhook: enabled 20 | - from: 21 | - namespaceSelector: 22 | matchLabels: 23 | webhook: enabled # Only from namespaces with this label 24 | ports: 25 | - port: 443 26 | protocol: TCP 27 | -------------------------------------------------------------------------------- /config/network-policy/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - allow-webhook-traffic.yaml 3 | - allow-metrics-traffic.yaml 4 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | # Prometheus Monitor Service (Metrics) 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | labels: 6 | control-plane: controller-manager 7 | app.kubernetes.io/name: keycloak-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | name: controller-manager-metrics-monitor 10 | namespace: system 11 | spec: 12 | endpoints: 13 | - path: /metrics 14 | port: https # Ensure this is the name of the port that exposes HTTPS metrics 15 | scheme: https 16 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 17 | tlsConfig: 18 | # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables 19 | # certificate verification. This poses a significant security risk by making the system vulnerable to 20 | # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between 21 | # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, 22 | # compromising the integrity and confidentiality of the information. 23 | # Please use the following options for secure configurations: 24 | # caFile: /etc/metrics-certs/ca.crt 25 | # certFile: /etc/metrics-certs/tls.crt 26 | # keyFile: /etc/metrics-certs/tls.key 27 | insecureSkipVerify: true 28 | selector: 29 | matchLabels: 30 | control-plane: controller-manager 31 | -------------------------------------------------------------------------------- /config/rbac/clusterkeycloak_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit clusterkeycloaks. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: clusterkeycloak-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - clusterkeycloaks 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - clusterkeycloaks/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/clusterkeycloak_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view clusterkeycloaks. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: clusterkeycloak-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - clusterkeycloaks 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - clusterkeycloaks/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/clusterkeycloakrealm_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit clusterkeycloakrealms. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: clusterkeycloakrealm-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - clusterkeycloakrealms 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - clusterkeycloakrealms/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/clusterkeycloakrealm_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view clusterkeycloakrealms. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: clusterkeycloakrealm-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - clusterkeycloakrealms 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - clusterkeycloakrealms/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloak_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloaks. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloak-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloaks 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloaks/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloak_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloaks. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloak-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloaks 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloaks/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakauthflow_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakauthflows. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakauthflow-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakauthflows 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakauthflows/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakauthflow_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakauthflows. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakauthflow-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakauthflows 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakauthflows/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakclient_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakclients. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakclient-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakclients 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakclients/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakclient_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakclients. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakclient-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakclients 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakclients/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakclientscope_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakclientscopes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakclientscope-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakclientscopes 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakclientscopes/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakclientscope_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakclientscopes. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakclientscope-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakclientscopes 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakclientscopes/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealm_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakrealms. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealm-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealms 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealms/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealm_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakrealms. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealm-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealms 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealms/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmcomponent_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakrealmcomponents. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmcomponent-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmcomponents 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmcomponents/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmcomponent_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakrealmcomponents. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmcomponent-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmcomponents 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmcomponents/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmgroup_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakrealmgroups. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmgroup-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmgroups 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmgroups/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmgroup_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakrealmgroups. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmgroup-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmgroups 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmgroups/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmidentityprovider_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakrealmidentityproviders. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmidentityprovider-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmidentityproviders 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmidentityproviders/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmidentityprovider_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakrealmidentityproviders. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmidentityprovider-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmidentityproviders 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmidentityproviders/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmrole_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakrealmroles. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmrole-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmroles 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmroles/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmrole_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakrealmroles. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmrole-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmroles 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmroles/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmrolebatch_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakrealmrolebatches. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmrolebatch-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmrolebatches 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmrolebatches/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmrolebatch_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakrealmrolebatches. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmrolebatch-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmrolebatches 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmrolebatches/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmuser_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit keycloakrealmusers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmuser-editor-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmusers 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - v1.edp.epam.com 24 | resources: 25 | - keycloakrealmusers/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/keycloakrealmuser_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view keycloakrealmusers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | name: keycloakrealmuser-viewer-role 9 | rules: 10 | - apiGroups: 11 | - v1.edp.epam.com 12 | resources: 13 | - keycloakrealmusers 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - v1.edp.epam.com 20 | resources: 21 | - keycloakrealmusers/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | labels: 7 | app.kubernetes.io/name: keycloak-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | rules: 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - configmaps 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - create 19 | - update 20 | - patch 21 | - delete 22 | - apiGroups: 23 | - coordination.k8s.io 24 | resources: 25 | - leases 26 | verbs: 27 | - get 28 | - list 29 | - watch 30 | - create 31 | - update 32 | - patch 33 | - delete 34 | - apiGroups: 35 | - "" 36 | resources: 37 | - events 38 | verbs: 39 | - create 40 | - patch 41 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: Role 11 | name: leader-election-role 12 | subjects: 13 | - kind: ServiceAccount 14 | name: controller-manager 15 | namespace: system 16 | -------------------------------------------------------------------------------- /config/rbac/metrics_auth_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-auth-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/metrics_auth_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: metrics-auth-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: metrics-auth-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/metrics_reader_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | labels: 6 | app.kubernetes.io/name: keycloak-operator 7 | app.kubernetes.io/managed-by: kustomize 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: Role 11 | name: manager-role 12 | subjects: 13 | - kind: ServiceAccount 14 | name: controller-manager 15 | namespace: system 16 | --- 17 | apiVersion: rbac.authorization.k8s.io/v1 18 | kind: ClusterRoleBinding 19 | metadata: 20 | name: manager-clusterrolebinding 21 | labels: 22 | app.kubernetes.io/name: keycloak-operator 23 | app.kubernetes.io/managed-by: kustomize 24 | roleRef: 25 | kind: ClusterRole 26 | name: manager-role 27 | apiGroup: rbac.authorization.k8s.io 28 | subjects: 29 | - kind: ServiceAccount 30 | name: controller-manager 31 | namespace: system 32 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | labels: 7 | app.kubernetes.io/name: keycloak-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - v1_v1_keycloak.yaml 4 | - v1_v1_keycloakauthflow.yaml 5 | - v1_v1_keycloakclient.yaml 6 | - v1_v1_keycloakclientscope.yaml 7 | - v1_v1_keycloakrealmcomponent.yaml 8 | - v1_v1_keycloakrealm.yaml 9 | - v1_v1_keycloakrealmgroup.yaml 10 | - v1_v1_keycloakrealmidentityprovider.yaml 11 | - v1_v1_keycloakrealmrole.yaml 12 | - v1_v1_keycloakrealmrolebatch.yaml 13 | - v1_v1_keycloakrealmuser.yaml 14 | - v1_v1alpha1_clusterkeycloak.yaml 15 | - v1_v1alpha1_clusterkeycloakrealm.yaml 16 | #+kubebuilder:scaffold:manifestskustomizesamples 17 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloak.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: Keycloak 3 | metadata: 4 | name: keycloak-sample 5 | spec: 6 | secret: my-keycloak-secret 7 | url: https://example.com 8 | 9 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakauthflow.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakAuthFlow 3 | metadata: 4 | name: keycloakauthflow-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | alias: MyBrowser 10 | description: browser with idp 11 | providerId: basic-flow 12 | topLevel: true 13 | builtIn: false 14 | authenticationExecutions: 15 | - authenticator: "auth-cookie" 16 | priority: 0 17 | requirement: "ALTERNATIVE" 18 | - authenticator: "identity-provider-redirector" 19 | priority: 1 20 | requirement: "REQUIRED" 21 | authenticatorConfig: 22 | alias: my-alias 23 | config: 24 | "defaultProvider": "my-alias" 25 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakclientscope.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakClientScope 3 | metadata: 4 | name: keycloakclientscope-sample 5 | spec: 6 | name: groups 7 | realmRef: 8 | name: keycloakrealm-sample 9 | kind: KeycloakRealm 10 | description: "Group Membership" 11 | protocol: openid-connect 12 | protocolMappers: 13 | - name: groups 14 | protocol: openid-connect 15 | protocolMapper: "oidc-group-membership-mapper" 16 | config: 17 | "access.token.claim": "true" 18 | "claim.name": "groups" 19 | "full.path": "false" 20 | "id.token.claim": "true" 21 | "userinfo.token.claim": "true" 22 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakrealm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealm 3 | metadata: 4 | name: keycloakrealm-sample 5 | spec: 6 | id: d1-id-kc-realm-name 7 | realmName: d2-id-kc-realm-name 8 | keycloakRef: 9 | name: keycloak-sample 10 | kind: Keycloak 11 | passwordPolicy: 12 | - type: "forceExpiredPasswordChange" 13 | value: "365" 14 | - type: "length" 15 | value: "8" 16 | realmEventConfig: 17 | adminEventsDetailsEnabled: false 18 | adminEventsEnabled: true 19 | enabledEventTypes: 20 | - UPDATE_CONSENT_ERROR 21 | - CLIENT_LOGIN 22 | eventsEnabled: true 23 | eventsExpiration: 15000 24 | eventsListeners: 25 | - jboss-logging 26 | userProfileConfig: 27 | unmanagedAttributePolicy: "ENABLED" 28 | attributes: 29 | - name: "test-attribute" 30 | displayName: "Test Attribute" 31 | required: 32 | roles: 33 | - "admin" 34 | scopes: 35 | - "profile" 36 | multivalued: true 37 | group: "test-group" 38 | permissions: 39 | edit: 40 | - "admin" 41 | view: 42 | - "admin" 43 | - "user" 44 | selector: 45 | scopes: 46 | - "profile" 47 | annotations: 48 | inputType: "text" 49 | validations: 50 | email: 51 | max-local-length: 52 | intVal: 64 53 | local-date: { } 54 | options: 55 | options: 56 | sliceVal: 57 | - "option1" 58 | - "option2" 59 | multivalued: 60 | min: 61 | stringVal: "1" 62 | max: 63 | stringVal: "10" 64 | groups: 65 | - name: "test-group" 66 | displayDescription: "Test Group" 67 | displayHeader: "Test Group" 68 | annotations: 69 | groupAnnotation: "groupAnnotation" 70 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakrealmcomponent.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmComponent 3 | metadata: 4 | name: keycloakrealmcomponent-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | name: cr-kerb-test 10 | providerId: kerberos 11 | providerType: "org.keycloak.storage.UserStorageProvider" 12 | config: 13 | allowPasswordAuthentication: ["true"] 14 | cachePolicy: ["EVICT_WEEKLY"] 15 | debug: ["true"] 16 | editMode: ["READ_ONLY"] 17 | enabled: ["true"] 18 | evictionDay: ["3"] 19 | evictionHour: ["5"] 20 | evictionMinute: ["7"] 21 | kerberosRealm: ["test-realm"] 22 | keyTab: ["test-key-tab"] 23 | priority: ["0"] 24 | serverPrincipal: ["srv-principal-test"] 25 | updateProfileFirstLogin: ["true"] 26 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakrealmgroup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmGroup 3 | metadata: 4 | name: keycloakrealmgroup-sample 5 | spec: 6 | name: ArgoCDAdmins 7 | realmRef: 8 | name: keycloakrealm-sample 9 | kind: KeycloakRealm 10 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakrealmidentityprovider.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmIdentityProvider 3 | metadata: 4 | name: keycloakrealmidentityprovider-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | alias: instagram 10 | authenticateByDefault: false 11 | enabled: true 12 | firstBrokerLoginFlowAlias: "first broker login" 13 | providerId: "instagram" 14 | config: 15 | clientId: "foo" 16 | clientSecret: "$secretName:secretKey" 17 | hideOnLoginPage: "true" 18 | syncMode: "IMPORT" 19 | useJwksUrl: "true" 20 | mappers: 21 | - name: "test3212" 22 | identityProviderMapper: "oidc-hardcoded-role-idp-mapper" 23 | identityProviderAlias: "instagram" 24 | config: 25 | role: "role-tr" 26 | syncMode: "INHERIT" 27 | - name: "test-33221" 28 | identityProviderMapper: "hardcoded-attribute-idp-mapper" 29 | identityProviderAlias: "instagram" 30 | config: 31 | attribute: "foo" 32 | "attribute.value": "bar" 33 | syncMode: "IMPORT" 34 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakrealmrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmRole 3 | metadata: 4 | name: keycloakrealmrole-sample 5 | spec: 6 | composite: true 7 | description: default developer role 8 | name: developer 9 | realmRef: 10 | name: keycloakrealm-sample 11 | kind: KeycloakRealm 12 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakrealmrolebatch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmRoleBatch 3 | metadata: 4 | name: keycloakrealmrolebatch-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | roles: 10 | - composite: true 11 | description: default developer role 12 | isDefault: false 13 | name: developer 14 | - composite: true 15 | description: default administrator role 16 | isDefault: false 17 | name: administrator 18 | -------------------------------------------------------------------------------- /config/samples/v1_v1_keycloakrealmuser.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmUser 3 | metadata: 4 | name: keycloakrealmuser-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | username: "john.snow13" 10 | firstName: "John" 11 | lastName: "Snow" 12 | email: "john.snow13@example.com" 13 | enabled: true 14 | emailVerified: true 15 | password: "12345678" 16 | keepResource: true 17 | requiredUserActions: 18 | - UPDATE_PASSWORD 19 | attributes: 20 | foo: "bar" 21 | baz: "jazz" 22 | -------------------------------------------------------------------------------- /config/samples/v1_v1alpha1_clusterkeycloak.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1alpha1 2 | kind: ClusterKeycloak 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterkeycloak 6 | app.kubernetes.io/instance: clusterkeycloak-sample 7 | app.kubernetes.io/part-of: edp-keycloak-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: edp-keycloak-operator 10 | name: clusterkeycloak-sample 11 | spec: 12 | secret: keycloak-access 13 | url: https://keycloak.example.com 14 | -------------------------------------------------------------------------------- /config/samples/v1_v1alpha1_clusterkeycloakrealm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1alpha1 2 | kind: ClusterKeycloakRealm 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: clusterkeycloakrealm 6 | app.kubernetes.io/instance: clusterkeycloakrealm-sample 7 | app.kubernetes.io/part-of: edp-keycloak-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: edp-keycloak-operator 10 | name: clusterkeycloakrealm-sample 11 | spec: 12 | clusterKeycloakRef: clusterkeycloak-sample 13 | realmName: realm-sample 14 | authenticationFlows: 15 | browserFlow: browserFlow-sample 16 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | apiVersion: kustomize.config.k8s.io/v1beta1 4 | kind: Kustomization 5 | patches: 6 | - path: patches/basic.config.yaml 7 | target: 8 | group: scorecard.operatorframework.io 9 | kind: Configuration 10 | name: config 11 | version: v1alpha3 12 | - path: patches/olm.config.yaml 13 | target: 14 | group: scorecard.operatorframework.io 15 | kind: Configuration 16 | name: config 17 | version: v1alpha3 18 | # +kubebuilder:scaffold:patches 19 | -------------------------------------------------------------------------------- /config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:v1.39.2 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:v1.39.2 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:v1.39.2 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:v1.39.2 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:v1.39.2 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.39.2 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /ct-configs/chart_schema.yaml: -------------------------------------------------------------------------------- 1 | name: str() 2 | home: str() 3 | version: str() 4 | type: str() 5 | apiVersion: str() 6 | appVersion: any(str(), num()) 7 | description: str() 8 | keywords: list(str(), required=False) 9 | sources: list(str(), required=True) 10 | maintainers: list(include('maintainer'), required=True) 11 | dependencies: list(include('dependency'), required=False) 12 | icon: str(required=False) 13 | engine: str(required=False) 14 | condition: str(required=False) 15 | tags: str(required=False) 16 | deprecated: bool(required=False) 17 | kubeVersion: str(required=False) 18 | annotations: map(str(), str(), required=False) 19 | --- 20 | maintainer: 21 | name: str(required=True) 22 | email: str(required=False) 23 | url: str(required=False) 24 | --- 25 | dependency: 26 | name: str() 27 | version: str() 28 | repository: str() 29 | condition: str(required=False) 30 | tags: list(str(), required=False) 31 | enabled: bool(required=False) 32 | import-values: any(list(str()), list(include('import-value')), required=False) 33 | alias: str(required=False) 34 | -------------------------------------------------------------------------------- /ct-configs/ct.yaml: -------------------------------------------------------------------------------- 1 | validate-maintainers: false 2 | -------------------------------------------------------------------------------- /ct-configs/lintconf.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | braces: 4 | min-spaces-inside: 0 5 | max-spaces-inside: 0 6 | min-spaces-inside-empty: -1 7 | max-spaces-inside-empty: -1 8 | brackets: 9 | min-spaces-inside: 0 10 | max-spaces-inside: 0 11 | min-spaces-inside-empty: -1 12 | max-spaces-inside-empty: -1 13 | colons: 14 | max-spaces-before: 0 15 | max-spaces-after: 1 16 | commas: 17 | max-spaces-before: 0 18 | min-spaces-after: 1 19 | max-spaces-after: 1 20 | comments: 21 | require-starting-space: true 22 | min-spaces-from-content: 2 23 | document-end: disable 24 | document-start: disable # No --- to start a file 25 | empty-lines: 26 | max: 2 27 | max-start: 0 28 | max-end: 0 29 | hyphens: 30 | max-spaces-after: 1 31 | indentation: 32 | spaces: consistent 33 | indent-sequences: whatever # - list indentation will handle both indentation and without 34 | check-multi-line-strings: false 35 | key-duplicates: enable 36 | line-length: disable # Lines can be any length 37 | new-line-at-end-of-file: enable 38 | new-lines: 39 | type: unix 40 | trailing-spaces: enable 41 | truthy: 42 | level: warning 43 | -------------------------------------------------------------------------------- /deploy-templates/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/clusterkeycloak.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1alpha1 2 | kind: ClusterKeycloak 3 | metadata: 4 | name: keycloak-sample 5 | spec: 6 | secret: keycloak-access 7 | url: https://keycloak.example.com 8 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/clusterkeycloakrealm.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1alpha1 2 | kind: ClusterKeycloakRealm 3 | metadata: 4 | name: clusterkeycloakrealm-sample 5 | spec: 6 | clusterKeycloakRef: clusterkeycloak-sample 7 | realmName: realm-sample1234 8 | authenticationFlows: 9 | browserFlow: browserFlow-sample 10 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloak.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: Keycloak 3 | metadata: 4 | name: keycloak-sample 5 | spec: 6 | secret: keycloak-access 7 | url: https://keycloak.example.com 8 | 9 | --- 10 | apiVersion: v1 11 | kind: Secret 12 | metadata: 13 | name: keycloak-access 14 | data: 15 | username: YWRtaW4= 16 | password: YWRtaW4= 17 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloakclientscope.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakClientScope 3 | metadata: 4 | name: keycloakclientscope-sample 5 | spec: 6 | name: groups 7 | realmRef: 8 | name: keycloakrealm-sample 9 | kind: KeycloakRealm 10 | description: "Group Membership" 11 | protocol: openid-connect 12 | protocolMappers: 13 | - name: groups 14 | protocol: openid-connect 15 | protocolMapper: "oidc-group-membership-mapper" 16 | config: 17 | "access.token.claim": "true" 18 | "claim.name": "groups" 19 | "full.path": "false" 20 | "id.token.claim": "true" 21 | "userinfo.token.claim": "true" 22 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloakrealmcomponent.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmComponent 3 | metadata: 4 | name: component-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | name: component-sample 10 | providerId: scope 11 | providerType: "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" 12 | 13 | --- 14 | 15 | apiVersion: v1.edp.epam.com/v1 16 | kind: KeycloakRealmComponent 17 | metadata: 18 | name: component-sample-child 19 | spec: 20 | realmRef: 21 | name: keycloakrealm-sample 22 | kind: KeycloakRealm 23 | name: component-sample-child 24 | providerId: scope 25 | providerType: "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" 26 | parentRef: 27 | name: component-sample 28 | kind: KeycloakRealmComponent 29 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloakrealmgroup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmGroup 3 | metadata: 4 | name: keycloakrealmgroup-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | name: ArgoCDAdmins 10 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloakrealmidentityprovider.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmIdentityProvider 3 | metadata: 4 | name: keycloakrealmidentityprovider-sample 5 | spec: 6 | realmRef: 7 | kind: KeycloakRealm 8 | name: realm 9 | alias: instagram 10 | authenticateByDefault: false 11 | enabled: true 12 | firstBrokerLoginFlowAlias: "first broker login" 13 | providerId: "instagram" 14 | config: 15 | clientId: "foo" 16 | clientSecret: "$secretName:secretKey" 17 | hideOnLoginPage: "true" 18 | syncMode: "IMPORT" 19 | useJwksUrl: "true" 20 | mappers: 21 | - name: "test-33221" 22 | identityProviderMapper: "hardcoded-attribute-idp-mapper" 23 | identityProviderAlias: "instagram" 24 | config: 25 | attribute: "foo" 26 | "attribute.value": "bar" 27 | syncMode: "IMPORT" 28 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloakrealmrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmRole 3 | metadata: 4 | name: keycloakrealmrole-sample 5 | spec: 6 | description: developer role 7 | name: test-role 8 | realmRef: 9 | name: keycloakrealm-sample 10 | kind: KeycloakRealm 11 | composite: true 12 | composites: 13 | - name: offline_access 14 | compositesClientRoles: 15 | broker: 16 | - name: read-token 17 | account: 18 | - name: manage-account 19 | - name: view-profile 20 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloakrealmrolebatch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmRoleBatch 3 | metadata: 4 | name: keycloakrealmrolebatch-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | roles: 10 | - description: default qa role 11 | isDefault: false 12 | name: qa 13 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloakrealmuser.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmUser 3 | metadata: 4 | name: keycloakrealmuser-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | username: "john.snow13" 10 | firstName: "John" 11 | lastName: "Snow" 12 | email: "john.snow13@example.com" 13 | enabled: true 14 | emailVerified: true 15 | keepResource: true 16 | requiredUserActions: 17 | - UPDATE_PASSWORD 18 | attributes: 19 | foo: "bar" 20 | baz: "jazz" 21 | -------------------------------------------------------------------------------- /deploy-templates/_crd_examples/keycloakrealmuser_password.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmUser 3 | metadata: 4 | name: keycloakrealmuser-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | username: "john.snow13" 10 | firstName: "John" 11 | lastName: "Snow" 12 | email: "john.snow13@example.com" 13 | enabled: true 14 | emailVerified: true 15 | keepResource: true 16 | attributes: 17 | foo: "bar" 18 | baz: "jazz" 19 | passwordSecret: 20 | name: existing-k8s-secret 21 | key: key-which-contains-password 22 | identityProviders: 23 | - provider-alias 24 | -------------------------------------------------------------------------------- /deploy-templates/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "keycloak-operator.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "keycloak-operator.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "keycloak-operator.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "keycloak-operator.labels" -}} 37 | helm.sh/chart: {{ include "keycloak-operator.chart" . }} 38 | {{ include "keycloak-operator.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "keycloak-operator.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "keycloak-operator.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "keycloak-operator.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "keycloak-operator.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /deploy-templates/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.clusterReconciliationEnabled }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: edp-{{ .Release.Namespace }}-servicebindings 6 | labels: 7 | {{- include "keycloak-operator.labels" . | nindent 4 }} 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: ClusterRole 11 | name: edp-{{ .Release.Namespace }}-clusterrole 12 | subjects: 13 | - kind: ServiceAccount 14 | name: edp-{{ .Values.name }} 15 | namespace: {{ .Release.Namespace }} 16 | {{- end}} 17 | -------------------------------------------------------------------------------- /deploy-templates/templates/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: edp-{{ .Values.name }}-leader-election-role 5 | labels: 6 | {{- include "keycloak-operator.labels" . | nindent 4 }} 7 | rules: 8 | - apiGroups: 9 | - "" 10 | resources: 11 | - configmaps 12 | verbs: 13 | - get 14 | - list 15 | - watch 16 | - create 17 | - update 18 | - patch 19 | - delete 20 | - apiGroups: 21 | - coordination.k8s.io 22 | resources: 23 | - leases 24 | verbs: 25 | - get 26 | - list 27 | - watch 28 | - create 29 | - update 30 | - patch 31 | - delete 32 | - apiGroups: 33 | - "" 34 | resources: 35 | - events 36 | verbs: 37 | - create 38 | - patch 39 | -------------------------------------------------------------------------------- /deploy-templates/templates/leader_election_rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: edp-{{ .Values.name }}-leader-election-rolebinding 5 | labels: 6 | {{- include "keycloak-operator.labels" . | nindent 4 }} 7 | roleRef: 8 | apiGroup: rbac.authorization.k8s.io 9 | kind: Role 10 | name: edp-{{ .Values.name }}-leader-election-role 11 | subjects: 12 | - kind: ServiceAccount 13 | name: edp-{{ .Values.name }} 14 | -------------------------------------------------------------------------------- /deploy-templates/templates/operator_rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: edp-{{ .Values.name }}-rolebinding 5 | labels: 6 | {{- include "keycloak-operator.labels" . | nindent 4 }} 7 | roleRef: 8 | apiGroup: rbac.authorization.k8s.io 9 | kind: Role 10 | name: edp-{{ .Values.name }}-role 11 | subjects: 12 | - kind: ServiceAccount 13 | name: edp-{{ .Values.name }} 14 | -------------------------------------------------------------------------------- /deploy-templates/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: edp-{{ .Values.name }} 5 | labels: 6 | {{- include "keycloak-operator.labels" . | nindent 4 }} 7 | -------------------------------------------------------------------------------- /docs/arch.md: -------------------------------------------------------------------------------- 1 | # Architecture Scheme of EDP Keycloak Operator 2 | 3 | This page contains a representation of the current EDP Keycloak Operator architecture that is built using the plantUML capabilities. 4 | All the diagrams sources are placed under the **/puml** directory of the current folder. 5 | 6 | An Image of the HEAD of the current branch is displayed as a result of an Image building with the plantUML proxy server. 7 | 8 | If you are in the detached mode, use the sources to get the required version of diagrams. 9 | 10 | ![arch](https://www.plantuml.com/plantuml/proxy?src=https://raw.githubusercontent.com/epam/edp-keycloak-operator/master/docs/puml/arch.puml) 11 | -------------------------------------------------------------------------------- /docs/puml/arch.puml: -------------------------------------------------------------------------------- 1 | @startuml keycloak-operator 2 | 3 | skinparam BackgroundColor transparent 4 | skinparam componentStyle rectangle 5 | 6 | component namespace { 7 | package "Keycloak operator" { 8 | [kind: KeycloakRealm] --> [kind: Keycloak] : spec.keycloakOwner 9 | [kind: KeycloakRealmRole] -left-> [kind: KeycloakRealm]: spec.realm 10 | [kind: KeycloakRealmRoleBatch] --> [kind: KeycloakRealm]: spec.realm 11 | [kind: KeycloakRealmIdentityProvider] --> [kind: KeycloakRealm]: spec.realm 12 | [kind: KeycloakRealmGroup] --> [kind: KeycloakRealm]: spec.realm 13 | [kind: KeycloakRealmComponent]--> [kind: KeycloakRealm]: spec.realm 14 | [kind: KeycloakClientScope] --> [kind: KeycloakRealm]: spec.realm 15 | [kind: KeycloakClient] --> [kind: KeycloakRealm]: spec.targetRealm 16 | [kind: KeycloakAuthFlow] --> [kind: KeycloakRealm]: spec.realm 17 | [kind: Keycloak] 18 | [kind: KeycloakRealmUser] -right-> [kind: KeycloakRealm]: spec.realm 19 | } 20 | [kind: Keycloak] ---> [kind: Secret]: spec.secret 21 | } 22 | 23 | [kind: Secret] <-- [Keycloak]: username, password 24 | [kind: Keycloak] ---> [Keycloak]: spec.url 25 | 26 | component [Keycloak] #Yellow 27 | component [kind: Secret] #SeaGreen 28 | 29 | @enduml 30 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epam/edp-keycloak-operator/a2913bd4f73d1ef366b1a6a9d865ae02b4bac8e8/hack/boilerplate.go.txt -------------------------------------------------------------------------------- /hack/install-kuttl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo curl -Lo /usr/local/bin/kubectl-kuttl https://github.com/kudobuilder/kuttl/releases/download/v0.22.0/kubectl-kuttl_0.22.0_linux_x86_64 4 | sudo chmod +x /usr/local/bin/kubectl-kuttl 5 | export PATH=$PATH:/usr/local/bin 6 | -------------------------------------------------------------------------------- /hack/kind-1.30.yaml: -------------------------------------------------------------------------------- 1 | kind: Cluster 2 | apiVersion: kind.x-k8s.io/v1alpha4 3 | nodes: 4 | - role: control-plane 5 | image: kindest/node:v1.30.10 6 | 7 | -------------------------------------------------------------------------------- /hack/kind-1.31.yaml: -------------------------------------------------------------------------------- 1 | kind: Cluster 2 | apiVersion: kind.x-k8s.io/v1alpha4 3 | nodes: 4 | - role: control-plane 5 | image: kindest/node:v1.31.6 6 | -------------------------------------------------------------------------------- /internal/controller/clusterkeycloak/clusterkeycloak_controller_integration_test.go: -------------------------------------------------------------------------------- 1 | package clusterkeycloak 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | . "github.com/onsi/ginkgo/v2" 8 | . "github.com/onsi/gomega" 9 | 10 | v1 "k8s.io/api/core/v1" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "k8s.io/apimachinery/pkg/types" 13 | 14 | keycloakAlpha "github.com/epam/edp-keycloak-operator/api/v1alpha1" 15 | ) 16 | 17 | var _ = Describe("ClusterKeycloak controller", func() { 18 | const ( 19 | timeout = time.Second * 10 20 | interval = time.Millisecond * 250 21 | keycloakName = "test-keycloak" 22 | keycloakSecretName = "keycloak-auth-secret" 23 | ) 24 | 25 | ctx := context.Background() 26 | 27 | It("Should create ClusterKeycloak object with secret auth", func() { 28 | By("By creating a secret") 29 | secret := &v1.Secret{ 30 | ObjectMeta: metav1.ObjectMeta{ 31 | Name: keycloakSecretName, 32 | Namespace: "default", 33 | }, 34 | Data: map[string][]byte{ 35 | "username": []byte("admin"), 36 | "password": []byte("admin"), 37 | }, 38 | } 39 | Expect(k8sClient.Create(ctx, secret)).Should(Succeed()) 40 | By("By creating a new ClusterKeycloak object") 41 | newKeycloak := &keycloakAlpha.ClusterKeycloak{ 42 | ObjectMeta: metav1.ObjectMeta{ 43 | Name: keycloakName, 44 | }, 45 | Spec: keycloakAlpha.ClusterKeycloakSpec{ 46 | Url: keycloakURL, 47 | Secret: keycloakSecretName, 48 | }, 49 | } 50 | Expect(k8sClient.Create(ctx, newKeycloak)).Should(Succeed()) 51 | Eventually(func() bool { 52 | createdKeycloak := &keycloakAlpha.ClusterKeycloak{} 53 | err := k8sClient.Get(ctx, types.NamespacedName{Name: keycloakName}, createdKeycloak) 54 | if err != nil { 55 | return false 56 | } 57 | return createdKeycloak.Status.Connected 58 | }, timeout, interval).Should(BeTrue()) 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /internal/controller/clusterkeycloakrealm/chain/auth_flow.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1alpha1" 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 11 | ) 12 | 13 | type AuthFlow struct { 14 | } 15 | 16 | func NewAuthFlow() *AuthFlow { 17 | return &AuthFlow{} 18 | } 19 | 20 | func (a AuthFlow) ServeRequest(ctx context.Context, realm *keycloakApi.ClusterKeycloakRealm, kClient keycloak.Client) error { 21 | log := ctrl.LoggerFrom(ctx) 22 | log.Info("Start configuring authentication flow") 23 | 24 | if realm.Spec.AuthenticationFlow == nil || realm.Spec.AuthenticationFlow.BrowserFlow == "" { 25 | log.Info("Authentication flow is not provided, skip configuring") 26 | return nil 27 | } 28 | 29 | if err := kClient.SetRealmBrowserFlow(ctx, realm.Spec.RealmName, realm.Spec.AuthenticationFlow.BrowserFlow); err != nil { 30 | return fmt.Errorf("setting realm browser flow: %w", err) 31 | } 32 | 33 | log.Info("Authentication flow has been configured") 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /internal/controller/clusterkeycloakrealm/chain/chain.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/api/v1alpha1" 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 11 | ) 12 | 13 | type RealmHandler interface { 14 | ServeRequest(ctx context.Context, realm *v1alpha1.ClusterKeycloakRealm, kClient keycloak.Client) error 15 | } 16 | 17 | type chain struct { 18 | handlers []RealmHandler 19 | } 20 | 21 | func (ch *chain) Use(handlers ...RealmHandler) { 22 | ch.handlers = append(ch.handlers, handlers...) 23 | } 24 | 25 | func (ch *chain) ServeRequest(ctx context.Context, realm *v1alpha1.ClusterKeycloakRealm, kClient keycloak.Client) error { 26 | log := ctrl.LoggerFrom(ctx) 27 | 28 | log.Info("Starting ClusterKeycloak chain") 29 | 30 | for i := 0; i < len(ch.handlers); i++ { 31 | h := ch.handlers[i] 32 | 33 | err := h.ServeRequest(ctx, realm, kClient) 34 | if err != nil { 35 | log.Info("ClusterKeycloak chain finished with error") 36 | 37 | return fmt.Errorf("failed to serve handler: %w", err) 38 | } 39 | } 40 | 41 | log.Info("Handling of ClusterKeycloak has been finished") 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /internal/controller/clusterkeycloakrealm/chain/configure_email.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | 10 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1alpha1" 11 | keycloakrealmchain "github.com/epam/edp-keycloak-operator/internal/controller/keycloakrealm/chain" 12 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 13 | ) 14 | 15 | type ConfigureEmail struct { 16 | client client.Client 17 | operatorNs string 18 | } 19 | 20 | func NewConfigureEmail(client client.Client, operatorNs string) *ConfigureEmail { 21 | return &ConfigureEmail{client: client, operatorNs: operatorNs} 22 | } 23 | 24 | func (s ConfigureEmail) ServeRequest(ctx context.Context, realm *keycloakApi.ClusterKeycloakRealm, kClient keycloak.Client) error { 25 | if realm.Spec.Smtp == nil { 26 | return nil 27 | } 28 | 29 | l := ctrl.LoggerFrom(ctx) 30 | l.Info("Configuring email for realm") 31 | 32 | if err := keycloakrealmchain.ConfigureRamlEmail( 33 | ctx, 34 | realm.Spec.RealmName, 35 | realm.Spec.Smtp, 36 | s.operatorNs, 37 | kClient, 38 | s.client, 39 | ); err != nil { 40 | return fmt.Errorf("failed to configure email: %w", err) 41 | } 42 | 43 | l.Info("Email has been configured") 44 | 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /internal/controller/clusterkeycloakrealm/chain/factory.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "sigs.k8s.io/controller-runtime/pkg/client" 5 | ) 6 | 7 | func MakeChain(c client.Client, operatorNs string) RealmHandler { 8 | ch := &chain{} 9 | ch.Use( 10 | NewPutRealm(c), 11 | NewPutRealmSettings(), 12 | NewUserProfile(), 13 | NewConfigureEmail(c, operatorNs), 14 | ) 15 | 16 | return ch 17 | } 18 | -------------------------------------------------------------------------------- /internal/controller/clusterkeycloakrealm/chain/put_realm.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | 10 | "github.com/epam/edp-keycloak-operator/api/v1alpha1" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 12 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/dto" 13 | ) 14 | 15 | type PutRealm struct { 16 | client client.Client 17 | } 18 | 19 | // NewPutRealm returns PutRealm chain handler. 20 | func NewPutRealm(client client.Client) *PutRealm { 21 | return &PutRealm{client: client} 22 | } 23 | 24 | func (h PutRealm) ServeRequest(ctx context.Context, realm *v1alpha1.ClusterKeycloakRealm, kClient keycloak.Client) error { 25 | log := ctrl.LoggerFrom(ctx) 26 | log.Info("Start putting realm") 27 | 28 | rDto := convertSpecToRealm(&realm.Spec) 29 | 30 | exist, err := kClient.ExistRealm(realm.Spec.RealmName) 31 | if err != nil { 32 | return fmt.Errorf("failed to check realm existence: %w", err) 33 | } 34 | 35 | if exist { 36 | log.Info("Realm already exists") 37 | 38 | return nil 39 | } 40 | 41 | err = kClient.CreateRealmWithDefaultConfig(rDto) 42 | if err != nil { 43 | return fmt.Errorf("failed to create realm: %w", err) 44 | } 45 | 46 | log.Info("Realm has been created") 47 | 48 | return nil 49 | } 50 | 51 | func convertSpecToRealm(spec *v1alpha1.ClusterKeycloakRealmSpec) *dto.Realm { 52 | return &dto.Realm{ 53 | Name: spec.RealmName, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /internal/controller/clusterkeycloakrealm/chain/user_profile.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/api/v1alpha1" 10 | keycloakrealmchain "github.com/epam/edp-keycloak-operator/internal/controller/keycloakrealm/chain" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 12 | ) 13 | 14 | type UserProfile struct { 15 | } 16 | 17 | func NewUserProfile() *UserProfile { 18 | return &UserProfile{} 19 | } 20 | 21 | func (h UserProfile) ServeRequest(ctx context.Context, realm *v1alpha1.ClusterKeycloakRealm, kClient keycloak.Client) error { 22 | l := ctrl.LoggerFrom(ctx) 23 | 24 | if realm.Spec.UserProfileConfig == nil { 25 | l.Info("User profile is empty, skipping configuration") 26 | 27 | return nil 28 | } 29 | 30 | l.Info("Start configuring keycloak realm user profile") 31 | 32 | err := keycloakrealmchain.ProcessUserProfile(ctx, realm.Spec.RealmName, realm.Spec.UserProfileConfig, kClient) 33 | if err != nil { 34 | return fmt.Errorf("unable to process user profile: %w", err) 35 | } 36 | 37 | l.Info("User profile has been configured") 38 | 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /internal/controller/clusterkeycloakrealm/terminator.go: -------------------------------------------------------------------------------- 1 | package clusterkeycloakrealm 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type terminator struct { 13 | realmName string 14 | kClient keycloak.Client 15 | preserveResourcesOnDeletion bool 16 | } 17 | 18 | func (t *terminator) DeleteResource(ctx context.Context) error { 19 | log := ctrl.LoggerFrom(ctx) 20 | if t.preserveResourcesOnDeletion { 21 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 22 | return nil 23 | } 24 | 25 | log.Info("Start deleting keycloak realm") 26 | 27 | if err := t.kClient.DeleteRealm(ctx, t.realmName); err != nil { 28 | return fmt.Errorf("failed to delete keycloak realm: %w", err) 29 | } 30 | 31 | log.Info("Realm has been deleted") 32 | 33 | return nil 34 | } 35 | 36 | func makeTerminator(realmName string, kClient keycloak.Client, preserveResourcesOnDeletion bool) *terminator { 37 | return &terminator{ 38 | realmName: realmName, 39 | kClient: kClient, 40 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /internal/controller/helper/controller_helper_failure_counter.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "time" 5 | 6 | "sigs.k8s.io/controller-runtime/pkg/event" 7 | ) 8 | 9 | type FailureCountable interface { 10 | GetFailureCount() int64 11 | SetFailureCount(count int64) 12 | } 13 | 14 | type StatusValue interface { 15 | GetStatus() string 16 | SetStatus(val string) 17 | } 18 | 19 | type StatusValueFailureCountable interface { 20 | FailureCountable 21 | StatusValue 22 | } 23 | 24 | func (h *Helper) SetFailureCount(fc FailureCountable) time.Duration { 25 | failures := fc.GetFailureCount() 26 | 27 | const timeoutSeconds = 10 28 | timeout := h.getTimeout(failures, timeoutSeconds*time.Second) 29 | failures += 1 30 | fc.SetFailureCount(failures) 31 | 32 | return timeout 33 | } 34 | 35 | func (h *Helper) getTimeout(factor int64, baseDuration time.Duration) time.Duration { 36 | return baseDuration * time.Duration(factor+1) 37 | } 38 | 39 | func IsFailuresUpdated(e event.UpdateEvent) bool { 40 | oo, ok := e.ObjectOld.(FailureCountable) 41 | if !ok { 42 | return false 43 | } 44 | 45 | no, ok := e.ObjectNew.(FailureCountable) 46 | if !ok { 47 | return false 48 | } 49 | 50 | return oo.GetFailureCount() == no.GetFailureCount() 51 | } 52 | 53 | func SetSuccessStatus(el StatusValueFailureCountable) { 54 | el.SetStatus(StatusOK) 55 | el.SetFailureCount(0) 56 | } 57 | -------------------------------------------------------------------------------- /internal/controller/keycloakclient/chain/chain.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | 10 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 12 | "github.com/epam/edp-keycloak-operator/pkg/secretref" 13 | ) 14 | 15 | type ClientHandler interface { 16 | Serve( 17 | ctx context.Context, 18 | keycloakClient *keycloakApi.KeycloakClient, 19 | realmName string, 20 | ) error 21 | } 22 | 23 | type Chain struct { 24 | handlers []ClientHandler 25 | } 26 | 27 | func (ch *Chain) Use(handlers ...ClientHandler) { 28 | ch.handlers = append(ch.handlers, handlers...) 29 | } 30 | 31 | func (ch *Chain) Serve( 32 | ctx context.Context, 33 | keycloakClient *keycloakApi.KeycloakClient, 34 | realmName string, 35 | ) error { 36 | log := ctrl.LoggerFrom(ctx) 37 | 38 | log.Info("Starting KeycloakClient chain") 39 | 40 | for i := 0; i < len(ch.handlers); i++ { 41 | h := ch.handlers[i] 42 | 43 | err := h.Serve(ctx, keycloakClient, realmName) 44 | if err != nil { 45 | log.Info("KeycloakClient chain finished with error") 46 | 47 | return fmt.Errorf("failed to serve handler: %w", err) 48 | } 49 | } 50 | 51 | log.Info("Handling of KeycloakClient has been finished") 52 | 53 | return nil 54 | } 55 | 56 | func MakeChain( 57 | keycloakApiClient keycloak.Client, 58 | k8sClient client.Client, 59 | ) *Chain { 60 | c := &Chain{} 61 | 62 | c.Use( 63 | NewPutClient(keycloakApiClient, k8sClient, secretref.NewSecretRef(k8sClient)), 64 | NewPutClientRole(keycloakApiClient), 65 | NewPutRealmRole(keycloakApiClient), 66 | NewPutClientScope(keycloakApiClient), 67 | NewPutProtocolMappers(keycloakApiClient), 68 | NewServiceAccount(keycloakApiClient), 69 | NewProcessScope(keycloakApiClient), 70 | NewProcessResources(keycloakApiClient), 71 | NewProcessPolicy(keycloakApiClient), 72 | NewProcessPermissions(keycloakApiClient), 73 | NewPutAdminFineGrainedPermissions(keycloakApiClient), 74 | ) 75 | 76 | return c 77 | } 78 | -------------------------------------------------------------------------------- /internal/controller/keycloakclient/chain/put_client_role.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/dto" 12 | ) 13 | 14 | type PutClientRole struct { 15 | keycloakApiClient keycloak.Client 16 | } 17 | 18 | func NewPutClientRole(keycloakApiClient keycloak.Client) *PutClientRole { 19 | return &PutClientRole{keycloakApiClient: keycloakApiClient} 20 | } 21 | 22 | func (el *PutClientRole) Serve(ctx context.Context, keycloakClient *keycloakApi.KeycloakClient, realmName string) error { 23 | if err := el.putKeycloakClientRole(ctx, keycloakClient, realmName); err != nil { 24 | return errors.Wrap(err, "unable to put keycloak client role") 25 | } 26 | 27 | return nil 28 | } 29 | 30 | func (el *PutClientRole) putKeycloakClientRole(ctx context.Context, keycloakClient *keycloakApi.KeycloakClient, realmName string) error { 31 | reqLog := ctrl.LoggerFrom(ctx) 32 | reqLog.Info("Start put keycloak client role") 33 | 34 | clientDto := dto.ConvertSpecToClient(&keycloakClient.Spec, "", realmName, nil) 35 | 36 | for _, role := range clientDto.Roles { 37 | exist, err := el.keycloakApiClient.ExistClientRole(clientDto, role) 38 | if err != nil { 39 | return errors.Wrap(err, "error during ExistClientRole") 40 | } 41 | 42 | if exist { 43 | reqLog.Info("Client role already exists", "role", role) 44 | continue 45 | } 46 | 47 | if err := el.keycloakApiClient.CreateClientRole(clientDto, role); err != nil { 48 | return errors.Wrap(err, "unable to create client role") 49 | } 50 | } 51 | 52 | reqLog.Info("End put keycloak client role") 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /internal/controller/keycloakclient/chain/put_protocol_mappers.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "maps" 6 | 7 | "github.com/Nerzal/gocloak/v12" 8 | "github.com/pkg/errors" 9 | 10 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 12 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/dto" 13 | ) 14 | 15 | type PutProtocolMappers struct { 16 | keycloakApiClient keycloak.Client 17 | } 18 | 19 | func NewPutProtocolMappers(keycloakApiClient keycloak.Client) *PutProtocolMappers { 20 | return &PutProtocolMappers{keycloakApiClient: keycloakApiClient} 21 | } 22 | 23 | func (el *PutProtocolMappers) Serve(_ context.Context, keycloakClient *keycloakApi.KeycloakClient, realmName string) error { 24 | if err := el.putProtocolMappers(keycloakClient, realmName); err != nil { 25 | return errors.Wrap(err, "unable to put protocol mappers") 26 | } 27 | 28 | return nil 29 | } 30 | 31 | func (el *PutProtocolMappers) putProtocolMappers(keycloakClient *keycloakApi.KeycloakClient, realmName string) error { 32 | var protocolMappers []gocloak.ProtocolMapperRepresentation 33 | 34 | if keycloakClient.Spec.ProtocolMappers != nil { 35 | protocolMappers = make([]gocloak.ProtocolMapperRepresentation, 0, 36 | len(*keycloakClient.Spec.ProtocolMappers)) 37 | 38 | for _, mapper := range *keycloakClient.Spec.ProtocolMappers { 39 | configCopy := make(map[string]string, len(mapper.Config)) 40 | maps.Copy(configCopy, mapper.Config) 41 | 42 | protocolMappers = append(protocolMappers, gocloak.ProtocolMapperRepresentation{ 43 | Name: gocloak.StringP(mapper.Name), 44 | Protocol: gocloak.StringP(mapper.Protocol), 45 | ProtocolMapper: gocloak.StringP(mapper.ProtocolMapper), 46 | Config: &configCopy, 47 | }) 48 | } 49 | } 50 | 51 | if err := el.keycloakApiClient.SyncClientProtocolMapper( 52 | dto.ConvertSpecToClient(&keycloakClient.Spec, "", realmName, nil), 53 | protocolMappers, keycloakClient.GetReconciliationStrategy() == keycloakApi.ReconciliationStrategyAddOnly); err != nil { 54 | return errors.Wrap(err, "unable to sync protocol mapper") 55 | } 56 | 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /internal/controller/keycloakclient/chain/put_realm_role.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/dto" 12 | ) 13 | 14 | type PutRealmRole struct { 15 | keycloakApiClient keycloak.Client 16 | } 17 | 18 | func NewPutRealmRole(keycloakApiClient keycloak.Client) *PutRealmRole { 19 | return &PutRealmRole{keycloakApiClient: keycloakApiClient} 20 | } 21 | 22 | func (el *PutRealmRole) Serve(ctx context.Context, keycloakClient *keycloakApi.KeycloakClient, realmName string) error { 23 | if err := el.putRealmRoles(ctx, keycloakClient, realmName); err != nil { 24 | return errors.Wrap(err, "unable to put realm roles") 25 | } 26 | 27 | return nil 28 | } 29 | 30 | func (el *PutRealmRole) putRealmRoles(ctx context.Context, keycloakClient *keycloakApi.KeycloakClient, realmName string) error { 31 | reqLog := ctrl.LoggerFrom(ctx) 32 | reqLog.Info("Start put realm roles") 33 | 34 | if keycloakClient.Spec.RealmRoles == nil || len(*keycloakClient.Spec.RealmRoles) == 0 { 35 | reqLog.Info("Keycloak client does not have realm roles") 36 | return nil 37 | } 38 | 39 | for _, role := range *keycloakClient.Spec.RealmRoles { 40 | roleDto := &dto.IncludedRealmRole{ 41 | Name: role.Name, 42 | Composite: role.Composite, 43 | } 44 | 45 | exist, err := el.keycloakApiClient.ExistRealmRole(realmName, roleDto.Name) 46 | if err != nil { 47 | return errors.Wrap(err, "error during ExistRealmRole") 48 | } 49 | 50 | if exist { 51 | reqLog.Info("Client already exists") 52 | return nil 53 | } 54 | 55 | err = el.keycloakApiClient.CreateIncludedRealmRole(realmName, roleDto) 56 | if err != nil { 57 | return errors.Wrap(err, "error during CreateRealmRole") 58 | } 59 | } 60 | 61 | reqLog.Info("End put realm roles") 62 | 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /internal/controller/keycloakclient/chain/service_account.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | 8 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type ServiceAccount struct { 13 | keycloakApiClient keycloak.Client 14 | } 15 | 16 | func NewServiceAccount(keycloakApiClient keycloak.Client) *ServiceAccount { 17 | return &ServiceAccount{keycloakApiClient: keycloakApiClient} 18 | } 19 | 20 | func (el *ServiceAccount) Serve(_ context.Context, keycloakClient *keycloakApi.KeycloakClient, realmName string) error { 21 | if keycloakClient.Spec.ServiceAccount == nil || !keycloakClient.Spec.ServiceAccount.Enabled { 22 | return nil 23 | } 24 | 25 | if keycloakClient.Spec.ServiceAccount != nil && keycloakClient.Spec.Public { 26 | return errors.New("service account can not be configured with public client") 27 | } 28 | 29 | clientRoles := make(map[string][]string) 30 | for _, v := range keycloakClient.Spec.ServiceAccount.ClientRoles { 31 | clientRoles[v.ClientID] = v.Roles 32 | } 33 | 34 | addOnly := keycloakClient.GetReconciliationStrategy() == keycloakApi.ReconciliationStrategyAddOnly 35 | 36 | if err := el.keycloakApiClient.SyncServiceAccountRoles(realmName, 37 | keycloakClient.Status.ClientID, keycloakClient.Spec.ServiceAccount.RealmRoles, clientRoles, addOnly); err != nil { 38 | return errors.Wrap(err, "unable to sync service account roles") 39 | } 40 | 41 | if keycloakClient.Spec.ServiceAccount.Groups != nil { 42 | if err := el.keycloakApiClient.SyncServiceAccountGroups(realmName, 43 | keycloakClient.Status.ClientID, keycloakClient.Spec.ServiceAccount.Groups, addOnly); err != nil { 44 | return errors.Wrap(err, "unable to sync service account groups") 45 | } 46 | } 47 | 48 | if keycloakClient.Spec.ServiceAccount.Attributes != nil { 49 | if err := el.keycloakApiClient.SetServiceAccountAttributes(realmName, keycloakClient.Status.ClientID, 50 | keycloakClient.Spec.ServiceAccount.Attributes, addOnly); err != nil { 51 | return errors.Wrap(err, "unable to set service account attributes") 52 | } 53 | } 54 | 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /internal/controller/keycloakclient/chain/service_account_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/epam/edp-keycloak-operator/api/common" 10 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks" 12 | ) 13 | 14 | func TestServiceAccount_Serve(t *testing.T) { 15 | kc := keycloakApi.KeycloakClient{ 16 | Spec: keycloakApi.KeycloakClientSpec{ 17 | RealmRef: common.RealmRef{ 18 | Kind: keycloakApi.KeycloakRealmKind, 19 | Name: "realm", 20 | }, 21 | ServiceAccount: &keycloakApi.ServiceAccount{ 22 | Enabled: true, 23 | Attributes: map[string]string{ 24 | "foo": "bar", 25 | }, 26 | ClientRoles: []keycloakApi.ClientRole{ 27 | { 28 | ClientID: "clid2", 29 | Roles: []string{"foo", "bar"}, 30 | }, 31 | }, 32 | RealmRoles: []string{"baz", "zaz"}, 33 | Groups: []string{"group1", "group2"}, 34 | }, 35 | }, 36 | Status: keycloakApi.KeycloakClientStatus{ 37 | ClientID: "clid1", 38 | }, 39 | } 40 | 41 | realmName := "realm" 42 | apiClient := mocks.NewMockClient(t) 43 | 44 | apiClient.On("SyncServiceAccountRoles", realmName, kc.Status.ClientID, 45 | kc.Spec.ServiceAccount.RealmRoles, 46 | map[string][]string{ 47 | kc.Spec.ServiceAccount.ClientRoles[0].ClientID: kc.Spec.ServiceAccount.ClientRoles[0].Roles}, false).Return(nil) 48 | apiClient.On("SyncServiceAccountGroups", realmName, kc.Status.ClientID, 49 | kc.Spec.ServiceAccount.Groups, false).Return(nil) 50 | apiClient.On("SetServiceAccountAttributes", realmName, kc.Status.ClientID, 51 | kc.Spec.ServiceAccount.Attributes, false).Return(nil) 52 | 53 | sa := NewServiceAccount(apiClient) 54 | 55 | err := sa.Serve(context.Background(), &kc, realmName) 56 | require.NoError(t, err) 57 | } 58 | -------------------------------------------------------------------------------- /internal/controller/keycloakclient/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakclient 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type terminator struct { 13 | clientID, realmName string 14 | kClient keycloak.Client 15 | preserveResourcesOnDeletion bool 16 | } 17 | 18 | func makeTerminator(clientID, realmName string, kClient keycloak.Client, preserveResourcesOnDeletion bool) *terminator { 19 | return &terminator{ 20 | clientID: clientID, 21 | realmName: realmName, 22 | kClient: kClient, 23 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 24 | } 25 | } 26 | 27 | func (t *terminator) DeleteResource(ctx context.Context) error { 28 | log := ctrl.LoggerFrom(ctx).WithValues("client_id", t.clientID) 29 | if t.preserveResourcesOnDeletion { 30 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 31 | return nil 32 | } 33 | 34 | log.Info("Start deleting keycloak client") 35 | 36 | if err := t.kClient.DeleteClient(ctx, t.clientID, t.realmName); err != nil { 37 | return fmt.Errorf("failed to delete keycloak client: %w", err) 38 | } 39 | 40 | log.Info("Keycloak client has been deleted") 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /internal/controller/keycloakclient/terminator_test.go: -------------------------------------------------------------------------------- 1 | package keycloakclient 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/mock" 9 | "github.com/stretchr/testify/require" 10 | 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks" 12 | ) 13 | 14 | func TestTerminator(t *testing.T) { 15 | kClient := mocks.NewMockClient(t) 16 | 17 | term := makeTerminator("realm", "client", kClient, false) 18 | 19 | kClient.On("DeleteClient", mock.Anything, "realm", "client").Return(nil).Once() 20 | 21 | err := term.DeleteResource(context.Background()) 22 | require.NoError(t, err) 23 | 24 | kClient.On("DeleteClient", mock.Anything, "realm", "client").Return(errors.New("fatal")).Once() 25 | 26 | if err := term.DeleteResource(context.Background()); err == nil { 27 | t.Fatal("no error returned") 28 | } 29 | } 30 | 31 | func TestTerminatorSkipDeletion(t *testing.T) { 32 | term := makeTerminator( 33 | "realm", 34 | "client", 35 | nil, 36 | true, 37 | ) 38 | 39 | err := term.DeleteResource(context.Background()) 40 | require.NoError(t, err) 41 | } 42 | -------------------------------------------------------------------------------- /internal/controller/keycloakclientscope/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakclientscope 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type terminator struct { 13 | realmName, scopeID string 14 | kClient keycloak.Client 15 | preserveResourcesOnDeletion bool 16 | } 17 | 18 | func makeTerminator(kClient keycloak.Client, realmName, scopeID string, preserveResourcesOnDeletion bool) *terminator { 19 | return &terminator{ 20 | kClient: kClient, 21 | realmName: realmName, 22 | scopeID: scopeID, 23 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 24 | } 25 | } 26 | 27 | func (t *terminator) DeleteResource(ctx context.Context) error { 28 | log := ctrl.LoggerFrom(ctx).WithValues("realm name", t.realmName, "scope id", t.scopeID) 29 | if t.preserveResourcesOnDeletion { 30 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 31 | return nil 32 | } 33 | 34 | log.Info("Start deleting client scope") 35 | 36 | if err := t.kClient.DeleteClientScope(ctx, t.realmName, t.scopeID); err != nil { 37 | return fmt.Errorf("failed to delete client scope: %w", err) 38 | } 39 | 40 | log.Info("Client scope has been deleted") 41 | 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /internal/controller/keycloakclientscope/terminator_test.go: -------------------------------------------------------------------------------- 1 | package keycloakclientscope 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/pkg/errors" 9 | testifymock "github.com/stretchr/testify/mock" 10 | "github.com/stretchr/testify/require" 11 | ctrl "sigs.k8s.io/controller-runtime" 12 | 13 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mock" 14 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks" 15 | ) 16 | 17 | func TestTerminator_DeleteResource(t *testing.T) { 18 | logger := mock.NewLogr() 19 | kClient := mocks.NewMockClient(t) 20 | kClient.On("DeleteClientScope", testifymock.Anything, "foo", "bar").Return(nil).Once() 21 | term := makeTerminator(kClient, "foo", "bar", false) 22 | err := term.DeleteResource(context.Background()) 23 | require.NoError(t, err) 24 | 25 | kClient.On("DeleteClientScope", testifymock.Anything, "foo", "bar").Return(errors.New("fatal")).Once() 26 | 27 | err = term.DeleteResource(ctrl.LoggerInto(context.Background(), logger)) 28 | require.Error(t, err) 29 | 30 | if !strings.Contains(err.Error(), "failed to delete client scope") { 31 | t.Fatalf("wrong error logged: %s", err.Error()) 32 | } 33 | } 34 | 35 | func TestTerminatorSkipDeletion(t *testing.T) { 36 | term := makeTerminator( 37 | nil, 38 | "realm", 39 | "scope", 40 | true, 41 | ) 42 | 43 | err := term.DeleteResource(context.Background()) 44 | require.NoError(t, err) 45 | } 46 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/chain/auth_flow.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | 8 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 9 | "github.com/epam/edp-keycloak-operator/internal/controller/keycloakrealm/chain/handler" 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 11 | ) 12 | 13 | type AuthFlow struct { 14 | next handler.RealmHandler 15 | } 16 | 17 | func (a AuthFlow) ServeRequest(ctx context.Context, realm *keycloakApi.KeycloakRealm, kClient keycloak.Client) error { 18 | rLog := log.WithValues("realm name", realm.Spec.RealmName) 19 | rLog.Info("Start configuring keycloak realm auth flow", "flow", realm.Spec.BrowserFlow) 20 | 21 | if realm.Spec.BrowserFlow == nil { 22 | rLog.Info("Browser flow is empty, exit") 23 | return nextServeOrNil(ctx, a.next, realm, kClient) 24 | } 25 | 26 | if err := kClient.SetRealmBrowserFlow(ctx, realm.Spec.RealmName, *realm.Spec.BrowserFlow); err != nil { 27 | return errors.Wrap(err, "unable to set realm auth flow") 28 | } 29 | 30 | rLog.Info("End of configuring keycloak realm auth flow") 31 | 32 | return nextServeOrNil(ctx, a.next, realm, kClient) 33 | } 34 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/chain/auth_flow_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/Nerzal/gocloak/v12" 8 | "github.com/pkg/errors" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/mock" 11 | "github.com/stretchr/testify/require" 12 | 13 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 14 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks" 15 | ) 16 | 17 | func TestAuthFlow_ServeRequest(t *testing.T) { 18 | kc := mocks.NewMockClient(t) 19 | af := AuthFlow{} 20 | 21 | realm := keycloakApi.KeycloakRealm{ 22 | Spec: keycloakApi.KeycloakRealmSpec{ 23 | RealmName: "realm1", 24 | }, 25 | } 26 | 27 | ctx := context.Background() 28 | 29 | err := af.ServeRequest(ctx, &realm, kc) 30 | require.NoError(t, err) 31 | 32 | kc.On("SetRealmBrowserFlow", mock.Anything, "realm1", "flow-alias-1").Return(nil) 33 | 34 | realm.Spec.BrowserFlow = gocloak.StringP("flow-alias-1") 35 | 36 | err = af.ServeRequest(ctx, &realm, kc) 37 | require.NoError(t, err) 38 | } 39 | 40 | func TestAuthFlow_ServeRequest_Failure(t *testing.T) { 41 | kc := mocks.NewMockClient(t) 42 | af := AuthFlow{} 43 | 44 | realm := keycloakApi.KeycloakRealm{ 45 | Spec: keycloakApi.KeycloakRealmSpec{ 46 | RealmName: "realm1", 47 | }, 48 | } 49 | 50 | mockErr := errors.New("fatal") 51 | 52 | kc.On("SetRealmBrowserFlow", mock.Anything, "realm1", "flow-alias-1").Return(mockErr) 53 | 54 | realm.Spec.BrowserFlow = gocloak.StringP("flow-alias-1") 55 | 56 | err := af.ServeRequest(context.Background(), &realm, kc) 57 | if err == nil { 58 | t.Fatal("no error on mock fatal") 59 | } 60 | 61 | assert.ErrorIs(t, err, mockErr) 62 | } 63 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/chain/factory.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "reflect" 7 | 8 | "k8s.io/apimachinery/pkg/runtime" 9 | ctrl "sigs.k8s.io/controller-runtime" 10 | "sigs.k8s.io/controller-runtime/pkg/client" 11 | 12 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 13 | "github.com/epam/edp-keycloak-operator/internal/controller/keycloakrealm/chain/handler" 14 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 15 | ) 16 | 17 | var log = ctrl.Log.WithName("realm_handler") 18 | 19 | func CreateDefChain(client client.Client, scheme *runtime.Scheme, hlp Helper) handler.RealmHandler { 20 | return PutRealm{ 21 | hlp: hlp, 22 | client: client, 23 | next: SetLabels{ 24 | client: client, 25 | next: PutUsers{ 26 | next: PutUsersRoles{ 27 | next: RealmSettings{ 28 | next: AuthFlow{ 29 | next: UserProfile{ 30 | next: ConfigureEmail{ 31 | client: client, 32 | }, 33 | }, 34 | }, 35 | }, 36 | }, 37 | }, 38 | }, 39 | } 40 | } 41 | 42 | func nextServeOrNil(ctx context.Context, next handler.RealmHandler, realm *keycloakApi.KeycloakRealm, kClient keycloak.Client) error { 43 | if next != nil { 44 | err := next.ServeRequest(ctx, realm, kClient) 45 | if err != nil { 46 | return fmt.Errorf("chain failed %s: %w", reflect.TypeOf(next).Name(), err) 47 | } 48 | 49 | return nil 50 | } 51 | 52 | log.Info("handling of realm has been finished", "realm name", realm.Spec.RealmName) 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/chain/handler/mock_realm_handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stretchr/testify/mock" 7 | 8 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type MockRealmHandler struct { 13 | mock.Mock 14 | } 15 | 16 | func (m *MockRealmHandler) ServeRequest(_ context.Context, realm *keycloakApi.KeycloakRealm, kClient keycloak.Client) error { 17 | args := m.Called(realm, kClient) 18 | return args.Error(0) 19 | } 20 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/chain/handler/realm_handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import ( 4 | "context" 5 | 6 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 7 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 8 | ) 9 | 10 | type RealmHandler interface { 11 | ServeRequest(ctx context.Context, realm *keycloakApi.KeycloakRealm, kClient keycloak.Client) error 12 | } 13 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/chain/set_labels.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | "sigs.k8s.io/controller-runtime/pkg/client" 8 | 9 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 10 | "github.com/epam/edp-keycloak-operator/internal/controller/keycloakrealm/chain/handler" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 12 | ) 13 | 14 | const TargetRealmLabel = "targetRealm" 15 | 16 | type SetLabels struct { 17 | next handler.RealmHandler 18 | client client.Client 19 | } 20 | 21 | func (s SetLabels) ServeRequest(ctx context.Context, realm *keycloakApi.KeycloakRealm, kClient keycloak.Client) error { 22 | if realm.Labels == nil { 23 | realm.Labels = make(map[string]string) 24 | } 25 | 26 | if tr, ok := realm.Labels[TargetRealmLabel]; !ok || tr != realm.Spec.RealmName { 27 | realm.Labels[TargetRealmLabel] = realm.Spec.RealmName 28 | } 29 | 30 | if err := s.client.Update(ctx, realm); err != nil { 31 | return errors.Wrapf(err, "unable to update realm with new labels, realm: %+v", realm) 32 | } 33 | 34 | return nextServeOrNil(ctx, s.next, realm, kClient) 35 | } 36 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/chain/users.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | 8 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 9 | "github.com/epam/edp-keycloak-operator/internal/controller/keycloakrealm/chain/handler" 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/dto" 12 | ) 13 | 14 | type PutUsers struct { 15 | next handler.RealmHandler 16 | } 17 | 18 | func (h PutUsers) ServeRequest(ctx context.Context, realm *keycloakApi.KeycloakRealm, kClient keycloak.Client) error { 19 | rLog := log.WithValues("keycloak users", realm.Spec.Users) 20 | rLog.Info("Start putting users to realm") 21 | 22 | rDto := dto.ConvertSpecToRealm(&realm.Spec) 23 | 24 | err := createUsers(rDto, kClient) 25 | if err != nil { 26 | return errors.Wrap(err, "error during createUsers") 27 | } 28 | 29 | rLog.Info("End put users to realm") 30 | 31 | return nextServeOrNil(ctx, h.next, realm, kClient) 32 | } 33 | 34 | func createUsers(realm *dto.Realm, kClient keycloak.Client) error { 35 | for _, user := range realm.Users { 36 | err := createOneUser(&user, realm, kClient) 37 | if err != nil { 38 | return errors.Wrap(err, "error during createOneUser") 39 | } 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func createOneUser(user *dto.User, realm *dto.Realm, kClient keycloak.Client) error { 46 | realmName := realm.Name 47 | 48 | exist, err := kClient.ExistRealmUser(realmName, user) 49 | if err != nil { 50 | return errors.Wrap(err, "error during exist ream user check") 51 | } 52 | 53 | if exist { 54 | log.Info("User already exists", "user", user) 55 | return nil 56 | } 57 | 58 | if err := kClient.CreateRealmUser(realmName, user); err != nil { 59 | return errors.Wrap(err, "unable to create user in realm") 60 | } 61 | 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/chain/users_roles.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | 8 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 9 | "github.com/epam/edp-keycloak-operator/internal/controller/keycloakrealm/chain/handler" 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 11 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/dto" 12 | ) 13 | 14 | type PutUsersRoles struct { 15 | next handler.RealmHandler 16 | } 17 | 18 | func (h PutUsersRoles) ServeRequest(ctx context.Context, realm *keycloakApi.KeycloakRealm, kClient keycloak.Client) error { 19 | rLog := log.WithValues("keycloak users", realm.Spec.Users) 20 | rLog.Info("Start putting roles to users") 21 | 22 | rDto := dto.ConvertSpecToRealm(&realm.Spec) 23 | 24 | err := putRolesToUsers(ctx, rDto, kClient) 25 | if err != nil { 26 | return errors.Wrap(err, "error during putRolesToUsers") 27 | } 28 | 29 | rLog.Info("End put role to users") 30 | 31 | return nextServeOrNil(ctx, h.next, realm, kClient) 32 | } 33 | 34 | func putRolesToUsers(ctx context.Context, realm *dto.Realm, kClient keycloak.Client) error { 35 | for _, user := range realm.Users { 36 | err := putRolesToOneUser(ctx, realm, &user, kClient) 37 | if err != nil { 38 | return errors.Wrap(err, "error during putRolesToOneUser") 39 | } 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func putRolesToOneUser(ctx context.Context, realm *dto.Realm, user *dto.User, kClient keycloak.Client) error { 46 | for _, role := range user.RealmRoles { 47 | if err := putOneRealmRoleToOneUser(ctx, realm, user, role, kClient); err != nil { 48 | return errors.Wrap(err, "error during putOneRoleToOneUser") 49 | } 50 | } 51 | 52 | return nil 53 | } 54 | 55 | func putOneRealmRoleToOneUser(ctx context.Context, realm *dto.Realm, user *dto.User, role string, kClient keycloak.Client) error { 56 | exist, err := kClient.HasUserRealmRole(realm.Name, user, role) 57 | if err != nil { 58 | return errors.Wrap(err, "error during check of client role") 59 | } 60 | 61 | if exist { 62 | log.Info("Role already exists", "user", user, "role", role) 63 | return nil 64 | } 65 | 66 | if err := kClient.AddRealmRoleToUser(ctx, realm.Name, user.Username, role); err != nil { 67 | return errors.Wrap(err, "unable to add realm role to user") 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealm/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakrealm 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type terminator struct { 13 | realmName string 14 | kClient keycloak.Client 15 | preserveResourcesOnDeletion bool 16 | } 17 | 18 | func (t *terminator) DeleteResource(ctx context.Context) error { 19 | log := ctrl.LoggerFrom(ctx).WithValues("keycloak_realm", t.realmName) 20 | if t.preserveResourcesOnDeletion { 21 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 22 | return nil 23 | } 24 | 25 | log.Info("Start deleting keycloak realm") 26 | 27 | if err := t.kClient.DeleteRealm(ctx, t.realmName); err != nil { 28 | return fmt.Errorf("failed to delete keycloak realm: %w", err) 29 | } 30 | 31 | log.Info("Realm has been deleted") 32 | 33 | return nil 34 | } 35 | 36 | func makeTerminator(realmName string, kClient keycloak.Client, preserveResourcesOnDeletion bool) *terminator { 37 | return &terminator{ 38 | realmName: realmName, 39 | kClient: kClient, 40 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmcomponent/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmcomponent 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type terminator struct { 13 | realmName string 14 | componentName string 15 | kClient keycloak.Client 16 | preserveResourcesOnDeletion bool 17 | } 18 | 19 | func makeTerminator(realmName, componentName string, kClient keycloak.Client, preserveResourcesOnDeletion bool) *terminator { 20 | return &terminator{ 21 | realmName: realmName, 22 | componentName: componentName, 23 | kClient: kClient, 24 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 25 | } 26 | } 27 | 28 | func (t *terminator) DeleteResource(ctx context.Context) error { 29 | log := ctrl.LoggerFrom(ctx) 30 | if t.preserveResourcesOnDeletion { 31 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 32 | return nil 33 | } 34 | 35 | log.Info("Start deleting KeycloakRealmComponent") 36 | 37 | if err := t.kClient.DeleteComponent(ctx, t.realmName, t.componentName); err != nil { 38 | return fmt.Errorf("unable to delete realm component %w", err) 39 | } 40 | 41 | log.Info("KeycloakRealmComponent deletion done") 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmcomponent/terminator_test.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmcomponent 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | testifymock "github.com/stretchr/testify/mock" 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks" 11 | ) 12 | 13 | func TestTerminator_DeleteResource(t *testing.T) { 14 | kcAdapter := mocks.NewMockClient(t) 15 | 16 | kcAdapter.On("DeleteComponent", testifymock.Anything, "foo", "bar").Return(nil) 17 | term := makeTerminator("foo", "bar", kcAdapter, false) 18 | err := term.DeleteResource(context.Background()) 19 | require.NoError(t, err) 20 | } 21 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmgroup/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmgroup 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/adapter" 11 | ) 12 | 13 | type terminator struct { 14 | kClient keycloak.Client 15 | realmName, 16 | groupName string 17 | preserveResourcesOnDeletion bool 18 | } 19 | 20 | func (t *terminator) DeleteResource(ctx context.Context) error { 21 | log := ctrl.LoggerFrom(ctx).WithValues("realm_name", t.realmName, "group_name", t.groupName) 22 | if t.preserveResourcesOnDeletion { 23 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 24 | return nil 25 | } 26 | 27 | log.Info("Start deleting group") 28 | 29 | if err := t.kClient.DeleteGroup(ctx, t.realmName, t.groupName); err != nil { 30 | if adapter.IsErrNotFound(err) { 31 | log.Info("Group not found, skipping deletion") 32 | 33 | return nil 34 | } 35 | 36 | return fmt.Errorf("unable to delete group %w", err) 37 | } 38 | 39 | log.Info("Group has been deleted") 40 | 41 | return nil 42 | } 43 | 44 | func makeTerminator(kClient keycloak.Client, realmName, groupName string, preserveResourcesOnDeletion bool) *terminator { 45 | return &terminator{ 46 | kClient: kClient, 47 | realmName: realmName, 48 | groupName: groupName, 49 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmidentityprovider/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmidentityprovider 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type terminator struct { 13 | realmName string 14 | idpAlias string 15 | kClient keycloak.Client 16 | preserveResourcesOnDeletion bool 17 | } 18 | 19 | func makeTerminator(realmName, idpAlias string, kClient keycloak.Client, preserveResourcesOnDeletion bool) *terminator { 20 | return &terminator{ 21 | realmName: realmName, 22 | idpAlias: idpAlias, 23 | kClient: kClient, 24 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 25 | } 26 | } 27 | 28 | func (t *terminator) DeleteResource(ctx context.Context) error { 29 | log := ctrl.LoggerFrom(ctx).WithValues("keycloak_realm_idp_alias", t.idpAlias) 30 | if t.preserveResourcesOnDeletion { 31 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 32 | return nil 33 | } 34 | 35 | log.Info("Start deleting keycloak realm idp") 36 | 37 | if err := t.kClient.DeleteIdentityProvider(ctx, t.realmName, t.idpAlias); err != nil { 38 | return fmt.Errorf("unable to delete realm idp %w", err) 39 | } 40 | 41 | log.Info("Realm idp has been deleted") 42 | 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmidentityprovider/terminator_test.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmidentityprovider 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/pkg/errors" 8 | "github.com/stretchr/testify/assert" 9 | testifymock "github.com/stretchr/testify/mock" 10 | "github.com/stretchr/testify/require" 11 | 12 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks" 13 | ) 14 | 15 | func TestTerminator_DeleteResource(t *testing.T) { 16 | kClient := mocks.NewMockClient(t) 17 | 18 | kClient.On("DeleteIdentityProvider", testifymock.Anything, "realm", "alias1").Return(nil).Once() 19 | term := makeTerminator("realm", "alias1", kClient, false) 20 | err := term.DeleteResource(context.Background()) 21 | require.NoError(t, err) 22 | 23 | kClient.On("DeleteIdentityProvider", testifymock.Anything, "realm", "alias1"). 24 | Return(errors.New("delete res fatal")).Once() 25 | 26 | err = term.DeleteResource(context.Background()) 27 | require.Error(t, err) 28 | 29 | assert.Contains(t, err.Error(), "delete res fatal") 30 | } 31 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmrole/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmrole 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type terminator struct { 13 | realmName, realmRoleName string 14 | kClient keycloak.Client 15 | preserveResourcesOnDeletion bool 16 | } 17 | 18 | func (t *terminator) DeleteResource(ctx context.Context) error { 19 | log := ctrl.LoggerFrom(ctx) 20 | if t.preserveResourcesOnDeletion { 21 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 22 | return nil 23 | } 24 | 25 | log.Info("Start deleting keycloak realm role") 26 | 27 | if err := t.kClient.DeleteRealmRole(ctx, t.realmName, t.realmRoleName); err != nil { 28 | return fmt.Errorf("unable to delete realm role %w", err) 29 | } 30 | 31 | log.Info("Realm role has been deleted") 32 | 33 | return nil 34 | } 35 | 36 | func makeTerminator(realmName, realmRoleName string, kClient keycloak.Client, preserveResourcesOnDeletion bool) *terminator { 37 | return &terminator{ 38 | realmRoleName: realmRoleName, 39 | realmName: realmName, 40 | kClient: kClient, 41 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmrole/terminator_test.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmrole 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/pkg/errors" 8 | "github.com/stretchr/testify/assert" 9 | testifymock "github.com/stretchr/testify/mock" 10 | "github.com/stretchr/testify/require" 11 | ctrl "sigs.k8s.io/controller-runtime" 12 | 13 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mock" 14 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/mocks" 15 | ) 16 | 17 | func TestTerminator(t *testing.T) { 18 | lg := mock.NewLogr() 19 | kClient := mocks.NewMockClient(t) 20 | 21 | term := makeTerminator("foo", "bar", kClient, false) 22 | kClient.On("DeleteRealmRole", testifymock.Anything, "foo", "bar").Return(nil).Once() 23 | 24 | err := term.DeleteResource(context.Background()) 25 | require.NoError(t, err) 26 | 27 | kClient.On("DeleteRealmRole", testifymock.Anything, "foo", "bar").Return(errors.New("fatal")).Once() 28 | 29 | err = term.DeleteResource(ctrl.LoggerInto(context.Background(), lg)) 30 | require.Error(t, err) 31 | 32 | loggerSink, ok := lg.GetSink().(*mock.Logger) 33 | require.True(t, ok, "wrong logger type") 34 | 35 | assert.NotEmpty(t, loggerSink.InfoMessages(), "no info messages logged") 36 | } 37 | 38 | func TestTerminatorSkipDeletion(t *testing.T) { 39 | term := makeTerminator( 40 | "realm", 41 | "role", 42 | nil, 43 | true, 44 | ) 45 | 46 | err := term.DeleteResource(context.Background()) 47 | require.NoError(t, err) 48 | } 49 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmrolebatch/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmrolebatch 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | 10 | keycloakApi "github.com/epam/edp-keycloak-operator/api/v1" 11 | ) 12 | 13 | type terminator struct { 14 | client client.Client 15 | childRoles []keycloakApi.KeycloakRealmRole 16 | preserveResourcesOnDeletion bool 17 | } 18 | 19 | func (t *terminator) DeleteResource(ctx context.Context) error { 20 | log := ctrl.LoggerFrom(ctx) 21 | if t.preserveResourcesOnDeletion { 22 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 23 | return nil 24 | } 25 | 26 | log.Info("Start deleting keycloak realm role batch") 27 | 28 | for i := range t.childRoles { 29 | if err := t.client.Delete(ctx, &t.childRoles[i]); err != nil { 30 | return fmt.Errorf("unable to delete realm role %w", err) 31 | } 32 | } 33 | 34 | log.Info("Realm role batch has been deleted") 35 | 36 | return nil 37 | } 38 | 39 | func makeTerminator(client client.Client, childRoles []keycloakApi.KeycloakRealmRole, preserveResourcesOnDeletion bool) *terminator { 40 | return &terminator{ 41 | client: client, 42 | childRoles: childRoles, 43 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /internal/controller/keycloakrealmuser/terminator.go: -------------------------------------------------------------------------------- 1 | package keycloakrealmuser 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | ctrl "sigs.k8s.io/controller-runtime" 8 | 9 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak" 10 | ) 11 | 12 | type terminator struct { 13 | kClient keycloak.Client 14 | realmName, userName string 15 | preserveResourcesOnDeletion bool 16 | } 17 | 18 | func (t *terminator) DeleteResource(ctx context.Context) error { 19 | log := ctrl.LoggerFrom(ctx) 20 | if t.preserveResourcesOnDeletion { 21 | log.Info("PreserveResourcesOnDeletion is enabled, skipping deletion.") 22 | return nil 23 | } 24 | 25 | log.Info("Start deleting keycloak realm user") 26 | 27 | if err := t.kClient.DeleteRealmUser(ctx, t.realmName, t.userName); err != nil { 28 | return fmt.Errorf("unable to delete realm user %w", err) 29 | } 30 | 31 | log.Info("Realm user has been deleted") 32 | 33 | return nil 34 | } 35 | 36 | func makeTerminator(realmName, userName string, kClient keycloak.Client, preserveResourcesOnDeletion bool) *terminator { 37 | return &terminator{ 38 | kClient: kClient, 39 | realmName: realmName, 40 | userName: userName, 41 | preserveResourcesOnDeletion: preserveResourcesOnDeletion, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /kuttl-test.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kuttl.dev/v1beta1 2 | kind: TestSuite 3 | testDirs: 4 | - tests/e2e/ 5 | skipClusterDelete: false 6 | skipDelete: false 7 | timeout: 100 8 | parallel: 1 9 | -------------------------------------------------------------------------------- /pkg/client/keycloak/adapter/errors.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import "strings" 4 | 5 | // SkipAlreadyExistsErr skips error if it is already exists error. 6 | func SkipAlreadyExistsErr(err error) error { 7 | if err == nil { 8 | return nil 9 | } 10 | 11 | if strings.Contains(err.Error(), "409 Conflict") { 12 | return nil 13 | } 14 | 15 | return err 16 | } 17 | -------------------------------------------------------------------------------- /pkg/client/keycloak/adapter/errors_test.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestSkipAlreadyExistsErr(t *testing.T) { 11 | t.Parallel() 12 | 13 | tests := []struct { 14 | name string 15 | err error 16 | wantErr assert.ErrorAssertionFunc 17 | }{ 18 | { 19 | name: "ReturnsNil_WhenErrorIsNil", 20 | err: nil, 21 | wantErr: assert.NoError, 22 | }, 23 | { 24 | name: "ReturnsNil_WhenErrorIsConflict", 25 | err: errors.New("409 Conflict"), 26 | wantErr: assert.NoError, 27 | }, 28 | { 29 | name: "ReturnsError_WhenErrorIsNotConflict", 30 | err: errors.New("500 Internal Server Error"), 31 | wantErr: assert.Error, 32 | }, 33 | } 34 | 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | t.Parallel() 38 | 39 | tt.wantErr(t, SkipAlreadyExistsErr(tt.err)) 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pkg/client/keycloak/adapter/gocloack_adapter_realm_event_test.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/Nerzal/gocloak/v12" 8 | "github.com/go-resty/resty/v2" 9 | "github.com/jarcoal/httpmock" 10 | "github.com/stretchr/testify/require" 11 | 12 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/adapter/mocks" 13 | ) 14 | 15 | func TestGoCloakAdapter_SetRealmEventConfig(t *testing.T) { 16 | mockClient := mocks.NewMockGoCloak(t) 17 | restyClient := resty.New() 18 | httpmock.ActivateNonDefault(restyClient.GetClient()) 19 | mockClient.On("RestyClient").Return(restyClient) 20 | 21 | adapter := GoCloakAdapter{ 22 | client: mockClient, 23 | basePath: "", 24 | token: &gocloak.JWT{AccessToken: "token"}, 25 | } 26 | 27 | err := adapter.SetRealmEventConfig("realm1", &RealmEventConfig{}) 28 | require.Error(t, err) 29 | 30 | if !strings.Contains(err.Error(), "failed to set realm event config request") { 31 | t.Fatalf("wrong error returned: %s", err.Error()) 32 | } 33 | 34 | httpmock.RegisterResponder("PUT", "/admin/realms/r1/events/config", 35 | httpmock.NewStringResponder(200, "")) 36 | 37 | err = adapter.SetRealmEventConfig("r1", 38 | &RealmEventConfig{EventsListeners: []string{"foo", "bar"}}) 39 | require.NoError(t, err) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/keycloak/adapter/gocloak_adapter_realm_event.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type RealmEventConfig struct { 8 | AdminEventsDetailsEnabled bool `json:"adminEventsDetailsEnabled"` 9 | AdminEventsEnabled bool `json:"adminEventsEnabled"` 10 | EnabledEventTypes []string `json:"enabledEventTypes"` 11 | EventsEnabled bool `json:"eventsEnabled"` 12 | EventsExpiration int `json:"eventsExpiration"` 13 | EventsListeners []string `json:"eventsListeners"` 14 | } 15 | 16 | func (a GoCloakAdapter) SetRealmEventConfig(realmName string, eventConfig *RealmEventConfig) error { 17 | rsp, err := a.startRestyRequest(). 18 | SetBody(eventConfig). 19 | SetPathParams(map[string]string{keycloakApiParamRealm: realmName}). 20 | Put(a.buildPath(realmEventConfigPut)) 21 | 22 | if err = a.checkError(err, rsp); err != nil { 23 | return fmt.Errorf("failed to set realm event config request: %w", err) 24 | } 25 | 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /pkg/client/keycloak/adapter/gocloak_adapter_resty.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "github.com/go-resty/resty/v2" 5 | ) 6 | 7 | func (a GoCloakAdapter) startRestyRequest() *resty.Request { 8 | return a.client.RestyClient().R(). 9 | SetAuthToken(a.token.AccessToken). 10 | SetHeader(contentTypeHeader, contentTypeJson) 11 | } 12 | -------------------------------------------------------------------------------- /pkg/client/keycloak/adapter/gocloak_adapter_service_account_test.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/Nerzal/gocloak/v12" 7 | "github.com/stretchr/testify/mock" 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/epam/edp-keycloak-operator/pkg/client/keycloak/adapter/mocks" 11 | ) 12 | 13 | func TestGoCloakAdapter_SetServiceAccountAttributes(t *testing.T) { 14 | mockClient := mocks.NewMockGoCloak(t) 15 | 16 | adapter := GoCloakAdapter{ 17 | client: mockClient, 18 | basePath: "", 19 | token: &gocloak.JWT{AccessToken: "token"}, 20 | } 21 | 22 | usr1 := gocloak.User{ 23 | Username: gocloak.StringP("user1"), 24 | Attributes: &map[string][]string{ 25 | "foo1": {"bar1"}, 26 | }, 27 | } 28 | 29 | usr2 := gocloak.User{ 30 | Username: gocloak.StringP("user1"), 31 | Attributes: &map[string][]string{ 32 | "foo": {"bar"}, 33 | "foo1": {"bar1"}, 34 | }, 35 | } 36 | 37 | mockClient.On("GetClientServiceAccount", mock.Anything, "token", "realm1", "clientID1").Return(&usr1, nil) 38 | mockClient.On("UpdateUser", mock.Anything, "token", "realm1", usr2).Return(nil) 39 | 40 | err := adapter.SetServiceAccountAttributes("realm1", "clientID1", 41 | map[string]string{"foo": "bar"}, true) 42 | require.NoError(t, err) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/client/keycloak/adapter/http.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | const ( 4 | contentTypeHeader = "Content-Type" 5 | contentTypeJson = "application/json" 6 | ) 7 | -------------------------------------------------------------------------------- /pkg/client/keycloak/api/identity_provider.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | type IdentityProviderRepresentation struct { 4 | Alias string `json:"alias"` 5 | DisplayName string `json:"displayName"` 6 | Enabled bool `json:"enabled"` 7 | ProviderId string `json:"providerId"` 8 | Config IdentityProviderConfig `json:"config"` 9 | } 10 | 11 | type IdentityProviderConfig struct { 12 | UserInfoUrl string `json:"userInfoUrl"` 13 | TokenUrl string `json:"tokenUrl"` 14 | JwksUrl string `json:"jwksUrl"` 15 | Issuer string `json:"issuer"` 16 | AuthorizationUrl string `json:"authorizationUrl"` 17 | LogoutUrl string `json:"logoutUrl"` 18 | ClientId string `json:"clientId"` 19 | ClientSecret string `json:"clientSecret"` 20 | } 21 | 22 | type IdentityProviderMapperRepresentation struct { 23 | Config map[string]string `json:"config"` 24 | IdentityProviderAlias string `json:"identityProviderAlias"` 25 | IdentityProviderMapper string `json:"identityProviderMapper"` 26 | Name string `json:"name"` 27 | } 28 | 29 | type SimpleAuthExecution struct { 30 | Id string `json:"id"` 31 | ProviderId string `json:"providerId"` 32 | AuthenticationConfig string `json:"authenticationConfig"` 33 | } 34 | -------------------------------------------------------------------------------- /pkg/client/keycloak/mock/mock_logger.go: -------------------------------------------------------------------------------- 1 | package mock 2 | 3 | import "github.com/go-logr/logr" 4 | 5 | func NewLogr() logr.Logger { 6 | return logr.New(&Logger{}) 7 | } 8 | 9 | type Logger struct { 10 | errors []error 11 | infoMessages map[string][]interface{} 12 | } 13 | 14 | // Init implements logr.LogSink. 15 | func (log *Logger) Init(logr.RuntimeInfo) { 16 | } 17 | 18 | // Info implements logr.InfoLogger. 19 | func (l *Logger) Info(level int, msg string, keysAndValues ...interface{}) { 20 | if l.infoMessages == nil { 21 | l.infoMessages = make(map[string][]interface{}) 22 | } 23 | 24 | l.infoMessages[msg] = keysAndValues 25 | } 26 | 27 | func (l *Logger) InfoMessages() map[string][]interface{} { 28 | return l.infoMessages 29 | } 30 | 31 | // Enabled implements logr.InfoLogger. 32 | func (Logger) Enabled(level int) bool { 33 | return true 34 | } 35 | 36 | func (l *Logger) Error(err error, msg string, keysAndValues ...interface{}) { 37 | l.errors = append(l.errors, err) 38 | } 39 | 40 | func (l Logger) LastError() error { 41 | if len(l.errors) == 0 { 42 | return nil 43 | } 44 | 45 | return l.errors[len(l.errors)-1] 46 | } 47 | 48 | // WithName implements logr.Logger. 49 | func (log *Logger) WithName(_ string) logr.LogSink { 50 | return log 51 | } 52 | 53 | // WithValues implements logr.Logger. 54 | func (log *Logger) WithValues(_ ...interface{}) logr.LogSink { 55 | return log 56 | } 57 | -------------------------------------------------------------------------------- /pkg/objectmeta/deletion.go: -------------------------------------------------------------------------------- 1 | package objectmeta 2 | 3 | import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 4 | 5 | const PreserveResourcesOnDeletionAnnotation = "edp.epam.com/preserve-resources-on-deletion" 6 | 7 | // PreserveResourcesOnDeletion returns true if the object has annotation that indicates that resources must not be deleted. 8 | func PreserveResourcesOnDeletion(object metav1.Object) bool { 9 | return object.GetAnnotations()[PreserveResourcesOnDeletionAnnotation] == "true" 10 | } 11 | -------------------------------------------------------------------------------- /pkg/objectmeta/deletion_test.go: -------------------------------------------------------------------------------- 1 | package objectmeta 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | func TestPreserveResourcesOnDeletion(t *testing.T) { 11 | t.Parallel() 12 | 13 | tests := []struct { 14 | name string 15 | object v1.Object 16 | want bool 17 | }{ 18 | { 19 | name: "should return true if annotation is set", 20 | object: &v1.ObjectMeta{ 21 | Annotations: map[string]string{ 22 | PreserveResourcesOnDeletionAnnotation: "true", 23 | }, 24 | }, 25 | want: true, 26 | }, 27 | { 28 | name: "should return false if annotation is not set", 29 | object: &v1.ObjectMeta{ 30 | Annotations: map[string]string{}, 31 | }, 32 | want: false, 33 | }, 34 | { 35 | name: "should return false if annotation is set to false", 36 | object: &v1.ObjectMeta{ 37 | Annotations: map[string]string{ 38 | PreserveResourcesOnDeletionAnnotation: "false", 39 | }, 40 | }, 41 | want: false, 42 | }, 43 | { 44 | name: "should return false if annotation is set to not empty string", 45 | object: &v1.ObjectMeta{ 46 | Annotations: map[string]string{ 47 | PreserveResourcesOnDeletionAnnotation: "some string", 48 | }, 49 | }, 50 | want: false, 51 | }, 52 | } 53 | 54 | for _, tt := range tests { 55 | t.Run(tt.name, func(t *testing.T) { 56 | t.Parallel() 57 | 58 | got := PreserveResourcesOnDeletion(tt.object) 59 | assert.Equal(t, tt.want, got) 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/secretref/sourceref.go: -------------------------------------------------------------------------------- 1 | package secretref 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | corev1 "k8s.io/api/core/v1" 8 | "k8s.io/apimachinery/pkg/types" 9 | "sigs.k8s.io/controller-runtime/pkg/client" 10 | 11 | "github.com/epam/edp-keycloak-operator/api/common" 12 | ) 13 | 14 | // GetValueFromSourceRef retries value from ConfigMap or Secret by SourceRef. 15 | func GetValueFromSourceRef(ctx context.Context, sourceRef *common.SourceRef, namespace string, k8sClient client.Client) (string, error) { 16 | if sourceRef == nil { 17 | return "", nil 18 | } 19 | 20 | if sourceRef.ConfigMapKeyRef != nil { 21 | configMap := &corev1.ConfigMap{} 22 | if err := k8sClient.Get(ctx, types.NamespacedName{ 23 | Namespace: namespace, 24 | Name: sourceRef.ConfigMapKeyRef.Name, 25 | }, configMap); err != nil { 26 | return "", fmt.Errorf("unable to get configmap: %w", err) 27 | } 28 | 29 | return configMap.Data[sourceRef.ConfigMapKeyRef.Key], nil 30 | } 31 | 32 | if sourceRef.SecretKeyRef != nil { 33 | secret := &corev1.Secret{} 34 | if err := k8sClient.Get(ctx, types.NamespacedName{ 35 | Namespace: namespace, 36 | Name: sourceRef.SecretKeyRef.Name, 37 | }, secret); err != nil { 38 | return "", fmt.Errorf("unable to get secret: %w", err) 39 | } 40 | 41 | return string(secret.Data[sourceRef.SecretKeyRef.Key]), nil 42 | } 43 | 44 | return "", nil 45 | } 46 | 47 | // GetValueFromSourceRefOrVal retries value from ConfigMap or Secret or directly from value. 48 | func GetValueFromSourceRefOrVal(ctx context.Context, sourceRef *common.SourceRefOrVal, namespace string, k8sClient client.Client) (string, error) { 49 | if sourceRef == nil { 50 | return "", nil 51 | } 52 | 53 | if sourceRef.SourceRef != nil { 54 | return GetValueFromSourceRef(ctx, sourceRef.SourceRef, namespace, k8sClient) 55 | } 56 | 57 | return sourceRef.Value, nil 58 | } 59 | -------------------------------------------------------------------------------- /pkg/util/cluster.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | const ( 9 | watchNamespaceEnvVar = "WATCH_NAMESPACE" 10 | ) 11 | 12 | // GetWatchNamespace returns the namespace the operator should be watching for changes. 13 | func GetWatchNamespace() (string, error) { 14 | ns, found := os.LookupEnv(watchNamespaceEnvVar) 15 | if !found { 16 | return "", fmt.Errorf("%s must be set", watchNamespaceEnvVar) 17 | } 18 | 19 | return ns, nil 20 | } 21 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Pull Request Template 2 | 3 | ## Description 4 | Please include a summary of the change and why it is needed. 5 | 6 | Fixes #(issue) 7 | 8 | ## Type of change 9 | 10 | - [ ] Bug fix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Enhancement (non-breaking change which improves an existing feature or documentation) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) 14 | 15 | ## How Has This Been Tested? 16 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. 17 | 18 | ## Checklist: 19 | - [ ] I have performed a self-review of my code 20 | - [ ] I have commented on my code, particularly in hard-to-understand areas 21 | - [ ] I have made corresponding changes to the documentation 22 | - [ ] My changes generate no new warnings 23 | - [ ] I have added tests that prove my fix is effective or that my feature works 24 | - [ ] New and existing unit tests pass locally with my changes 25 | - [ ] Pull Request contains one commit. I squash my commits. 26 | 27 | ## Screenshots (if appropriate): 28 | 29 | ## Additional context 30 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=keycloak-operator 2 | sonar.projectName=keycloak-operator 3 | sonar.projectVersion=1.0 4 | sonar.go.coverage.reportPaths=coverage.out 5 | sonar.test.inclusions=**/*_test.go 6 | sonar.exclusions=**/config/**,**/deploy-templates/**,**/factory.go,**/mock_*.go,**/*_mock.go,**/.github/**,**/api/**,**/tests/**,**/hack/**,**/bundle/**,**/main.go 7 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/00-assert-operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: keycloak-operator 5 | status: 6 | readyReplicas: 1 7 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/00-install-operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kuttl.dev/v1beta1 2 | kind: TestStep 3 | commands: 4 | - command: helm install --set image.repository=${CONTAINER_REGISTRY_URL}/${CONTAINER_REGISTRY_SPACE}/${E2E_IMAGE_REPOSITORY} --set image.tag=${E2E_IMAGE_TAG} --replace --wait keycloak-operator-e2e ../../../deploy-templates 5 | namespaced: true 6 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/01-assert-install-keycloak-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: keycloak 5 | status: 6 | readyReplicas: 1 7 | 8 | --- 9 | apiVersion: v1 10 | kind: Secret 11 | metadata: 12 | name: keycloak-secret 13 | 14 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/01-install-keycloak-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: keycloak 5 | --- 6 | apiVersion: v1 7 | kind: Service 8 | metadata: 9 | name: keycloak 10 | labels: 11 | app: keycloak 12 | spec: 13 | ports: 14 | - name: http 15 | port: 8081 16 | targetPort: 8081 17 | selector: 18 | app: keycloak 19 | type: ClusterIP 20 | --- 21 | apiVersion: v1 22 | kind: ConfigMap 23 | metadata: 24 | name: config 25 | data: 26 | default.conf: | 27 | server { 28 | listen 8081; 29 | server_name _; 30 | location / { 31 | proxy_pass http://localhost:8080; 32 | } 33 | } 34 | --- 35 | apiVersion: apps/v1 36 | kind: Deployment 37 | metadata: 38 | name: keycloak 39 | labels: 40 | app: keycloak 41 | spec: 42 | replicas: 1 43 | selector: 44 | matchLabels: 45 | app: keycloak 46 | template: 47 | metadata: 48 | labels: 49 | app: keycloak 50 | spec: 51 | volumes: 52 | - name: configs 53 | configMap: 54 | name: config 55 | containers: 56 | - image: nginxinc/nginx-unprivileged:1.23 57 | imagePullPolicy: Always 58 | name: nginx 59 | ports: 60 | - containerPort: 8081 61 | volumeMounts: 62 | - name: configs 63 | mountPath: /etc/nginx/conf.d 64 | - name: keycloak 65 | image: quay.io/keycloak/keycloak:24.0.4 66 | args: 67 | - "start-dev" 68 | - "--proxy-headers" 69 | - "forwarded" 70 | - "--http-enabled" 71 | - "true" 72 | env: 73 | - name: KEYCLOAK_ADMIN 74 | value: "admin" 75 | - name: KEYCLOAK_ADMIN_PASSWORD 76 | value: "admin" 77 | - name: KC_FEATURES 78 | value: admin-fine-grained-authz 79 | ports: 80 | - name: http 81 | containerPort: 8080 82 | readinessProbe: 83 | httpGet: 84 | path: /realms/master 85 | port: 8080 86 | 87 | --- 88 | apiVersion: v1 89 | kind: Secret 90 | metadata: 91 | name: keycloak-secret 92 | type: Opaque 93 | data: 94 | username: "YWRtaW4=" 95 | password: "YWRtaW4=" 96 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/02-assert-keycloak-resource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: Keycloak 3 | metadata: 4 | name: keycloak 5 | spec: 6 | secret: keycloak-secret 7 | url: http://keycloak:8081 8 | status: 9 | connected: true 10 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/02-install-keycloak-resource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: Keycloak 3 | metadata: 4 | name: keycloak 5 | spec: 6 | secret: keycloak-secret 7 | url: http://keycloak:8081 8 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/03-assert-keycloak-user.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: existing-k8s-secret 6 | --- 7 | apiVersion: v1.edp.epam.com/v1 8 | kind: KeycloakRealm 9 | metadata: 10 | name: keycloakrealm-sample 11 | status: 12 | available: true 13 | value: OK 14 | --- 15 | apiVersion: v1.edp.epam.com/v1 16 | kind: KeycloakRealmUser 17 | metadata: 18 | name: keycloakrealmuser-sample 19 | spec: 20 | realmRef: 21 | name: keycloakrealm-sample 22 | kind: KeycloakRealm 23 | username: "john.snow13" 24 | enabled: true 25 | emailVerified: true 26 | keepResource: true 27 | attributes: 28 | foo: "bar" 29 | baz: "jazz" 30 | passwordSecret: 31 | name: existing-k8s-secret 32 | key: key-which-contains-password 33 | status: 34 | value: OK 35 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/03-install-keycloak-user.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: existing-k8s-secret 6 | type: Opaque 7 | data: 8 | key-which-contains-password: "YWRtaW4=" 9 | --- 10 | apiVersion: v1.edp.epam.com/v1 11 | kind: KeycloakRealm 12 | metadata: 13 | name: keycloakrealm-sample 14 | spec: 15 | id: bfebeff6-ac63-4b46-a1f3-37df5099a9c4 16 | realmName: test-realm 17 | keycloakRef: 18 | name: keycloak 19 | kind: Keycloak 20 | realmEventConfig: 21 | adminEventsDetailsEnabled: false 22 | adminEventsEnabled: true 23 | enabledEventTypes: 24 | - UPDATE_CONSENT_ERROR 25 | - CLIENT_LOGIN 26 | eventsEnabled: true 27 | eventsExpiration: 15000 28 | eventsListeners: 29 | - jboss-logging 30 | tokenSettings: 31 | accessTokenLifespan: 300 32 | accessCodeLifespan: 300 33 | accessToken: 300 34 | actionTokenGeneratedByAdminLifespan: 300 35 | actionTokenGeneratedByUserLifespan: 300 36 | refreshTokenMaxReuse: 300 37 | revokeRefreshToken: true 38 | defaultSignatureAlgorithm: RS256 39 | --- 40 | apiVersion: v1.edp.epam.com/v1 41 | kind: KeycloakRealmUser 42 | metadata: 43 | name: keycloakrealmuser-sample 44 | spec: 45 | realmRef: 46 | name: keycloakrealm-sample 47 | kind: KeycloakRealm 48 | username: "john.snow13" 49 | firstName: "John" 50 | lastName: "Snow" 51 | email: "john.snow13@example.com" 52 | enabled: true 53 | emailVerified: true 54 | keepResource: true 55 | attributes: 56 | foo: "bar" 57 | baz: "jazz" 58 | passwordSecret: 59 | name: existing-k8s-secret 60 | key: key-which-contains-password 61 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/04-assert-component.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmComponent 3 | metadata: 4 | name: component-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | name: component-sample 10 | providerId: scope 11 | providerType: "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" 12 | status: 13 | value: "OK" 14 | 15 | --- 16 | 17 | apiVersion: v1.edp.epam.com/v1 18 | kind: KeycloakRealmComponent 19 | metadata: 20 | name: component-sample-child 21 | spec: 22 | realmRef: 23 | name: keycloakrealm-sample 24 | kind: KeycloakRealm 25 | name: component-sample-child 26 | providerId: scope 27 | providerType: "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" 28 | parentRef: 29 | name: component-sample 30 | kind: KeycloakRealmComponent 31 | status: 32 | value: "OK" 33 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/04-install-component.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmComponent 3 | metadata: 4 | name: component-sample 5 | spec: 6 | realmRef: 7 | name: keycloakrealm-sample 8 | kind: KeycloakRealm 9 | name: component-sample 10 | providerId: scope 11 | providerType: "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" 12 | 13 | --- 14 | 15 | apiVersion: v1.edp.epam.com/v1 16 | kind: KeycloakRealmComponent 17 | metadata: 18 | name: component-sample-child 19 | spec: 20 | realmRef: 21 | name: keycloakrealm-sample 22 | kind: KeycloakRealm 23 | name: component-sample-child 24 | providerId: scope 25 | providerType: "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" 26 | parentRef: 27 | name: component-sample 28 | kind: KeycloakRealmComponent 29 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/05-assert-identityprovider.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmIdentityProvider 3 | metadata: 4 | name: keycloakrealmidentityprovider-sample 5 | spec: 6 | enabled: true 7 | providerId: instagram 8 | alias: instagram 9 | config: 10 | clientId: foo 11 | clientSecret: $secret-name:secretKey 12 | status: 13 | value: "OK" 14 | 15 | --- 16 | apiVersion: v1.edp.epam.com/v1 17 | kind: KeycloakRealmIdentityProvider 18 | metadata: 19 | name: keycloakrealmidentityprovider-sample-with-pass 20 | spec: 21 | enabled: true 22 | providerId: facebook 23 | alias: facebook 24 | config: 25 | clientId: bar 26 | clientSecret: password-in-clear-form 27 | status: 28 | value: "OK" 29 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/05-install-identityprovider.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: secret-name 5 | type: Opaque 6 | data: 7 | secretKey: dGVzdAo= 8 | 9 | # we use secretKey from secret-name secret for clientSecret field 10 | --- 11 | apiVersion: v1.edp.epam.com/v1 12 | kind: KeycloakRealmIdentityProvider 13 | metadata: 14 | name: keycloakrealmidentityprovider-sample 15 | spec: 16 | realmRef: 17 | kind: KeycloakRealm 18 | name: keycloakrealm-sample 19 | alias: instagram 20 | authenticateByDefault: false 21 | enabled: true 22 | firstBrokerLoginFlowAlias: "first broker login" 23 | providerId: "instagram" 24 | config: 25 | clientId: "foo" 26 | clientSecret: "$secret-name:secretKey" 27 | hideOnLoginPage: "true" 28 | syncMode: "IMPORT" 29 | useJwksUrl: "true" 30 | mappers: 31 | - name: "test-33221" 32 | identityProviderMapper: "hardcoded-attribute-idp-mapper" 33 | identityProviderAlias: "instagram" 34 | config: 35 | attribute: "foo" 36 | "attribute.value": "bar" 37 | syncMode: "IMPORT" 38 | 39 | # We define clientSecret in clear form in CR 40 | --- 41 | apiVersion: v1.edp.epam.com/v1 42 | kind: KeycloakRealmIdentityProvider 43 | metadata: 44 | name: keycloakrealmidentityprovider-sample-with-pass 45 | spec: 46 | realmRef: 47 | kind: KeycloakRealm 48 | name: keycloakrealm-sample 49 | alias: facebook 50 | authenticateByDefault: false 51 | enabled: true 52 | firstBrokerLoginFlowAlias: "first broker login" 53 | providerId: "facebook" 54 | config: 55 | clientId: "bar" 56 | clientSecret: "password-in-clear-form" 57 | hideOnLoginPage: "true" 58 | syncMode: "IMPORT" 59 | useJwksUrl: "true" 60 | mappers: 61 | - name: "mymapper" 62 | identityProviderMapper: "hardcoded-attribute-idp-mapper" 63 | identityProviderAlias: "instagram" 64 | config: 65 | attribute: "foo" 66 | "attribute.value": "bar" 67 | syncMode: "IMPORT" 68 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/06-assert-group.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmGroup 3 | metadata: 4 | name: keycloakrealmgroup-sample 5 | spec: 6 | name: developers 7 | realmRef: 8 | kind: KeycloakRealm 9 | name: keycloakrealm-sample 10 | status: 11 | value: "OK" 12 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/06-install-group.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmGroup 3 | metadata: 4 | name: keycloakrealmgroup-sample 5 | spec: 6 | name: developers 7 | realmRef: 8 | kind: KeycloakRealm 9 | name: keycloakrealm-sample 10 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/07-assert-client.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakClient 3 | metadata: 4 | name: keycloakclient-sample 5 | spec: 6 | realmRef: 7 | kind: KeycloakRealm 8 | name: keycloakrealm-sample 9 | clientId: keycloakclient-sample 10 | secret: $client-secret-name:clientSecretKey 11 | webUrl: https://keycloakclient-sample.com 12 | attributes: 13 | post.logout.redirect.uris: "+" 14 | clientRoles: 15 | - administrator 16 | - developer 17 | redirectUris: 18 | - https://keycloakclient-sample.com 19 | - https://keycloakclient-sample.com/* 20 | status: 21 | value: "OK" 22 | 23 | --- 24 | apiVersion: v1.edp.epam.com/v1 25 | kind: KeycloakClient 26 | metadata: 27 | name: keycloakclient-nosecret 28 | spec: 29 | realmRef: 30 | kind: KeycloakRealm 31 | name: keycloakrealm-sample 32 | clientId: keycloakclient-nosecret 33 | secret: $keycloak-client-keycloakclient-nosecret-secret:clientSecret 34 | webUrl: https://keycloakclient-sample.com 35 | attributes: 36 | post.logout.redirect.uris: "+" 37 | clientRoles: 38 | - administrator 39 | - developer 40 | redirectUris: 41 | - https://keycloakclient-sample.com 42 | - https://keycloakclient-sample.com/* 43 | status: 44 | value: "OK" 45 | --- 46 | #keycloak-client-keycloakclient-nosecret-secret:clientSecret 47 | apiVersion: v1 48 | kind: Secret 49 | metadata: 50 | name: keycloak-client-keycloakclient-nosecret-secret 51 | type: Opaque 52 | --- 53 | 54 | apiVersion: v1.edp.epam.com/v1 55 | kind: KeycloakClient 56 | metadata: 57 | name: keycloakclient-serviceaccount 58 | spec: 59 | realmRef: 60 | kind: KeycloakRealm 61 | name: keycloakrealm-sample 62 | clientId: keycloakclient-serviceaccount 63 | secret: $client-secret-name:clientSecretKey 64 | webUrl: https://keycloakclient-serviceaccount.com 65 | attributes: 66 | post.logout.redirect.uris: "+" 67 | clientRoles: 68 | - administrator 69 | - developer 70 | redirectUris: 71 | - https://keycloakclient-serviceaccount.com 72 | - https://keycloakclient-serviceaccount.com/* 73 | serviceAccount: 74 | enabled: true 75 | groups: 76 | - developers 77 | status: 78 | value: "OK" 79 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/07-install-client.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: client-secret-name 5 | type: Opaque 6 | data: 7 | clientSecretKey: dGVzdAo= 8 | 9 | --- 10 | apiVersion: v1.edp.epam.com/v1 11 | kind: KeycloakClient 12 | metadata: 13 | name: keycloakclient-sample 14 | spec: 15 | realmRef: 16 | kind: KeycloakRealm 17 | name: keycloakrealm-sample 18 | clientId: keycloakclient-sample 19 | secret: $client-secret-name:clientSecretKey 20 | webUrl: https://keycloakclient-sample.com 21 | attributes: 22 | post.logout.redirect.uris: "+" 23 | clientRoles: 24 | - administrator 25 | - developer 26 | redirectUris: 27 | - https://keycloakclient-sample.com 28 | - https://keycloakclient-sample.com/* 29 | 30 | --- 31 | apiVersion: v1.edp.epam.com/v1 32 | kind: KeycloakClient 33 | metadata: 34 | name: keycloakclient-nosecret 35 | spec: 36 | realmRef: 37 | kind: KeycloakRealm 38 | name: keycloakrealm-sample 39 | clientId: keycloakclient-nosecret 40 | webUrl: https://keycloakclient-sample.com 41 | attributes: 42 | post.logout.redirect.uris: "+" 43 | clientRoles: 44 | - administrator 45 | - developer 46 | redirectUris: 47 | - https://keycloakclient-sample.com 48 | - https://keycloakclient-sample.com/* 49 | 50 | --- 51 | apiVersion: v1.edp.epam.com/v1 52 | kind: KeycloakClient 53 | metadata: 54 | name: keycloakclient-serviceaccount 55 | spec: 56 | realmRef: 57 | kind: KeycloakRealm 58 | name: keycloakrealm-sample 59 | clientId: keycloakclient-serviceaccount 60 | secret: $client-secret-name:clientSecretKey 61 | webUrl: https://keycloakclient-serviceaccount.com 62 | attributes: 63 | post.logout.redirect.uris: "+" 64 | clientRoles: 65 | - administrator 66 | - developer 67 | redirectUris: 68 | - https://keycloakclient-serviceaccount.com 69 | - https://keycloakclient-serviceaccount.com/* 70 | serviceAccount: 71 | enabled: true 72 | groups: 73 | - developers 74 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/08-assert-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmRole 3 | metadata: 4 | name: keycloakrealmrole-sample 5 | spec: 6 | description: developer role 7 | name: developer 8 | isDefault: true 9 | realmRef: 10 | kind: KeycloakRealm 11 | name: keycloakrealm-sample 12 | status: 13 | value: "OK" 14 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/08-install-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1.edp.epam.com/v1 2 | kind: KeycloakRealmRole 3 | metadata: 4 | name: keycloakrealmrole-sample 5 | spec: 6 | description: developer role 7 | name: developer 8 | isDefault: true 9 | realmRef: 10 | kind: KeycloakRealm 11 | name: keycloakrealm-sample 12 | -------------------------------------------------------------------------------- /tests/e2e/helm-success-path/99-cleanup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kuttl.dev/v1beta1 2 | kind: TestStep 3 | commands: 4 | - command: kubectl delete keycloakrealmrole keycloakrealmrole-sample 5 | namespaced: true 6 | - command: kubectl delete keycloakclient keycloakclient-sample keycloakclient-nosecret keycloakclient-serviceaccount 7 | namespaced: true 8 | - command: kubectl delete keycloakrealmgroup keycloakrealmgroup-sample 9 | namespaced: true 10 | - command: kubectl delete keycloakrealmidentityprovider keycloakrealmidentityprovider-sample keycloakrealmidentityprovider-sample-with-pass 11 | namespaced: true 12 | - command: kubectl delete keycloakrealmcomponent component-sample 13 | namespaced: true 14 | - command: kubectl delete keycloakrealmuser keycloakrealmuser-sample 15 | namespaced: true 16 | - command: kubectl delete keycloakrealm keycloakrealm-sample 17 | namespaced: true 18 | # we have to uninstall helm since clusterwide resources, like ClusterRole are preserved 19 | - command: helm uninstall keycloak-operator-e2e 20 | namespaced: true 21 | -------------------------------------------------------------------------------- /tests/e2e/keycloak-selfsigned-certificates/00-assert-operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: keycloak-operator 5 | status: 6 | readyReplicas: 1 7 | -------------------------------------------------------------------------------- /tests/e2e/keycloak-selfsigned-certificates/00-install-operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kuttl.dev/v1beta1 2 | kind: TestStep 3 | commands: 4 | - command: helm install --set image.repository=${CONTAINER_REGISTRY_URL}/${CONTAINER_REGISTRY_SPACE}/${E2E_IMAGE_REPOSITORY} --set image.tag=${E2E_IMAGE_TAG} --set clusterReconciliationEnabled=true --replace --wait keycloak-operator-e2e ../../../deploy-templates 5 | namespaced: true 6 | -------------------------------------------------------------------------------- /tests/e2e/keycloak-selfsigned-certificates/01-assert-install-keycloak-server.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: test-keycloak 5 | status: 6 | readyReplicas: 1 7 | -------------------------------------------------------------------------------- /tests/e2e/keycloak-selfsigned-certificates/02-assert-keycloak-resource.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1.edp.epam.com/v1 3 | kind: Keycloak 4 | metadata: 5 | name: keycloak-with-no-cert-check 6 | status: 7 | connected: false 8 | 9 | --- 10 | 11 | apiVersion: v1.edp.epam.com/v1 12 | kind: Keycloak 13 | metadata: 14 | name: keycloak-with-insecure-skip-verify 15 | status: 16 | connected: true 17 | 18 | --- 19 | 20 | apiVersion: v1.edp.epam.com/v1 21 | kind: Keycloak 22 | metadata: 23 | name: keycloak-with-ca-cert-secret 24 | status: 25 | connected: true 26 | 27 | --- 28 | 29 | apiVersion: v1.edp.epam.com/v1 30 | kind: Keycloak 31 | metadata: 32 | name: keycloak-with-ca-cert-configmap 33 | status: 34 | connected: true 35 | 36 | --- 37 | 38 | apiVersion: v1.edp.epam.com/v1 39 | kind: Keycloak 40 | metadata: 41 | name: keycloak-with-insecure-flag 42 | status: 43 | connected: true 44 | 45 | --- 46 | 47 | apiVersion: v1.edp.epam.com/v1alpha1 48 | kind: ClusterKeycloak 49 | metadata: 50 | name: keycloak-with-ca-cert-secret 51 | status: 52 | connected: true 53 | 54 | --- 55 | 56 | apiVersion: v1.edp.epam.com/v1alpha1 57 | kind: ClusterKeycloak 58 | metadata: 59 | name: keycloak-with-ca-cert-configmap 60 | status: 61 | connected: true 62 | -------------------------------------------------------------------------------- /tests/e2e/keycloak-selfsigned-certificates/02-install-keycloak-resource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: keycloak-secret 5 | type: Opaque 6 | data: 7 | username: "YWRtaW4=" 8 | password: "YWRtaW4=" 9 | 10 | --- 11 | 12 | apiVersion: v1.edp.epam.com/v1 13 | kind: Keycloak 14 | metadata: 15 | name: keycloak-with-no-cert-check 16 | spec: 17 | secret: keycloak-secret 18 | url: https://test-keycloak:8443 19 | 20 | --- 21 | 22 | apiVersion: v1.edp.epam.com/v1 23 | kind: Keycloak 24 | metadata: 25 | name: keycloak-with-insecure-skip-verify 26 | spec: 27 | secret: keycloak-secret 28 | url: https://test-keycloak:8443 29 | insecureSkipVerify: true 30 | 31 | --- 32 | 33 | apiVersion: v1.edp.epam.com/v1 34 | kind: Keycloak 35 | metadata: 36 | name: keycloak-with-ca-cert-secret 37 | spec: 38 | secret: keycloak-secret 39 | url: https://test-keycloak:8443 40 | caCert: 41 | secretKeyRef: 42 | name: test-keycloak-certs 43 | key: ca.crt 44 | 45 | --- 46 | 47 | apiVersion: v1.edp.epam.com/v1 48 | kind: Keycloak 49 | metadata: 50 | name: keycloak-with-ca-cert-configmap 51 | spec: 52 | secret: keycloak-secret 53 | url: https://test-keycloak:8443 54 | caCert: 55 | configMapKeyRef: 56 | name: test-keycloak-certs 57 | key: ca.crt 58 | 59 | --- 60 | 61 | apiVersion: v1.edp.epam.com/v1 62 | kind: Keycloak 63 | metadata: 64 | name: keycloak-with-insecure-flag 65 | spec: 66 | secret: keycloak-secret 67 | url: https://test-keycloak:8443 68 | insecureSkipVerify: true 69 | 70 | --- 71 | 72 | apiVersion: v1.edp.epam.com/v1alpha1 73 | kind: ClusterKeycloak 74 | metadata: 75 | name: keycloak-with-ca-cert-secret 76 | spec: 77 | secret: keycloak-secret 78 | url: https://test-keycloak:8443 79 | caCert: 80 | secretKeyRef: 81 | name: test-keycloak-certs 82 | key: ca.crt 83 | 84 | --- 85 | 86 | apiVersion: v1.edp.epam.com/v1alpha1 87 | kind: ClusterKeycloak 88 | metadata: 89 | name: keycloak-with-ca-cert-configmap 90 | spec: 91 | secret: keycloak-secret 92 | url: https://test-keycloak:8443 93 | caCert: 94 | configMapKeyRef: 95 | name: test-keycloak-certs 96 | key: ca.crt 97 | -------------------------------------------------------------------------------- /tests/e2e/keycloak-selfsigned-certificates/03-assert-keycloak-realm-resource.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: v1.edp.epam.com/v1 4 | kind: KeycloakRealm 5 | metadata: 6 | name: keycloak-user-profile-self-signed 7 | status: 8 | available: true 9 | value: OK 10 | -------------------------------------------------------------------------------- /tests/e2e/keycloak-selfsigned-certificates/03-install-keycloak-realm-resource.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: v1.edp.epam.com/v1 4 | kind: KeycloakRealm 5 | metadata: 6 | name: keycloak-user-profile-self-signed 7 | spec: 8 | keycloakRef: 9 | kind: Keycloak 10 | name: keycloak-with-ca-cert-configmap 11 | realmName: keycloak-user-profile-self-signed 12 | userProfileConfig: 13 | unmanagedAttributePolicy: ENABLED 14 | -------------------------------------------------------------------------------- /tests/e2e/keycloak-selfsigned-certificates/99-cleanup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kuttl.dev/v1beta1 2 | kind: TestStep 3 | commands: 4 | - command: kubectl delete keycloakrealm keycloak-user-profile-self-signed 5 | namespaced: true 6 | - command: kubectl delete keycloak keycloak-with-no-cert-check keycloak-with-insecure-skip-verify keycloak-with-ca-cert-secret keycloak-with-ca-cert-configmap 7 | namespaced: true 8 | - command: kubectl delete clusterkeycloak keycloak-with-ca-cert-secret keycloak-with-ca-cert-configmap 9 | namespaced: false 10 | - command: helm uninstall keycloak-operator-e2e 11 | namespaced: true 12 | --------------------------------------------------------------------------------