Modify User (as a regular user) fails #891#959
Conversation
|
Shouldn't validateModifyUser be deleted now? With the changes that @zwass added to change password and the removal of this function call in this PR, it looks like dead code to me. Let's remove it. Other than that, the change LGTM. |
|
using the canPerformactions Middleware allows the user to modify any of the properties in the ModifyUser method. https://github.com/kolide/kolide-ose/blob/3802f3098ecd3c0a5fab7b955e4ff71803dbfddc/server/service/service_users.go#L52 This would include some very undesirable security issues such as allowing a user to disable their own account and promote themselves to Admin. Either update the validateModifyUser endpoint and use that, or introduce separate endpoints for security sensitive modifications on a user. I think @zwass had plans to do the latter. |
groob
left a comment
There was a problem hiding this comment.
implement changes in the comments above
|
After discussing with @groob I added two new endpoints that allow admin users to set/unset admin and enabled status for users. Not the id in the url is the id of the user whose admin or enabled status will be changed. This simplifies the authorization of these operations. If operations succeed an empty response is returned. If a user isn't an admin the http status is 503, otherwise a standard error structure is returned describing what went horribly awry. |
|
The new endpoints should not use PATCH, use POST instead. |
|
If you're returning an empty response, make sure the code is 204 NO Content. IMO it would be more consistent to return the modified user in the response. |
| SaveUserEnabledFuncInvoked bool | ||
| } | ||
|
|
||
| func (s *UserStore) SaveUserAdmin(id uint, isAdmin bool) error { |
There was a problem hiding this comment.
This file is for generated code. There's a helpers file for handwritten methods.
|
@groob I'd favor returning the request with the new values instead of the whole user object. The reason being is that fetching the user would entail another sql select request and a network round trip for nothing. Admittedly, it's not a huge cost, but essentially it's burning cycles for nothing. |
| SaveUser(user *User) error | ||
| // SaveUserAdmin sets the value of the user admin field for user identified | ||
| // by id. | ||
| SaveUserAdmin(id uint, isAdmin bool) error |
There was a problem hiding this comment.
These methods should not exist. Just use SaveUser
|
|
||
| type SaveUserFunc func(user *kolide.User) error | ||
|
|
||
| type SaveUserAdminFunc func(id uint, isAdmin bool) error |
There was a problem hiding this comment.
Need to remove these methods. they're not in the PR anymore.
| payload := kolide.UserPayload{ | ||
| Admin: &req.Admin, | ||
| } | ||
| user, err := svc.ModifyUser(ctx, req.ID, payload) |
There was a problem hiding this comment.
These endpoints can't use the ModifyUser method on service as they're shared by the ModifyUser endpoint, which has different authorization.
The service methods must exist and be different. Only the datastore method for SaveUser can be shared. Sorry for the confusion.
| Admin: &req.Admin, | ||
| } | ||
| user, err := svc.ModifyUser(ctx, req.ID, payload) | ||
| user, err := svc.User(ctx, req.ID) |
There was a problem hiding this comment.
The endpoint factory function is for creating an endpoint, not for business logic. Any app changes should happen within the service layer. This might sound odd but the separation of concerns allows us to cleanly add metrics, logging, validation and so on.
I'd revert this commit and create two service methods for the actions you're adding.
|
|
||
| // ModifyUser updates a user's parameters given a UserPayload. | ||
| ModifyUser(ctx context.Context, userID uint, p UserPayload) (user *User, err error) | ||
| // ChangeUserAdmin is used to modify the admin state of the user identified by id. |
There was a problem hiding this comment.
Add spacing like the rest of the documented methods on this service.
| // ModifyUser updates a user's parameters given a UserPayload. | ||
| ModifyUser(ctx context.Context, userID uint, p UserPayload) (user *User, err error) | ||
| // ChangeUserAdmin is used to modify the admin state of the user identified by id. | ||
| ChangeUserAdmin(id uint, isAdmin bool) (*User, error) |
There was a problem hiding this comment.
add the ctx context.Context method as a first param to all service methods. We populate context with information like who is making the request.
Finally, add the user and err params for the return types. I am only asking for this last one because when working with the types package to do codegen/refactoring, it helps to have the variable names.
| // ChangeUserAdmin is used to modify the admin state of the user identified by id. | ||
| ChangeUserAdmin(id uint, isAdmin bool) (*User, error) | ||
| // ChangeUserEnabled is used to enable/disable the user identified by id. | ||
| ChangeUserEnabled(id uint, isEnabled bool) (*User, error) |
There was a problem hiding this comment.
in addition to implementing these methods on service , implement them with the loggingMiddleware and metricsMiddleware types in the service package.
groob
left a comment
There was a problem hiding this comment.
Address the review comment regarding 1 line spacing above documented methods. the rest looks good.
The ability to modify a users admin and enabled status was erroneously left in place during development of #959. To mitigate a privilege escalation vulnerability we need to ensure those values can only be modified through the explicit methods. This patch includes a unit test and fix for the vulnerability. Thanks to 'Quikke' for submitting this vulnerability.
No description provided.