All checks were successful
Build and Publish / build-release (push) Successful in 2m9s
227 lines
6.4 KiB
Go
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")
|
|
}
|
|
}
|