This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 76d999ef8a2a9676430e51b892b9ab9898e0709f Author: Benoit TELLIER <[email protected]> AuthorDate: Wed Mar 18 10:59:23 2026 +0100 JAMES-4185 Allow working with the vault of deleted users --- .../modules/servers/partials/operate/webadmin.adoc | 28 ++++++++++++----- .../vault/routes/DeletedMessagesVaultRoutes.java | 14 +++++---- .../routes/DeletedMessagesVaultRoutesTest.java | 33 ++++++++++++++++++++ src/site/markdown/server/manage-webadmin.md | 35 ++++++++++++++-------- 4 files changed, 84 insertions(+), 26 deletions(-) diff --git a/docs/modules/servers/partials/operate/webadmin.adoc b/docs/modules/servers/partials/operate/webadmin.adoc index 11b1f955fa..a707c3c20e 100644 --- a/docs/modules/servers/partials/operate/webadmin.adoc +++ b/docs/modules/servers/partials/operate/webadmin.adoc @@ -4603,7 +4603,7 @@ configure the DeletedMessageVault PreDeletionHook. Deleted messages of a specific user can be listed by calling the following endpoint: .... -curl -XPOST http://ip:port/deletedMessages/users/[email protected]/messages +curl -XPOST http://ip:port/deletedMessages/users/[email protected]/messages[?force=true] { "combinator": "and", @@ -4642,6 +4642,9 @@ Pass an empty criteria list to retrieve all deleted messages: } .... +*Note*: The optional `force` query parameter (`?force=true`) bypasses the user existence check. +This is useful for browsing the vault of a deleted user or a virtual user. + Response code: * 200: List of deleted messages matching the query @@ -4650,7 +4653,7 @@ Response code: ** can not parse the JSON body ** Json query object contains unsupported operator, fieldName ** Json query object values violate parsing rules -* 404: User not found +* 404: User not found (bypassed when `force=true`) Response body is a JSON array: @@ -4681,7 +4684,7 @@ Deleted messages of a specific user can be restored by calling the following endpoint: .... -curl -XPOST http://ip:port/deletedMessages/users/[email protected]?action=restore +curl -XPOST http://ip:port/deletedMessages/users/[email protected]?action=restore[&force=true] { "combinator": "and", @@ -4830,6 +4833,9 @@ comparators to apply on the String data of other special locales stored in the Vault. More details at https://issues.apache.org/jira/browse/MAILBOX-384[JIRA] +*Note*: The optional `force` query parameter (`?force=true` or `?action=restore&force=true`) bypasses +the user existence check. This is useful for restoring the vault of a deleted user or a virtual user. + Response code: * 201: Task for restoring deleted has been created @@ -4840,7 +4846,7 @@ Response code: ** can not parse the JSON body ** Json query object contains unsupported operator, fieldName ** Json query object values violate parsing rules -* 404: User not found +* 404: User not found (bypassed when `force=true`) link:#_endpoints_returning_a_task[More details about endpoints returning a task]. @@ -4868,7 +4874,7 @@ Retrieve deleted messages matched with requested query from an user then share the content to a targeted mail address (exportTo) .... -curl -XPOST 'http://ip:port/deletedMessages/users/[email protected]?action=export&[email protected]' +curl -XPOST 'http://ip:port/deletedMessages/users/[email protected]?action=export&[email protected][&force=true]' BODY: is the json query has the same structure with Restore Deleted Messages section .... @@ -4877,6 +4883,9 @@ BODY: is the json query has the same structure with Restore Deleted Messages sec restrictions like in link:#_restore_deleted_messages[Restore Deleted Messages] +*Note*: The optional `force` query parameter (`&force=true`) bypasses the user existence check. +This is useful for exporting the vault of a deleted user or a virtual user. + Response code: * 201: Task for exporting has been created @@ -4889,7 +4898,7 @@ Response code: ** can not parse the JSON body ** Json query object contains unsupported operator, fieldName ** Json query object values violate parsing rules -* 404: User not found +* 404: User not found (bypassed when `force=true`) link:#_endpoints_returning_a_task[More details about endpoints returning a task]. @@ -4942,19 +4951,22 @@ You may want to call this endpoint on a regular basis. Delete a Deleted Message with `MessageId` .... -curl -XDELETE http://ip:port/deletedMessages/users/[email protected]/messages/3294a976-ce63-491e-bd52-1b6f465ed7a2 +curl -XDELETE http://ip:port/deletedMessages/users/[email protected]/messages/3294a976-ce63-491e-bd52-1b6f465ed7a2[?force=true] .... link:#_endpoints_returning_a_task[More details about endpoints returning a task]. +*Note*: The optional `force` query parameter (`?force=true`) bypasses the user existence check. +This is useful for removing a message from the vault of a deleted user or a virtual user. + Response code: * 201: Task for deleting message has been created * 400: Bad request: ** user parameter is invalid ** messageId parameter is invalid -* 404: User not found +* 404: User not found (bypassed when `force=true`) The scheduled task will have the following type `deleted-messages-delete` and the following `additionalInformation`: diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java index deb7955b15..c3637313fe 100644 --- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java @@ -70,6 +70,7 @@ public class DeletedMessagesVaultRoutes implements Routes { private static final String DELETE_PATH = MESSAGES_PATH + SEPARATOR + MESSAGE_ID_PARAM; private static final String SCOPE_QUERY_PARAM = "scope"; private static final String EXPORT_TO_QUERY_PARAM = "exportTo"; + private static final String FORCE_QUERY_PARAM = "force"; private final RestoreService vaultRestore; private final ExportService vaultExport; @@ -121,13 +122,13 @@ public class DeletedMessagesVaultRoutes implements Routes { private Task export(Request request) throws JsonExtractException { Username username = extractUser(request); - validateUserExist(username); + validateUserExist(request, username); return new DeletedMessagesVaultExportTask(vaultExport, username, extractQuery(request), extractMailAddress(request)); } private Object browseMessages(Request request, spark.Response response) throws JsonExtractException { Username username = extractUser(request); - validateUserExist(username); + validateUserExist(request, username); Query query = extractQuery(request); return Flux.from(deletedMessageVault.search(username, query)) .map(DeletedMessageDTO::from) @@ -137,7 +138,7 @@ public class DeletedMessagesVaultRoutes implements Routes { private Task restore(Request request) throws JsonExtractException { Username username = extractUser(request); - validateUserExist(username); + validateUserExist(request, username); return new DeletedMessagesVaultRestoreTask(vaultRestore, username, extractQuery(request)); } @@ -150,13 +151,16 @@ public class DeletedMessagesVaultRoutes implements Routes { private Task deleteMessage(Request request) { Username username = extractUser(request); - validateUserExist(username); + validateUserExist(request, username); MessageId messageId = parseMessageId(request); return new DeletedMessagesVaultDeleteTask(deletedMessageVault, username, messageId); } - private void validateUserExist(Username username) { + private void validateUserExist(Request request, Username username) { + if (Boolean.parseBoolean(request.queryParams(FORCE_QUERY_PARAM))) { + return; + } try { if (!usersRepository.contains(username)) { throw ErrorResponder.builder() diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java index 55c17a7f04..269f9d76da 100644 --- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java +++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java @@ -397,6 +397,19 @@ class DeletedMessagesVaultRoutesTest { .body("message", is(notNullValue())); } + @Test + void restoreShouldBypassUserExistenceCheckWhenForceIsTrue() { + given() + .queryParam("action", "restore") + .queryParam("force", "true") + .body(MATCH_ALL_QUERY) + .when() + .post(USERS + SEPARATOR + USERNAME_2.asString()) + .then() + .statusCode(HttpStatus.CREATED_201) + .body("taskId", is(notNullValue())); + } + @ParameterizedTest @ValueSource(strings = {"restore", "export"}) void userVaultAPIShouldReturnNotFoundWhenNoUserPathParameter(String action) { @@ -2308,6 +2321,15 @@ class DeletedMessagesVaultRoutesTest { .body("message", is(notNullValue())); } + @Test + void deleteShouldBypassUserExistenceCheckWhenForceIsTrue() { + when() + .delete(USERS + SEPARATOR + USERNAME_2.asString() + SEPARATOR + DELETED_MESSAGE_PARAM_PATH + "?force=true") + .then() + .statusCode(HttpStatus.CREATED_201) + .body("taskId", is(notNullValue())); + } + @Test void deleteShouldReturnInvalidWhenMessageIdIsInvalid() { when() @@ -2380,6 +2402,17 @@ class DeletedMessagesVaultRoutesTest { .statusCode(HttpStatus.NOT_FOUND_404); } + @Test + void browseMessagesShouldBypassUserExistenceCheckWhenForceIsTrue() { + given() + .queryParam("force", "true") + .body(MATCH_ALL_QUERY) + .when() + .post(USERS + SEPARATOR + "[email protected]" + SEPARATOR + MESSAGE_PATH_PARAM) + .then() + .statusCode(HttpStatus.OK_200); + } + @Test void browseMessagesShouldReturn400WhenQueryBodyIsInvalid() { given() diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md index be6e2f3d51..ed32669692 100644 --- a/src/site/markdown/server/manage-webadmin.md +++ b/src/site/markdown/server/manage-webadmin.md @@ -4682,7 +4682,7 @@ Here are the following actions available on the 'Deleted Messages Vault' Deleted messages of a specific user can be restored by calling the following endpoint: ``` -curl -XPOST http://ip:port/deletedMessages/users/[email protected]?action=restore +curl -XPOST http://ip:port/deletedMessages/users/[email protected]?action=restore[&force=true] { "combinator": "and", @@ -4794,20 +4794,23 @@ Messages in the Deleted Messages Vault of a specified user that are matched with } ``` -**Warning**: Current web-admin uses `US` locale as the default. Therefore, there might be some conflicts when using String `containsIgnoreCase` comparators to apply -on the String data of other special locales stored in the Vault. More details at [JIRA](https://issues.apache.org/jira/browse/MAILBOX-384) +**Warning**: Current web-admin uses `US` locale as the default. Therefore, there might be some conflicts when using String `containsIgnoreCase` comparators to apply +on the String data of other special locales stored in the Vault. More details at [JIRA](https://issues.apache.org/jira/browse/MAILBOX-384) + +**Note**: The optional `force` query parameter (`&force=true`) bypasses the user existence check. +This is useful for restoring the vault of a deleted user or a virtual user. Response code: - 201: Task for restoring deleted has been created - - 400: Bad request: + - 400: Bad request: - action query param is not present - action query param is not a valid action - user parameter is invalid - can not parse the JSON body - Json query object contains unsupported operator, fieldName - - Json query object values violate parsing rules - - 404: User not found + - Json query object values violate parsing rules + - 404: User not found (bypassed when `force=true`) [More details about endpoints returning a task](#Endpoints_returning_a_task). @@ -4832,16 +4835,19 @@ while: Retrieve deleted messages matched with requested query from an user then share the content to a targeted mail address (exportTo) ``` -curl -XPOST 'http://ip:port/deletedMessages/users/[email protected]?action=export&[email protected]' +curl -XPOST 'http://ip:port/deletedMessages/users/[email protected]?action=export&[email protected][&force=true]' BODY: is the json query has the same structure with Restore Deleted Messages section ``` **Note**: Json query passing into the body follows the same rules & restrictions like in [Restore Deleted Messages](#Restore_deleted_messages) +**Note**: The optional `force` query parameter (`&force=true`) bypasses the user existence check. +This is useful for exporting the vault of a deleted user or a virtual user. + Response code: - 201: Task for exporting has been created - - 400: Bad request: + - 400: Bad request: - exportTo query param is not present - exportTo query param is not a valid mail address - action query param is not present @@ -4849,8 +4855,8 @@ Response code: - user parameter is invalid - can not parse the JSON body - Json query object contains unsupported operator, fieldName - - Json query object values violate parsing rules - - 404: User not found + - Json query object values violate parsing rules + - 404: User not found (bypassed when `force=true`) [More details about endpoints returning a task](#Endpoints_returning_a_task). @@ -4895,18 +4901,21 @@ You may want to call this endpoint on a regular basis. Delete a Deleted Message with `MessageId` ``` -curl -XDELETE http://ip:port/deletedMessages/users/[email protected]/messages/3294a976-ce63-491e-bd52-1b6f465ed7a2 +curl -XDELETE http://ip:port/deletedMessages/users/[email protected]/messages/3294a976-ce63-491e-bd52-1b6f465ed7a2[?force=true] ``` [More details about endpoints returning a task](#Endpoints_returning_a_task). +**Note**: The optional `force` query parameter (`?force=true`) bypasses the user existence check. +This is useful for removing a message from the vault of a deleted user or a virtual user. + Response code: - 201: Task for deleting message has been created - - 400: Bad request: + - 400: Bad request: - user parameter is invalid - messageId parameter is invalid - - 404: User not found + - 404: User not found (bypassed when `force=true`) The scheduled task will have the following type `deleted-messages-delete` and the following `additionalInformation`: --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
