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") } }