Implement UpdateUser support for the GraphAPI backend
This commit is contained in:
@@ -15,6 +15,9 @@ type Backend interface {
|
|||||||
// DeleteUser deletes a given user, identified by username or id, from the backend
|
// DeleteUser deletes a given user, identified by username or id, from the backend
|
||||||
DeleteUser(ctx context.Context, nameOrId string) error
|
DeleteUser(ctx context.Context, nameOrId string) error
|
||||||
|
|
||||||
|
// UpdateUser applies changes to given user, identified by username or id
|
||||||
|
UpdateUser(ctx context.Context, nameOrId string, user libregraph.User) (*libregraph.User, error)
|
||||||
|
|
||||||
GetUser(ctx context.Context, nameOrId string) (*libregraph.User, error)
|
GetUser(ctx context.Context, nameOrId string) (*libregraph.User, error)
|
||||||
GetUsers(ctx context.Context, queryParam url.Values) ([]*libregraph.User, error)
|
GetUsers(ctx context.Context, queryParam url.Values) ([]*libregraph.User, error)
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ func (i *CS3) DeleteUser(ctx context.Context, nameOrID string) error {
|
|||||||
return errorcode.New(errorcode.NotSupported, "not implemented")
|
return errorcode.New(errorcode.NotSupported, "not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUser implements the Backend Interface. It's currently not suported for the CS3 backend
|
||||||
|
func (i *CS3) UpdateUser(ctx context.Context, nameOrID string, user libregraph.User) (*libregraph.User, error) {
|
||||||
|
return nil, errorcode.New(errorcode.NotSupported, "not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func (i *CS3) GetUser(ctx context.Context, userID string) (*libregraph.User, error) {
|
func (i *CS3) GetUser(ctx context.Context, userID string) (*libregraph.User, error) {
|
||||||
client, err := pool.GetGatewayServiceClient(i.Config.Address)
|
client, err := pool.GetGatewayServiceClient(i.Config.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -160,6 +160,57 @@ func (i *LDAP) DeleteUser(ctx context.Context, nameOrID string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateUser implements the Backend Interface. It's currently not suported for the CS3 backedn
|
||||||
|
func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.User) (*libregraph.User, error) {
|
||||||
|
e, err := i.getLDAPUserByNameOrID(nameOrID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow updates of the ID
|
||||||
|
if user.Id != nil && *user.Id != "" {
|
||||||
|
if e.GetEqualFoldAttributeValue(i.userAttributeMap.id) != *user.Id {
|
||||||
|
return nil, errorcode.New(errorcode.NotAllowed, "changing the UserId is not allowed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: In order to allow updating the user name we'd need to issue a ModRDN operation
|
||||||
|
// As we currently using uid as the naming Attribute for the user entries. (Do we even
|
||||||
|
// want to allow changing the user name?). For now just disallow it.
|
||||||
|
if user.OnPremisesSamAccountName != nil && *user.OnPremisesSamAccountName != "" {
|
||||||
|
if e.GetEqualFoldAttributeValue(i.userAttributeMap.userName) != *user.OnPremisesSamAccountName {
|
||||||
|
return nil, errorcode.New(errorcode.NotSupported, "changing the user name is currently not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mr := ldap.ModifyRequest{DN: e.DN}
|
||||||
|
if user.DisplayName != nil && *user.DisplayName != "" {
|
||||||
|
if e.GetEqualFoldAttributeValue(i.userAttributeMap.displayName) != *user.DisplayName {
|
||||||
|
mr.Replace(i.userAttributeMap.displayName, []string{*user.DisplayName})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if user.Mail != nil && *user.Mail != "" {
|
||||||
|
if e.GetEqualFoldAttributeValue(i.userAttributeMap.mail) != *user.Mail {
|
||||||
|
mr.Replace(i.userAttributeMap.mail, []string{*user.Mail})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if user.PasswordProfile != nil && user.PasswordProfile.Password != nil && *user.PasswordProfile.Password != "" {
|
||||||
|
// password are hashed server side there is no need to check if the new password
|
||||||
|
// is actually different from the old one.
|
||||||
|
mr.Replace("userPassword", []string{*user.PasswordProfile.Password})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := i.conn.Modify(&mr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read back user from LDAP to get the generated UUID
|
||||||
|
e, err = i.getUserByDN(e.DN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return i.createUserModelFromLDAP(e), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (i *LDAP) getUserByDN(dn string) (*ldap.Entry, error) {
|
func (i *LDAP) getUserByDN(dn string) (*ldap.Entry, error) {
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
dn, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 1, 0, false,
|
dn, ldap.ScopeBaseObject, ldap.NeverDerefAliases, 1, 0, false,
|
||||||
|
|||||||
@@ -180,16 +180,31 @@ func (c ConnWithReconnect) Del(d *ldap.DelRequest) error {
|
|||||||
return conn.Del(d)
|
return conn.Del(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConnWithReconnect) Modify(*ldap.ModifyRequest) error {
|
func (c ConnWithReconnect) Modify(m *ldap.ModifyRequest) error {
|
||||||
return ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
|
conn, err := c.GetConnection()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn.Modify(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConnWithReconnect) ModifyDN(*ldap.ModifyDNRequest) error {
|
func (c ConnWithReconnect) ModifyDN(m *ldap.ModifyDNRequest) error {
|
||||||
return ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
|
conn, err := c.GetConnection()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn.ModifyDN(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConnWithReconnect) ModifyWithResult(*ldap.ModifyRequest) (*ldap.ModifyResult, error) {
|
func (c ConnWithReconnect) ModifyWithResult(m *ldap.ModifyRequest) (*ldap.ModifyResult, error) {
|
||||||
return nil, ldap.NewError(ldap.LDAPResultNotSupported, fmt.Errorf("not implemented"))
|
conn, err := c.GetConnection()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn.ModifyWithResult(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ConnWithReconnect) Compare(dn, attribute, value string) (bool, error) {
|
func (c ConnWithReconnect) Compare(dn, attribute, value string) (bool, error) {
|
||||||
|
|||||||
@@ -48,3 +48,8 @@ func (i instrument) PostUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (i instrument) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
func (i instrument) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||||
i.next.DeleteUser(w, r)
|
i.next.DeleteUser(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PatchUser implements the Service interface.
|
||||||
|
func (i instrument) PatchUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
i.next.PatchUser(w, r)
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,3 +48,8 @@ func (l logging) PostUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (l logging) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
func (l logging) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||||
l.next.DeleteUser(w, r)
|
l.next.DeleteUser(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PatchUser implements the Service interface.
|
||||||
|
func (l logging) PatchUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
l.next.PatchUser(w, r)
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Service interface {
|
|||||||
GetUser(http.ResponseWriter, *http.Request)
|
GetUser(http.ResponseWriter, *http.Request)
|
||||||
PostUser(http.ResponseWriter, *http.Request)
|
PostUser(http.ResponseWriter, *http.Request)
|
||||||
DeleteUser(http.ResponseWriter, *http.Request)
|
DeleteUser(http.ResponseWriter, *http.Request)
|
||||||
|
PatchUser(http.ResponseWriter, *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns a service implementation for Service.
|
// NewService returns a service implementation for Service.
|
||||||
@@ -73,6 +74,7 @@ func NewService(opts ...Option) Service {
|
|||||||
r.Route("/{userID}", func(r chi.Router) {
|
r.Route("/{userID}", func(r chi.Router) {
|
||||||
r.Get("/", svc.GetUser)
|
r.Get("/", svc.GetUser)
|
||||||
r.Delete("/", svc.DeleteUser)
|
r.Delete("/", svc.DeleteUser)
|
||||||
|
r.Patch("/", svc.PatchUser)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
r.Route("/groups", func(r chi.Router) {
|
r.Route("/groups", func(r chi.Router) {
|
||||||
|
|||||||
@@ -44,3 +44,8 @@ func (t tracing) PostUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (t tracing) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
func (t tracing) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||||
t.next.DeleteUser(w, r)
|
t.next.DeleteUser(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PatchUser implements the Service interface.
|
||||||
|
func (t tracing) PatchUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.next.PatchUser(w, r)
|
||||||
|
}
|
||||||
|
|||||||
@@ -123,6 +123,41 @@ func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
render.NoContent(w, r)
|
render.NoContent(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PatchUser implements the Service Interface. Updates the specified attributes of an
|
||||||
|
// ExistingUser
|
||||||
|
func (g Graph) PatchUser(w http.ResponseWriter, r *http.Request) {
|
||||||
|
nameOrID := chi.URLParam(r, "userID")
|
||||||
|
nameOrID, err := url.PathUnescape(nameOrID)
|
||||||
|
if err != nil {
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping user id failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if nameOrID == "" {
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing user id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
changes := libregraph.NewUser()
|
||||||
|
err = json.NewDecoder(r.Body).Decode(changes)
|
||||||
|
if err != nil {
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := g.identityBackend.UpdateUser(r.Context(), nameOrID, *changes)
|
||||||
|
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.StatusOK)
|
||||||
|
render.JSON(w, r, u)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func isNilOrEmpty(s *string) bool {
|
func isNilOrEmpty(s *string) bool {
|
||||||
return s == nil || *s == ""
|
return s == nil || *s == ""
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user