From d57d40a0c12249e1841edeacebf8c8f66bdb7122 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Tue, 18 Jan 2022 11:17:57 +0100 Subject: [PATCH] graph: Fix LDAP retry handling The reconnect package was missing the retry loop for LDAP Write Operations (add, delete, mod, modrdn) Fixes: #2974 --- changelog/unreleased/fix-ldapretry.md | 6 ++ graph/pkg/identity/ldap/reconnect.go | 131 +++++++++++++++++++------- 2 files changed, 101 insertions(+), 36 deletions(-) create mode 100644 changelog/unreleased/fix-ldapretry.md diff --git a/changelog/unreleased/fix-ldapretry.md b/changelog/unreleased/fix-ldapretry.md new file mode 100644 index 00000000000..0a9004b90af --- /dev/null +++ b/changelog/unreleased/fix-ldapretry.md @@ -0,0 +1,6 @@ +Bugfix: Fix retry handling for LDAP connections + +We've fixed the handling of network issues (e.g. connection loss) during LDAP Write Operations +to correcty retry the request. + +https://github.com/owncloud/ocis/issues/2974 diff --git a/graph/pkg/identity/ldap/reconnect.go b/graph/pkg/identity/ldap/reconnect.go index b4e868846c8..8fba2e61016 100644 --- a/graph/pkg/identity/ldap/reconnect.go +++ b/graph/pkg/identity/ldap/reconnect.go @@ -64,6 +64,101 @@ func (c ConnWithReconnect) Search(sr *ldap.SearchRequest) (*ldap.SearchResult, e return nil, ldap.NewError(ldap.ErrorNetwork, errors.New("max retries")) } +func (c ConnWithReconnect) Add(a *ldap.AddRequest) error { + conn, err := c.GetConnection() + if err != nil { + return err + } + for try := 0; try <= c.retries; try++ { + err = conn.Add(a) + if !ldap.IsErrorWithCode(err, ldap.ErrorNetwork) { + // non network error, return it to the client + return err + } + + c.logger.Debug().Msgf("Network Error. attempt %d", try) + conn, err = c.reconnect(conn) + if err != nil { + return err + } + c.logger.Debug().Msg("retrying LDAP Add") + } + // if we get here we reached the maximum retries. So return an error + return ldap.NewError(ldap.ErrorNetwork, errors.New("max retries")) +} + +func (c ConnWithReconnect) Del(d *ldap.DelRequest) error { + conn, err := c.GetConnection() + if err != nil { + return err + } + + for try := 0; try <= c.retries; try++ { + err = conn.Del(d) + if !ldap.IsErrorWithCode(err, ldap.ErrorNetwork) { + // non network error, return it to the client + return err + } + + c.logger.Debug().Msgf("Network Error. attempt %d", try) + conn, err = c.reconnect(conn) + if err != nil { + return err + } + c.logger.Debug().Msg("retrying LDAP Del") + } + // if we get here we reached the maximum retries. So return an error + return ldap.NewError(ldap.ErrorNetwork, errors.New("max retries")) +} + +func (c ConnWithReconnect) Modify(m *ldap.ModifyRequest) error { + conn, err := c.GetConnection() + if err != nil { + return err + } + + for try := 0; try <= c.retries; try++ { + err = conn.Modify(m) + if !ldap.IsErrorWithCode(err, ldap.ErrorNetwork) { + // non network error, return it to the client + return err + } + + c.logger.Debug().Msgf("Network Error. attempt %d", try) + conn, err = c.reconnect(conn) + if err != nil { + return err + } + c.logger.Debug().Msg("retrying LDAP Modify") + } + // if we get here we reached the maximum retries. So return an error + return ldap.NewError(ldap.ErrorNetwork, errors.New("max retries")) +} + +func (c ConnWithReconnect) ModifyDN(m *ldap.ModifyDNRequest) error { + conn, err := c.GetConnection() + if err != nil { + return err + } + + for try := 0; try <= c.retries; try++ { + err = conn.ModifyDN(m) + if !ldap.IsErrorWithCode(err, ldap.ErrorNetwork) { + // non network error, return it to the client + return err + } + + c.logger.Debug().Msgf("Network Error. attempt %d", try) + conn, err = c.reconnect(conn) + if err != nil { + return err + } + c.logger.Debug().Msg("retrying LDAP ModifyDN") + } + // if we get here we reached the maximum retries. So return an error + return ldap.NewError(ldap.ErrorNetwork, errors.New("max retries")) +} + func (c ConnWithReconnect) GetConnection() (*ldap.Conn, error) { conn := <-c.conn if conn.Conn != nil && !ldap.IsErrorWithCode(conn.Error, ldap.ErrorNetwork) { @@ -162,42 +257,6 @@ func (c ConnWithReconnect) ExternalBind() error { return ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented")) } -func (c ConnWithReconnect) Add(a *ldap.AddRequest) error { - conn, err := c.GetConnection() - if err != nil { - return err - } - - return conn.Add(a) -} - -func (c ConnWithReconnect) Del(d *ldap.DelRequest) error { - conn, err := c.GetConnection() - if err != nil { - return err - } - - return conn.Del(d) -} - -func (c ConnWithReconnect) Modify(m *ldap.ModifyRequest) error { - conn, err := c.GetConnection() - if err != nil { - return err - } - - return conn.Modify(m) -} - -func (c ConnWithReconnect) ModifyDN(m *ldap.ModifyDNRequest) error { - conn, err := c.GetConnection() - if err != nil { - return err - } - - return conn.ModifyDN(m) -} - func (c ConnWithReconnect) ModifyWithResult(m *ldap.ModifyRequest) (*ldap.ModifyResult, error) { conn, err := c.GetConnection() if err != nil {