Skip to content

Commit

Permalink
Allow passing additional parameters to the @users endpoint when delet…
Browse files Browse the repository at this point in the history
…ing a user (#1599)

* add an option to pass additional parameters to the delete user endpoint

* tests and docs

* black

* changelog

* remove unneeded commits

---------

Co-authored-by: Timo Stollenwerk <tisto@users.noreply.github.com>
  • Loading branch information
erral and tisto committed Aug 23, 2023
1 parent 731554b commit 48d2569
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 23 deletions.
48 changes: 41 additions & 7 deletions docs/source/endpoints/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ myst:
Available users in a Plone site can be created, queried, updated, and deleted by interacting with the `/@users` endpoint on portal root.
This action requires an authenticated user:


## List Users

To retrieve a list of all current users in the portal, call the `/@users` endpoint with a `GET` request:
Expand Down Expand Up @@ -104,7 +103,6 @@ The server will respond with a list of users where the `fullname`, `email` or `i
:language: http
```


## Create User

To create a new user, send a `POST` request to the global `/@users` endpoint with a JSON representation of the user you want to create in the body:
Expand Down Expand Up @@ -187,7 +185,6 @@ In this case, the server will respond with a {term}`200 OK` status code and the
:language: http
```


## Update User

To update the settings of a user, send a `PATCH` request with the user details you want to amend to the URL of that particular user.
Expand Down Expand Up @@ -232,7 +229,6 @@ If you still want Plone to take care of image scaling using the default Plone be
:request: ../../../src/plone/restapi/tests/http-examples/users_update_portrait_scale.req
```


## Delete User

To delete a user, send a `DELETE` request to the `/@users` endpoint and append the user ID of the user you want to delete.
Expand All @@ -249,6 +245,47 @@ A successful response will be indicated by a {term}`204 No Content` response:
:language: http
```

When deleting a user in large sites with a lot of users and content the deleting operation may take a lot of time to the extent of setting it in an unresponsive state.

There is a workaround about this which is to request Plone not to delete the Member areas or the local roles that may have been granted in the past.

To mark such a behavior we need to pass specific parameters to the delete endpoint.

In this case we request not to delete the local roles:

```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../../src/plone/restapi/tests/http-examples/users_delete_no_localroles.req
```

A successful response will be indicated by a {term}`204 No Content` response:

```{literalinclude} ../../../src/plone/restapi/tests/http-examples/users_delete_no_localroles.resp
:language: http
```

In this case we request not to delete the member areas:

```{eval-rst}
.. http:example:: curl httpie python-requests
:request: ../../../src/plone/restapi/tests/http-examples/users_delete_no_memberareas.req
```

A successful response will be indicated by a {term}`204 No Content` response:

```{literalinclude} ../../../src/plone/restapi/tests/http-examples/users_delete_no_memberareas.resp
:language: http
```

Both parameters can be added in the same request.

```{warning}
These two specific requests should be made with special care because they may leave traces of the deleted users in the Plone database.
Specifically, although the user is deleted the reference of its permissions stay on the database.
This means that if in the future you add a new user with the same userid, it may get the local roles and member area previously created for the old user.
```

## User registration

Expand All @@ -272,7 +309,6 @@ If the user has been created, the server will respond with a {term}`201 Created`
:language: http
```


## Reset User Password

Plone allows to reset a password for a user by sending a `POST` request to the user resource and appending `/reset-password` to the URL:
Expand All @@ -292,7 +328,6 @@ The token that is part of the reset URL in the email can be used to authorize se
:request: ../../../src/plone/restapi/tests/http-examples/users_reset.req
```


### Reset Own Password

Plone also allows a user to reset her own password directly without sending an email.
Expand All @@ -316,7 +351,6 @@ To set the password with the old password, you need either the `Set own password

If an API consumer tries to send a password in the payload that is not the same as the currently logged in user, the server will respond with a {term}`400 Bad Request` response.


### Return Values

- {term}`200 OK`
Expand Down
2 changes: 2 additions & 0 deletions news/1598.feat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Allow passing additional parameters to the delete users endpoint to request not to delete local roles and memberareas
[erral]
18 changes: 17 additions & 1 deletion src/plone/restapi/services/users/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
from zope.publisher.interfaces import IPublishTraverse


FALSE_VALUES = (0, "0", False, "false", "no")


@implementer(IPublishTraverse)
class UsersDelete(Service):
"""Deletes a user."""
Expand All @@ -27,7 +30,20 @@ def _get_user_id(self):
def reply(self):
portal = getSite()
portal_membership = getToolByName(portal, "portal_membership")
delete_successful = portal_membership.deleteMembers((self._get_user_id,))

delete_memberareas = (
self.request.get("delete_memberareas", True) not in FALSE_VALUES
)

delete_localroles = (
self.request.get("delete_localroles", True) not in FALSE_VALUES
)

delete_successful = portal_membership.deleteMembers(
(self._get_user_id,),
delete_memberareas=delete_memberareas,
delete_localroles=delete_localroles,
)
if delete_successful:
return self.reply_no_content()
else:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DELETE /plone/@users/noam HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Content-Type: application/x-www-form-urlencoded

delete_localroles=0
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
HTTP/1.1 204 No Content

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
DELETE /plone/@users/noam HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
Content-Type: application/x-www-form-urlencoded

delete_memberareas=0
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
HTTP/1.1 204 No Content

42 changes: 42 additions & 0 deletions src/plone/restapi/tests/test_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,48 @@ def test_documentation_users_delete(self):
response = self.api_session.delete("/@users/noam")
save_request_and_response_for_docs("users_delete", response)

def test_documentation_users_delete_no_localroles(self):
properties = {
"email": "noam.chomsky@example.com",
"username": "noamchomsky",
"fullname": "Noam Avram Chomsky",
"home_page": "web.mit.edu/chomsky",
"description": "Professor of Linguistics",
"location": "Cambridge, MA",
}
api.user.create(
email="noam.chomsky@example.com",
username="noam",
properties=properties,
)
transaction.commit()

response = self.api_session.delete(
"/@users/noam", data={"delete_localroles": 0}
)
save_request_and_response_for_docs("users_delete_no_localroles", response)

def test_documentation_users_delete_no_memberareas(self):
properties = {
"email": "noam.chomsky@example.com",
"username": "noamchomsky",
"fullname": "Noam Avram Chomsky",
"home_page": "web.mit.edu/chomsky",
"description": "Professor of Linguistics",
"location": "Cambridge, MA",
}
api.user.create(
email="noam.chomsky@example.com",
username="noam",
properties=properties,
)
transaction.commit()

response = self.api_session.delete(
"/@users/noam", data={"delete_memberareas": 0}
)
save_request_and_response_for_docs("users_delete_no_memberareas", response)

def test_documentation_groups(self):
gtool = api.portal.get_tool("portal_groups")
properties = {
Expand Down

0 comments on commit 48d2569

Please sign in to comment.