-
Notifications
You must be signed in to change notification settings - Fork 432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support setting role in add-member API requests #9190
Conversation
4d6aa54
to
d713620
Compare
""" | ||
Add `userid` to `group` with `roles` and return the resulting membership. | ||
|
||
If `roles=None` it will default to `[GroupMembershipRoles.MEMBER]`. | ||
|
||
If a membership matching `group`, `userid` and `roles` already exists | ||
in the DB it will just be returned. | ||
|
||
:raise ConflictError: if a membership already exists with the given | ||
group and userid but different roles | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously if you called this method and a matching membership already existed it would just return the existing membership. Now that I've added the roles
argument it introduces a new case:
It's now possible to call this method when a membership already exists with the matching group
and userid
but with different roles
. In this case we can't create a new membership. Nor do we want to update the existing membership (for example the API view that calls this method doesnt' want it to update existing memberships: a POST
API should never update an existing resource). So raise an exception in that case.
For backwards-compatibility it will still return a pre-existing membership if it has the same roles.
else: | ||
# This doesn't mean the membership will be created with no roles: | ||
# default roles will be applied by the service. | ||
roles = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For backwards-compatibility add-group-membership requests with no body are still allowed and they add memberships with the default role.
except ConflictError as err: | ||
raise HTTPConflict(str(err)) from err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return a 409 if there's an existing membership with different roles.
For backwards-compatibility if there's an existing membership with the same roles it'll still return 200 OK.
d713620
to
19731f3
Compare
user = self.user_fetcher(userid) | ||
|
||
existing_membership = self.get_membership(group, user) | ||
kwargs = {"roles": roles} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I get why we use this kwargs here vs a more explicit roles only check, are there any other membership attributes that might create a conflict coming soon?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this was just worrying about the future: I'm trying to code in such a way that, if we ever do add support for another attribute besides roles, that attribute will be taken into account when deciding whether to raise ConflictError
as well
2ebfb06
to
c649dc9
Compare
19731f3
to
d9632c0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 5 out of 5 changed files in this pull request and generated no suggestions.
Comments skipped due to low confidence (1)
h/services/group_members.py:140
- [nitpick] The error message could be more specific. Consider changing it to: 'The user is already a member of the group with different roles.'
raise ConflictError("The user is already a member of the group, with conflicting membership attributes")
Add support for an optional JSON body in add-member API requests that lets the user set the membership's role in the add-member API request. The add-group-membership API will now return a 409 Conflict if a conflicting membership (one with the same user and group but different roles) already exists in the DB: POST /api/groups/{groupid}/members/{userid} {"roles": ["owner"]} -> 200 OK POST /api/groups/{groupid}/members/{userid} {"roles": ["admin"]} -> 409 Conflict Previously this would have resulted in a 200 OK even though the membership's role in the DB would *not* have been updated with the new role from the second request. For backwards-compatibility it will still respond 200 OK if the subsequent request's role *does* match the existing role in the DB: POST /api/groups/{groupid}/members/{userid} {"roles": ["owner"]} -> 200 OK POST /api/groups/{groupid}/members/{userid} {"roles": ["owner"]} -> 200 OK This also works if requests are made with no JSON body (which creates memberships with the default role of "member"): POST /api/groups/{groupid}/members/{userid} (no body content) -> 200 OK POST /api/groups/{groupid}/members/{userid} (no body content) -> 200 OK
d9632c0
to
2b30f91
Compare
Fixes #9140.
Add support for an optional JSON body in add-membership API requests that lets the user set the membership's role in the add-member API request itself (rather than having to add the membership and then call the edit-membership API to change the role).
For backwards compatibility if the add-membership API is called without a body it still adds a member with the plain
"member"
role, as currently.No updated API docs in this request since the backwards-compat means this is a non-breaking change. I'll send a separate PR later to update the docs for several membership APIs that've had new features added recently.
Note that the add-member API can currently only be called with an authclient. It can't be called as a user. Auth clients have permission to add any user to any group (within the auth client's authority) and with any role.
Testing
Log in as
devdata_admin
Go to http://localhost:5000/groups/new and create a new group
Go to http://localhost:5000/admin/oauthclients/new and create an authclient with these properties:
Authority: localhost
Grant type: client_credentials
Trusted: yes
Use the authclient to authenticate an API request to add
devdata_user
to the group. With no JSON body it'll create a plain"member"
role:Create an API key and use it to delete the membership so we can recreate it again:
Now make the
POST
request again with a JSON body and it'll add the membership with the role from the JSON body:You can also trying posting an invalid role and you'll get an error message.
You can repeatedly make the same request (with or without a JSON body) and it'll keep on responding 200 OK and just returning the existing membership. But if you make a request with different roles it'll respond 409 Conflict.