From 4915195d9c250909fbe4836056b21d8cb4fed7e7 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Tue, 11 Jan 2022 17:22:48 +0100 Subject: [PATCH] Implement DeleteUser support for the Graph LDAP backend --- graph/pkg/identity/backend.go | 3 +++ graph/pkg/identity/cs3.go | 5 ++++ graph/pkg/identity/ldap.go | 36 ++++++++++++++++++++++------ graph/pkg/identity/ldap/reconnect.go | 9 +++++-- graph/pkg/service/v0/service.go | 1 + graph/pkg/service/v0/users.go | 26 ++++++++++++++++++++ 6 files changed, 71 insertions(+), 9 deletions(-) diff --git a/graph/pkg/identity/backend.go b/graph/pkg/identity/backend.go index d716009af..1a71a7b5d 100644 --- a/graph/pkg/identity/backend.go +++ b/graph/pkg/identity/backend.go @@ -12,6 +12,9 @@ type Backend interface { // CreateUser creates a given user in the identity backend. CreateUser(ctx context.Context, user libregraph.User) (*libregraph.User, error) + // DeleteUser deletes a given user, identified by username or id, from the backend + DeleteUser(ctx context.Context, nameOrId string) error + GetUser(ctx context.Context, nameOrId string) (*libregraph.User, error) GetUsers(ctx context.Context, queryParam url.Values) ([]*libregraph.User, error) diff --git a/graph/pkg/identity/cs3.go b/graph/pkg/identity/cs3.go index 272bccfe6..5fe0117c8 100644 --- a/graph/pkg/identity/cs3.go +++ b/graph/pkg/identity/cs3.go @@ -25,6 +25,11 @@ func (i *CS3) CreateUser(ctx context.Context, user libregraph.User) (*libregraph return nil, errorcode.New(errorcode.NotSupported, "not implemented") } +// DeleteUser implements the Backend Interface. It's currently not supported for the CS3 backend +func (i *CS3) DeleteUser(ctx context.Context, nameOrID string) error { + return errorcode.New(errorcode.NotSupported, "not implemented") +} + func (i *CS3) GetUser(ctx context.Context, userID string) (*libregraph.User, error) { client, err := pool.GetGatewayServiceClient(i.Config.Address) if err != nil { diff --git a/graph/pkg/identity/ldap.go b/graph/pkg/identity/ldap.go index 1becb2ca4..b9f9c65cf 100644 --- a/graph/pkg/identity/ldap.go +++ b/graph/pkg/identity/ldap.go @@ -146,6 +146,20 @@ func (i *LDAP) CreateUser(ctx context.Context, user libregraph.User) (*libregrap return i.createUserModelFromLDAP(e), nil } +// DeleteUser implements the Backend Interface. It permanently deletes a User identified +// by name or id from the LDAP server +func (i *LDAP) DeleteUser(ctx context.Context, nameOrID string) error { + e, err := i.getLDAPUserByNameOrID(nameOrID) + if err != nil { + return err + } + dr := ldap.DelRequest{DN: e.DN} + if err = i.conn.Del(&dr); err != nil { + return err + } + return nil +} + func (i *LDAP) getUserByDN(dn string) (*ldap.Entry, error) { searchRequest := ldap.NewSearchRequest( dn, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 1, 0, false, @@ -173,12 +187,11 @@ func (i *LDAP) getUserByDN(dn string) (*ldap.Entry, error) { return res.Entries[0], nil } -func (i *LDAP) GetUser(ctx context.Context, userID string) (*libregraph.User, error) { - i.logger.Debug().Str("backend", "ldap").Msg("GetUser") - userID = ldap.EscapeFilter(userID) +func (i *LDAP) getLDAPUserByNameOrID(nameOrID string) (*ldap.Entry, error) { + nameOrID = ldap.EscapeFilter(nameOrID) searchRequest := ldap.NewSearchRequest( i.userBaseDN, i.userScope, ldap.NeverDerefAliases, 1, 0, false, - fmt.Sprintf("(&%s(|(%s=%s)(%s=%s)))", i.userFilter, i.userAttributeMap.userName, userID, i.userAttributeMap.id, userID), + fmt.Sprintf("(&%s(|(%s=%s)(%s=%s)))", i.userFilter, i.userAttributeMap.userName, nameOrID, i.userAttributeMap.id, nameOrID), []string{ i.userAttributeMap.displayName, i.userAttributeMap.id, @@ -194,9 +207,9 @@ func (i *LDAP) GetUser(ctx context.Context, userID string) (*libregraph.User, er var errmsg string if lerr, ok := err.(*ldap.Error); ok { if lerr.ResultCode == ldap.LDAPResultSizeLimitExceeded { - errmsg = fmt.Sprintf("too many results searching for user '%s'", userID) + errmsg = fmt.Sprintf("too many results searching for user '%s'", nameOrID) i.logger.Debug().Str("backend", "ldap").Err(lerr). - Str("user", userID).Msg("too many results searching for user") + Str("user", nameOrID).Msg("too many results searching for user") } } return nil, errorcode.New(errorcode.ItemNotFound, errmsg) @@ -205,7 +218,16 @@ func (i *LDAP) GetUser(ctx context.Context, userID string) (*libregraph.User, er return nil, errorcode.New(errorcode.ItemNotFound, "not found") } - return i.createUserModelFromLDAP(res.Entries[0]), nil + return res.Entries[0], nil +} + +func (i *LDAP) GetUser(ctx context.Context, nameOrID string) (*libregraph.User, error) { + i.logger.Debug().Str("backend", "ldap").Msg("GetUser") + e, err := i.getLDAPUserByNameOrID(nameOrID) + if err != nil { + return nil, err + } + return i.createUserModelFromLDAP(e), nil } func (i *LDAP) GetUsers(ctx context.Context, queryParam url.Values) ([]*libregraph.User, error) { diff --git a/graph/pkg/identity/ldap/reconnect.go b/graph/pkg/identity/ldap/reconnect.go index bf5c7a1bd..272a0043e 100644 --- a/graph/pkg/identity/ldap/reconnect.go +++ b/graph/pkg/identity/ldap/reconnect.go @@ -171,8 +171,13 @@ func (c ConnWithReconnect) Add(a *ldap.AddRequest) error { return conn.Add(a) } -func (c ConnWithReconnect) Del(*ldap.DelRequest) error { - return ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented")) +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(*ldap.ModifyRequest) error { diff --git a/graph/pkg/service/v0/service.go b/graph/pkg/service/v0/service.go index 15fa77e43..004d36892 100644 --- a/graph/pkg/service/v0/service.go +++ b/graph/pkg/service/v0/service.go @@ -70,6 +70,7 @@ func NewService(opts ...Option) Service { r.Post("/", svc.PostUser) r.Route("/{userID}", func(r chi.Router) { r.Get("/", svc.GetUser) + r.Delete("/", svc.DeleteUser) }) }) r.Route("/groups", func(r chi.Router) { diff --git a/graph/pkg/service/v0/users.go b/graph/pkg/service/v0/users.go index 7cdd802e5..0593c6bcb 100644 --- a/graph/pkg/service/v0/users.go +++ b/graph/pkg/service/v0/users.go @@ -97,6 +97,32 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, user) } +func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) { + userID := chi.URLParam(r, "userID") + userID, err := url.PathUnescape(userID) + if err != nil { + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping user id failed") + } + + if userID == "" { + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing user id") + return + } + + err = g.identityBackend.DeleteUser(r.Context(), userID) + + if err != nil { + var errcode errorcode.Error + if errors.As(err, &errcode) { + errcode.Render(w, r) + } else { + errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) + } + } + render.Status(r, http.StatusNoContent) + render.NoContent(w, r) +} + func isNilOrEmpty(s *string) bool { return s == nil || *s == "" }