From 28139591bdc888a8e7c8024440fa67eab0133f60 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 | 4 ++++ graph/pkg/identity/ldap.go | 33 ++++++++++++++++++++++------ graph/pkg/identity/ldap/reconnect.go | 9 ++++++-- graph/pkg/service/v0/service.go | 1 + graph/pkg/service/v0/users.go | 26 ++++++++++++++++++++++ 6 files changed, 67 insertions(+), 9 deletions(-) diff --git a/graph/pkg/identity/backend.go b/graph/pkg/identity/backend.go index d716009af8a..1a71a7b5d08 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 6fcfd499212..6fb2e7e8b42 100644 --- a/graph/pkg/identity/cs3.go +++ b/graph/pkg/identity/cs3.go @@ -24,6 +24,10 @@ func (i *CS3) CreateUser(ctx context.Context, user libregraph.User) (*libregraph return nil, errorcode.New(errorcode.NotSupported, "not implemented") } +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 f7efc6a4f75..1efd78c526f 100644 --- a/graph/pkg/identity/ldap.go +++ b/graph/pkg/identity/ldap.go @@ -147,6 +147,17 @@ func (i *LDAP) CreateUser(ctx context.Context, user libregraph.User) (*libregrap } return i.createUserModelFromLDAP(e), nil } + +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) { @@ -173,15 +184,14 @@ func (i *LDAP) getUserByDN(dn string) (*ldap.Entry, error) { 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, 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, @@ -197,7 +207,7 @@ 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).Msg(errmsg) } } @@ -207,7 +217,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 bf5c7a1bd3c..272a0043e93 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 15fa77e436d..004d3689243 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 7d93aad21b7..e2a783deda5 100644 --- a/graph/pkg/service/v0/users.go +++ b/graph/pkg/service/v0/users.go @@ -98,6 +98,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 || len(*s) == 0 }