Files
HaimKortovich 319acd90de
All checks were successful
Build and Publish / build-release (push) Successful in 2m9s
allow to reference via zitadel too
2026-04-30 15:36:20 -05:00

227 lines
6.4 KiB
Go

package v1alpha1
import (
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
)
// ResolvedReference is the common abstraction for both reference types
type ResolvedReference struct {
ID string
Namespace string
Name string
}
// +kubebuilder:object:generate=false
type RefResolver struct {
client client.Client
}
func NewRefResolver(client client.Client) *RefResolver {
return &RefResolver{
client: client,
}
}
func (r *RefResolver) OIDCAppRef(ctx context.Context, ref *OIDCAppRef,
namespace string) (*OIDCApp, error) {
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "OIDCApp" {
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
}
key := types.NamespacedName{
Name: ref.ObjectReference.Name,
Namespace: namespace,
}
if ref.ObjectReference.Namespace != "" {
key.Namespace = ref.ObjectReference.Namespace
}
var zitadel OIDCApp
if err := r.client.Get(ctx, key, &zitadel); err != nil {
return nil, err
}
return &zitadel, nil
}
func (r *RefResolver) ActionRef(ctx context.Context, ref *ActionRef,
namespace string) (*Action, error) {
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "Action" {
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
}
key := types.NamespacedName{
Name: ref.ObjectReference.Name,
Namespace: namespace,
}
if ref.ObjectReference.Namespace != "" {
key.Namespace = ref.ObjectReference.Namespace
}
var zitadel Action
if err := r.client.Get(ctx, key, &zitadel); err != nil {
return nil, err
}
return &zitadel, nil
}
func (r *RefResolver) ProjectRef(ctx context.Context, ref *ProjectRef,
namespace string) (*Project, error) {
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "Project" {
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
}
key := types.NamespacedName{
Name: ref.ObjectReference.Name,
Namespace: namespace,
}
if ref.ObjectReference.Namespace != "" {
key.Namespace = ref.ObjectReference.Namespace
}
var zitadel Project
if err := r.client.Get(ctx, key, &zitadel); err != nil {
return nil, err
}
return &zitadel, nil
}
func (r *RefResolver) OrganizationRef(ctx context.Context, ref *OrganizationRef,
namespace string) (*Organization, error) {
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "Organization" {
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
}
key := types.NamespacedName{
Name: ref.ObjectReference.Name,
Namespace: namespace,
}
if ref.ObjectReference.Namespace != "" {
key.Namespace = ref.ObjectReference.Namespace
}
var zitadel Organization
if err := r.client.Get(ctx, key, &zitadel); err != nil {
return nil, err
}
return &zitadel, nil
}
func (r *RefResolver) ConnectionRef(ctx context.Context, ref *ConnectionRef, namespace string) (*Connection, error) {
if ref.ObjectReference.Kind != "" && ref.ObjectReference.Kind != "Connection" {
return nil, fmt.Errorf("Unsupported reference kind: '%s'", ref.ObjectReference.Kind)
}
key := types.NamespacedName{
Name: ref.ObjectReference.Name,
Namespace: namespace,
}
if ref.ObjectReference.Namespace != "" {
key.Namespace = ref.ObjectReference.Namespace
}
var connection Connection
if err := r.client.Get(ctx, key, &connection); err != nil {
return nil, err
}
return &connection, nil
}
func (r *RefResolver) SecretKeyRef(ctx context.Context, selector corev1.SecretKeySelector,
namespace string) (string, error) {
nn := types.NamespacedName{
Name: selector.Name,
Namespace: namespace,
}
var secret v1.Secret
if err := r.client.Get(ctx, nn, &secret); err != nil {
return "", fmt.Errorf("error getting secret: %v", err)
}
data, ok := secret.Data[selector.Key]
if !ok {
return "", fmt.Errorf("secret key \"%s\" not found", selector.Key)
}
return string(data), nil
}
// ResolveOrganization resolves an organization reference to a common abstraction
func (r *RefResolver) ResolveOrganization(ctx context.Context, ref *OrganizationRef, namespace string) (*ResolvedReference, error) {
// Priority 1: Direct Zitadel ID reference (cross-cluster)
if ref.ID != "" {
return &ResolvedReference{
ID: ref.ID,
}, nil
}
// Priority 2: K8s object reference (same-cluster, backward compatible)
if ref.ObjectReference.Name != "" {
org, err := r.OrganizationRef(ctx, ref, namespace)
if err != nil {
return nil, err
}
if org.Status.OrganizationId == nil {
return nil, fmt.Errorf("organization not ready")
}
return &ResolvedReference{
ID: *org.Status.OrganizationId,
Namespace: org.Namespace,
Name: org.Name,
}, nil
}
return nil, fmt.Errorf("no valid organization reference provided")
}
// ResolveProject resolves a project reference to a common abstraction
func (r *RefResolver) ResolveProject(ctx context.Context, ref *ProjectRef, namespace string) (*ResolvedReference, error) {
// Priority 1: Direct Zitadel ID reference (cross-cluster)
if ref.ID != "" {
return &ResolvedReference{
ID: ref.ID,
}, nil
}
// Priority 2: K8s object reference (same-cluster, backward compatible)
if ref.ObjectReference.Name != "" {
project, err := r.ProjectRef(ctx, ref, namespace)
if err != nil {
return nil, err
}
if project.Status.ProjectId == nil {
return nil, fmt.Errorf("project not ready")
}
return &ResolvedReference{
ID: *project.Status.ProjectId,
Namespace: project.Namespace,
Name: project.Name,
}, nil
}
return nil, fmt.Errorf("no valid project reference provided")
}
// ResolveConnectionForRef resolves connection from either a ConnectionRef or enhanced reference with embedded ConnectionRef
func (r *RefResolver) ResolveConnectionForRef(ctx context.Context, ref interface{}, namespace string) (*Connection, error) {
switch v := ref.(type) {
case *ConnectionRef:
return r.ConnectionRef(ctx, v, namespace)
case *OrganizationRef:
if v.ID != "" {
return r.ConnectionRef(ctx, &v.ConnectionRef, namespace)
}
return nil, fmt.Errorf("organization reference does not contain connection info")
case *ProjectRef:
if v.ID != "" {
return r.ConnectionRef(ctx, &v.ConnectionRef, namespace)
}
return nil, fmt.Errorf("project reference does not contain connection info")
default:
return nil, fmt.Errorf("unsupported reference type for connection resolution")
}
}