Skip to content

Commit

Permalink
Implement DeleteUser support for the Graph LDAP backend
Browse files Browse the repository at this point in the history
  • Loading branch information
rhafer committed Jan 12, 2022
1 parent f98ba20 commit c20ff80
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 9 deletions.
3 changes: 3 additions & 0 deletions graph/pkg/identity/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 5 additions & 0 deletions graph/pkg/identity/cs3.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
36 changes: 29 additions & 7 deletions graph/pkg/identity/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -170,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,
Expand All @@ -194,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)
}
}
Expand All @@ -204,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) {
Expand Down
9 changes: 7 additions & 2 deletions graph/pkg/identity/ldap/reconnect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions graph/pkg/service/v0/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
26 changes: 26 additions & 0 deletions graph/pkg/service/v0/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 || *s == ""
}

0 comments on commit c20ff80

Please sign in to comment.