From 29ca2f42debe07bc783d293ee4299ee05236a350 Mon Sep 17 00:00:00 2001 From: vinamra28 Date: Tue, 18 Aug 2026 17:47:57 +0530 Subject: [PATCH] Authenticate to LDAP using creds are configured In case where connection to ldap is guarded behind basic auth, unstructured controller should be able to honor that and check for user details from ldap server. Signed-off-by: vinamra28 --- api/v1alpha1/controllerconfig_types.go | 4 ++ ...ataverse.redhat.com_controllerconfigs.yaml | 5 +++ .../operator_v1alpha1_controllerconfig.yaml | 1 + .../controller/controllerconfig_controller.go | 6 +++ pkg/gdrive/ldap/client.go | 43 +++++++++++++++---- pkg/unstructured/source.go | 9 ++-- 6 files changed, 56 insertions(+), 12 deletions(-) diff --git a/api/v1alpha1/controllerconfig_types.go b/api/v1alpha1/controllerconfig_types.go index 717f330e..fe9b0e2a 100644 --- a/api/v1alpha1/controllerconfig_types.go +++ b/api/v1alpha1/controllerconfig_types.go @@ -36,6 +36,7 @@ import ( // userSearchFilter: "(objectClass=person)" // emailAttribute: "mail" // attributes: ["uid", "mail"] +// bindUserName: "foobar" // googleDriveConfig: # optional, required for googleDrive source type // maxRetries: 3 // concurrentFolders: 5 @@ -133,6 +134,9 @@ type LDAPConfig struct { // Attributes is the list of LDAP attributes to retrieve. // +optional Attributes []string `json:"attributes,omitempty"` + // BindUserName is the username for the LDAP server. Password is fetched from the secret specified in the SecretRef field. + // +optional + BindUserName string `json:"bindUserName,omitempty"` } // ControllerConfigStatus defines the observed state of ControllerConfig. diff --git a/config/crd/bases/operator.dataverse.redhat.com_controllerconfigs.yaml b/config/crd/bases/operator.dataverse.redhat.com_controllerconfigs.yaml index 4aae9997..4633e5f3 100644 --- a/config/crd/bases/operator.dataverse.redhat.com_controllerconfigs.yaml +++ b/config/crd/bases/operator.dataverse.redhat.com_controllerconfigs.yaml @@ -81,6 +81,11 @@ spec: baseUserDN: description: BaseUserDN is the base DN for user searches. type: string + bindUserName: + description: BindUserName is the username for the LDAP server. + Password is fetched from the secret specified in the SecretRef + field. + type: string emailAttribute: description: EmailAttribute is the LDAP attribute containing user email addresses (e.g., "mail"). diff --git a/config/samples/operator_v1alpha1_controllerconfig.yaml b/config/samples/operator_v1alpha1_controllerconfig.yaml index a3faa384..667651db 100644 --- a/config/samples/operator_v1alpha1_controllerconfig.yaml +++ b/config/samples/operator_v1alpha1_controllerconfig.yaml @@ -21,6 +21,7 @@ spec: # userSearchFilter: "(objectClass=person)" # emailAttribute: "mail" # attributes: ["uid", "mail"] + # bindUserName: "foobar" # Optional: Google Drive controller-level settings (required when using googleDrive source type) # googleDriveConfig: # maxRetries: 3 diff --git a/internal/controller/controllerconfig_controller.go b/internal/controller/controllerconfig_controller.go index 1837bcde..2a5f6c8e 100644 --- a/internal/controller/controllerconfig_controller.go +++ b/internal/controller/controllerconfig_controller.go @@ -142,6 +142,10 @@ func (r *ControllerConfigReconciler) Reconcile(ctx context.Context, req ctrl.Req // initialize LDAP client and cache if configured if config.Spec.LDAPConfig != nil && config.Spec.LDAPConfig.Server != "" { ldapCfg := *config.Spec.LDAPConfig + + // fetch LDAP password from secret if provided + ldapPassword := string(secret.Data["LDAP_PASSWORD"]) + lc, err := ldap.InitLDAP(ldap.Config{ Server: ldapCfg.Server, GroupDN: ldapCfg.GroupDN, @@ -150,6 +154,8 @@ func (r *ControllerConfigReconciler) Reconcile(ctx context.Context, req ctrl.Req UserSearchFilter: ldapCfg.UserSearchFilter, EmailAttribute: ldapCfg.EmailAttribute, Attributes: ldapCfg.Attributes, + BindUserName: ldapCfg.BindUserName, + BindPassword: ldapPassword, }) if err != nil { logger.Error(err, "failed to initialize LDAP client, will retry") diff --git a/pkg/gdrive/ldap/client.go b/pkg/gdrive/ldap/client.go index dea5a912..05cb8052 100644 --- a/pkg/gdrive/ldap/client.go +++ b/pkg/gdrive/ldap/client.go @@ -42,6 +42,9 @@ type Config struct { EmailAttribute string `yaml:"emailAttribute" json:"emailAttribute"` // Attributes is the list of LDAP attributes to retrieve. Attributes []string `yaml:"attributes" json:"attributes"` + + BindUserName string `yaml:"bindUserName" json:"bindUserName"` + BindPassword string `yaml:"bindPassword" json:"bindPassword"` } // DefaultConfig returns a Config with generic LDAP defaults. @@ -57,6 +60,7 @@ func DefaultConfig() Config { type LDAPConnClient interface { IsClosing() bool Search(*ldap.SearchRequest) (*ldap.SearchResult, error) + Bind(username, password string) error UnauthenticatedBind(username string) error } @@ -71,6 +75,8 @@ type LDAPConn struct { userSearchFilter string emailAttribute string attributes []string + bindUserName string + bindPassword string } // Client defines the interface for LDAP operations used by the gdrive package. @@ -87,11 +93,19 @@ func InitLDAP(config Config) (Client, error) { return nil, fmt.Errorf("failed to connect to LDAP server %s: %w", config.Server, err) } - // Perform anonymous bind - err = ldapConn.UnauthenticatedBind("") - if err != nil { - _ = ldapConn.Close() - return nil, fmt.Errorf("failed to bind LDAP connection: %w", err) + if config.BindUserName != "" && config.BindPassword != "" { + err = ldapConn.Bind(config.BindUserName, config.BindPassword) + if err != nil { + _ = ldapConn.Close() + return nil, fmt.Errorf("failed to bind LDAP connection: %w", err) + } + } else { + // Perform anonymous bind + err = ldapConn.UnauthenticatedBind("") + if err != nil { + _ = ldapConn.Close() + return nil, fmt.Errorf("failed to bind LDAP connection: %w", err) + } } emailAttr := config.EmailAttribute @@ -108,6 +122,8 @@ func InitLDAP(config Config) (Client, error) { userSearchFilter: config.UserSearchFilter, emailAttribute: emailAttr, attributes: config.Attributes, + bindUserName: config.BindUserName, + bindPassword: config.BindPassword, }, nil } @@ -120,10 +136,19 @@ func (l *LDAPConn) getConn() LDAPConnClient { if err != nil { return nil } - err = newConn.UnauthenticatedBind("") - if err != nil { - _ = newConn.Close() - return nil + + if l.bindUserName != "" && l.bindPassword != "" { + err = newConn.Bind(l.bindUserName, l.bindPassword) + if err != nil { + _ = newConn.Close() + return nil + } + } else { + err = newConn.UnauthenticatedBind("") + if err != nil { + _ = newConn.Close() + return nil + } } l.conn = newConn } diff --git a/pkg/unstructured/source.go b/pkg/unstructured/source.go index 61e33b50..8ff7854e 100644 --- a/pkg/unstructured/source.go +++ b/pkg/unstructured/source.go @@ -362,10 +362,13 @@ func (g *GDriveSource) SyncFilesToFilestore(ctx context.Context, fs *filestore.F if stored { logger.Info("stored gdrive file", "fileID", record.FileID, "fileName", record.FileName) - mu.Lock() - storedFiles = append(storedFiles, file) - mu.Unlock() } + + // still append the file to the storedFiles list, + mu.Lock() + storedFiles = append(storedFiles, file) + mu.Unlock() + return nil }) }