You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
draft: add endpoints to create and modify users - #38894
This repository is currently maintained by @openedx/wg-maintenance-openedx-platform-oncall.
Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.
🔘 Get product approval
If you haven't already, check this list to see if your contribution needs to go through the product review process.
If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
This process (including the steps you'll need to take) is documented here.
If it doesn't, simply proceed with the next step.
🔘 Provide context
To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
Dependencies
This PR must be merged before / after / at the same time as ...
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a new admin-only “modify user” API endpoint in user_api to support creating users (POST) and updating user attributes (PATCH) via /api/user/v1/modify.
Changes:
Added UserModifyView with JWT/Bearer/Session authentication and IsAdminUser permission, implementing POST (create) and PATCH (update).
Wired the new endpoint into user_api URL routing.
Added initial HTTP-level tests covering user creation success and common validation failures.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.
File
Description
openedx/core/djangoapps/user_api/views.py
Adds UserModifyView implementing POST/PATCH behavior for user creation and updates.
A UserProfile is only created when at least one profile field is supplied. Since ordinary User creation does not automatically create a profile in this codebase, a valid request containing only account fields leaves user.profile missing; a later PATCH of name or another profile field then raises RelatedObjectDoesNotExist instead of updating the user. Always create the profile here.
Create normalizes passwords before hashing, but update hashes the unnormalized value. For passwords whose NFKC form differs, login normalizes the entered password while the stored hash was made from the raw value, so the new password cannot authenticate consistently. Normalize before set_password here as well.
additionalProperties: true contradicts the endpoint implementation, which rejects every field not in UserProfileSerializer.Meta.fields with a 400 response. Clients generated from this schema may send arbitrary fields believing they are supported; set this to false (and make the same correction in the PATCH schema below).
Missing PATCH lookup identifier returns 404 instead of 400
openedx/core/djangoapps/user_api/views.py:318
username_or_email is optional here, so a PATCH body without the required lookup field passes None to get_user_by_username_or_email and is reported as a 404 User not found. The OpenAPI schema marks this field as required and the request is invalid rather than a missing resource; reject the missing/blank identifier with a 400 validation error before performing the lookup.
validate_password is called without the existing user, so PATCH password changes skip Django's UserAttributeSimilarityValidator checks against the target user's username/email. This lets an admin set a password equal to the account identifier even though the repository's password policy enables that validator; pass user=self.instance when validating updates.
Create missing profile before applying profile updates
Not every existing User is guaranteed to have a related profile (the codebase explicitly handles users without one), so PATCHing any profile field for such a user raises UserProfile.DoesNotExist and becomes a 500. Create or fetch the profile before applying profile_data so this endpoint can modify all existing accounts.
Read QueryDict identifier value before removing it
openedx/core/djangoapps/user_api/views.py:306
When form-encoded data is used, request.data is a QueryDict and QueryDict.pop() returns a list. The list is passed to get_user_by_username_or_email, so the normal form-encoded PATCH request can fail to find the user or raise instead of updating it. Read the identifier with request.data.get(...) before removing it from the copied payload.
The reason will be displayed to describe this comment to others. Learn more.
@viadanna I think in general this works well, and I was able to test this locally. Please see some inline nits though, mostly around docs and the serialiser. :)
Please see some questions inline, but nothing blocking from my end. Feel free to resolve the review threads from my last review; you've addressed them, but github doesn't let me resolve them.
I tested this: I didn't retest this functionally since I last reviewed, but the changes look good to me.
The reason will be displayed to describe this comment to others. Learn more.
@viadanna nice :) I'm not sure about the new logic though. Wouldn't we be checking for is_setting_superuser_flag = "is_superuser" in serializer.validated_data? I might be misinterpreting. Would you mind adding a comment about the purpose of the logic here?
The reason will be displayed to describe this comment to others. Learn more.
@viadanna I think we should, to avoid too much duplicated validation logic. But I'd be curious what upstream think of this. Perhaps this is useful to have user friendly error messages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This PR adds endpoints to create and modify users.
Supporting information
Private Refs:
Testing instructions
Deadline
None, still a draft