You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/02/14 06:02:10 UTC

[james-project] 05/05: JAMES-3040: change behavior of uploadDocument directly to userTargetSpace

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 2422d3ef92a6c74e9da233039613d38fd833e632
Author: duc91 <du...@gmail.com>
AuthorDate: Wed Feb 5 17:19:32 2020 +0700

    JAMES-3040: change behavior of uploadDocument directly to userTargetSpace
---
 CHANGELOG.md                                       |   1 +
 .../destination/conf/blob.properties               |   9 +-
 .../destination/conf/blob.properties               |   9 +-
 .../destination/conf/blob.properties               |   9 +-
 .../cassandra/destination/conf/blob.properties     |   9 +-
 .../james/modules/LinshareGuiceExtension.java      |  34 +++-
 ...LinshareBlobExportMechanismIntegrationTest.java |   4 +-
 ...LinshareBlobExportMechanismIntegrationTest.java |   5 +-
 ...LinshareBlobExportMechanismIntegrationTest.java |  98 ++----------
 .../james/webadmin/vault/routes/ExportService.java |   2 +-
 .../linshare/LinshareBlobExportMechanism.java      |  22 ++-
 .../james/linshare/LinshareConfiguration.java      |  60 ++++---
 .../apache/james/linshare/client/LinshareAPI.java  |  60 ++-----
 .../linshare/LinshareBlobExportMechanismTest.java  |  64 ++------
 .../james/linshare/LinshareConfigurationTest.java  |  92 +++++++++--
 .../apache/james/linshare/LinshareExtension.java   | 167 +++++++++++++------
 .../org/apache/james/linshare/LinshareTest.java    |   4 +-
 .../james/linshare/client/LinshareAPITest.java     | 176 +++++----------------
 .../client/TechnicalAccountCreationRequest.java    |   1 -
 upgrade-instructions.md                            |  21 +++
 20 files changed, 402 insertions(+), 445 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 205ce27..9ad4ae1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ of tasks being currently executed.
 - Unhealthy health checks now return HTTP 503 instead of 500, degraded now returns 200 instead of 500. See JAMES-2576.
 - In order to fasten JMAP-draft message retrieval upon calls on properties expected to be fast to fetch, we now compute the preview and hasAttachment properties asynchronously and persist them in Cassandra to improve performance. See JAMES-2919.
 - It is now forbidden to create new Usernames with the following set of characters in its local part : `"(),:; <>@\[]`, as we prefer it to stay simple to handle. However, the read of Usernames already existing with some of those characters is still allowed, to not introduce any breaking change. See JAMES-2950.
+- Linshare blob export configuration and mechanism change. See JAMES-3040.
 
 ### Fixed
 - JAMES-2828 & JAMES-2929 bugs affecting JDBCMailRepository usage with PostgresSQL thanks to Jörg Thomas & Sergey B
diff --git a/dockerfiles/run/guice/cassandra-ldap/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra-ldap/destination/conf/blob.properties
index 4600510..90c5135 100644
--- a/dockerfiles/run/guice/cassandra-ldap/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra-ldap/destination/conf/blob.properties
@@ -18,6 +18,9 @@ blob.export.localFile.directory=file://var/blobExporting
 # Mandatory if you choose LinShare, url to connect to LinShare service
 # blob.export.linshare.url=http://linshare:8080
 
-# Mandatory if you choose LinShare, access token to connect to LinShare service. It will be formalized to `Bearer` + a space + access token
-# So, no need to pass (`Bearer` + <space>) prefix
-# blob.export.linshare.token=LinShare-Access-Token-In-String
\ No newline at end of file
+# ======================================= LinShare Configuration BasicAuthentication ===================================
+# Authentication is mandatory if you choose LinShare, TechnicalAccount is need to connect to LinShare specific service.
+# For Example: It will be formalized to 'Authorization: Basic {Credential of UUID/password}'
+
+# blob.export.linshare.technical.account.uuid=Technical_Account_UUID
+# blob.export.linshare.technical.account.password=password
\ No newline at end of file
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
index ea41c7f..d7509a5 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
@@ -131,6 +131,9 @@ blob.export.localFile.directory=file://var/blobExporting
 # Mandatory if you choose LinShare, url to connect to LinShare service
 # blob.export.linshare.url=http://linshare:8080
 
-# Mandatory if you choose LinShare, access token to connect to LinShare service. It will be formalized to `Bearer` + a space + access token
-# So, no need to pass (`Bearer` + <space>) prefix
-# blob.export.linshare.token=LinShare-Access-Token-In-String
+# ======================================= LinShare Configuration BasicAuthentication ===================================
+# Authentication is mandatory if you choose LinShare, TechnicalAccount is need to connect to LinShare specific service.
+# For Example: It will be formalized to 'Authorization: Basic {Credential of UUID/password}'
+
+# blob.export.linshare.technical.account.uuid=Technical_Account_UUID
+# blob.export.linshare.technical.account.password=password
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
index ea41c7f..d7509a5 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
@@ -131,6 +131,9 @@ blob.export.localFile.directory=file://var/blobExporting
 # Mandatory if you choose LinShare, url to connect to LinShare service
 # blob.export.linshare.url=http://linshare:8080
 
-# Mandatory if you choose LinShare, access token to connect to LinShare service. It will be formalized to `Bearer` + a space + access token
-# So, no need to pass (`Bearer` + <space>) prefix
-# blob.export.linshare.token=LinShare-Access-Token-In-String
+# ======================================= LinShare Configuration BasicAuthentication ===================================
+# Authentication is mandatory if you choose LinShare, TechnicalAccount is need to connect to LinShare specific service.
+# For Example: It will be formalized to 'Authorization: Basic {Credential of UUID/password}'
+
+# blob.export.linshare.technical.account.uuid=Technical_Account_UUID
+# blob.export.linshare.technical.account.password=password
diff --git a/dockerfiles/run/guice/cassandra/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra/destination/conf/blob.properties
index 4600510..90c5135 100644
--- a/dockerfiles/run/guice/cassandra/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra/destination/conf/blob.properties
@@ -18,6 +18,9 @@ blob.export.localFile.directory=file://var/blobExporting
 # Mandatory if you choose LinShare, url to connect to LinShare service
 # blob.export.linshare.url=http://linshare:8080
 
-# Mandatory if you choose LinShare, access token to connect to LinShare service. It will be formalized to `Bearer` + a space + access token
-# So, no need to pass (`Bearer` + <space>) prefix
-# blob.export.linshare.token=LinShare-Access-Token-In-String
\ No newline at end of file
+# ======================================= LinShare Configuration BasicAuthentication ===================================
+# Authentication is mandatory if you choose LinShare, TechnicalAccount is need to connect to LinShare specific service.
+# For Example: It will be formalized to 'Authorization: Basic {Credential of UUID/password}'
+
+# blob.export.linshare.technical.account.uuid=Technical_Account_UUID
+# blob.export.linshare.technical.account.password=password
\ No newline at end of file
diff --git a/server/container/guice/blob-export-guice/src/test/java/org/apache/james/modules/LinshareGuiceExtension.java b/server/container/guice/blob-export-guice/src/test/java/org/apache/james/modules/LinshareGuiceExtension.java
index 856e0fb..8a21402 100644
--- a/server/container/guice/blob-export-guice/src/test/java/org/apache/james/modules/LinshareGuiceExtension.java
+++ b/server/container/guice/blob-export-guice/src/test/java/org/apache/james/modules/LinshareGuiceExtension.java
@@ -24,11 +24,15 @@ import org.apache.james.linshare.LinshareConfiguration;
 import org.apache.james.linshare.LinshareExtension;
 import org.apache.james.linshare.LinshareFixture;
 import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
 
 import com.google.inject.Module;
 import com.google.inject.util.Modules;
 
-public class LinshareGuiceExtension implements GuiceModuleTestExtension {
+public class LinshareGuiceExtension implements GuiceModuleTestExtension, ParameterResolver {
+
     private final LinshareExtension linshareExtension;
 
     public LinshareGuiceExtension() {
@@ -36,14 +40,18 @@ public class LinshareGuiceExtension implements GuiceModuleTestExtension {
     }
 
     @Override
-    public void beforeEach(ExtensionContext extensionContext) throws Exception {
+    public void beforeAll(ExtensionContext extensionContext) {
+        linshareExtension.beforeAll(extensionContext);
+    }
+
+    @Override
+    public void beforeEach(ExtensionContext extensionContext) {
         linshareExtension.beforeEach(extensionContext);
     }
 
     @Override
-    public void afterAll(ExtensionContext extensionContext) throws Exception {
-        linshareExtension.getLinshare()
-            .stop();
+    public void afterAll(ExtensionContext extensionContext) {
+        linshareExtension.getLinshare().stop();
     }
 
     @Override
@@ -54,7 +62,11 @@ public class LinshareGuiceExtension implements GuiceModuleTestExtension {
             binder -> {
                 try {
                     binder.bind(LinshareConfiguration.class)
-                        .toInstance(linshareExtension.configurationWithJwtFor(LinshareFixture.USER_1));
+                        .toInstance(linshareExtension.configurationWithBasicAuthFor(
+                            new LinshareFixture.Credential(
+                                linshareExtension.getTechnicalAccountUUID().toString(),
+                                LinshareFixture.TECHNICAL_ACCOUNT.getPassword()))
+                        );
                 } catch (Exception e) {
                     throw new RuntimeException(e);
                 }
@@ -62,7 +74,13 @@ public class LinshareGuiceExtension implements GuiceModuleTestExtension {
         );
     }
 
-    public LinshareExtension getLinshareJunitExtension() {
-        return linshareExtension;
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return linshareExtension.supportsParameter(parameterContext, extensionContext);
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return linshareExtension.resolveParameter(parameterContext, extensionContext);
     }
 }
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
index e7fd6db..75b8d77 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
@@ -31,6 +31,7 @@ import org.apache.james.backends.rabbitmq.DockerRabbitMQSingleton;
 import org.apache.james.mailbox.extractor.TextExtractor;
 import org.apache.james.mailbox.store.search.PDFTextExtractor;
 import org.apache.james.modules.AwsS3BlobStoreExtension;
+import org.apache.james.modules.LinshareGuiceExtension;
 import org.apache.james.modules.RabbitMQExtension;
 import org.apache.james.modules.TestJMAPServerModule;
 import org.apache.james.modules.TestRabbitMQModule;
@@ -40,14 +41,13 @@ import org.apache.james.webadmin.integration.vault.LinshareBlobExportMechanismIn
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class RabbitMQLinshareBlobExportMechanismIntegrationTest extends LinshareBlobExportMechanismIntegrationTest {
-
     @RegisterExtension
     static JamesServerExtension testExtension = new JamesServerBuilder()
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
         .extension(new RabbitMQExtension())
         .extension(new AwsS3BlobStoreExtension())
-        .extension(LinshareBlobExportMechanismIntegrationTest.linshareGuiceExtension)
+        .extension(new LinshareGuiceExtension())
         .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
             .combineWith(CassandraRabbitMQJamesServerMain.MODULES)
             .overrideWith(binder -> binder.bind(TextExtractor.class).to(PDFTextExtractor.class))
diff --git a/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/vault/MemoryLinshareBlobExportMechanismIntegrationTest.java b/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/vault/MemoryLinshareBlobExportMechanismIntegrationTest.java
index b6961e5..090944b 100644
--- a/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/vault/MemoryLinshareBlobExportMechanismIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/memory/vault/MemoryLinshareBlobExportMechanismIntegrationTest.java
@@ -31,12 +31,9 @@ import org.apache.james.webadmin.integration.vault.LinshareBlobExportMechanismIn
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class MemoryLinshareBlobExportMechanismIntegrationTest extends LinshareBlobExportMechanismIntegrationTest {
-
-    private static final LinshareGuiceExtension linshareGuiceExtension = new LinshareGuiceExtension();
-
     @RegisterExtension
     static JamesServerExtension jamesServerExtension = new JamesServerBuilder()
-        .extension(linshareGuiceExtension)
+        .extension(new LinshareGuiceExtension())
         .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
             .combineWith(MemoryJamesServerMain.IN_MEMORY_SERVER_AGGREGATE_MODULE)
             .overrideWith(TestJMAPServerModule.limitToTenMessages())
diff --git a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/vault/LinshareBlobExportMechanismIntegrationTest.java b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/vault/LinshareBlobExportMechanismIntegrationTest.java
index c55f60c..2fa6fec 100644
--- a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/vault/LinshareBlobExportMechanismIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/vault/LinshareBlobExportMechanismIntegrationTest.java
@@ -19,7 +19,6 @@
 
 package org.apache.james.webadmin.integration.vault;
 
-import static io.restassured.RestAssured.given;
 import static io.restassured.RestAssured.with;
 import static org.apache.james.jmap.HttpJmapAuthentication.authenticateJamesUser;
 import static org.apache.james.jmap.JMAPTestingConstants.ARGUMENTS;
@@ -31,14 +30,13 @@ import static org.apache.james.jmap.JmapCommonRequests.deleteMessages;
 import static org.apache.james.jmap.JmapCommonRequests.getOutboxId;
 import static org.apache.james.jmap.JmapCommonRequests.listMessageIdsForAccount;
 import static org.apache.james.jmap.LocalHostURIBuilder.baseUri;
+import static org.apache.james.linshare.LinshareExtension.LinshareAPIForTechnicalAccountTesting;
+import static org.apache.james.linshare.LinshareExtension.LinshareAPIForUserTesting;
 import static org.apache.james.linshare.LinshareFixture.MATCH_ALL_QUERY;
 import static org.apache.james.linshare.LinshareFixture.USER_1;
 import static org.apache.james.mailbox.backup.ZipAssert.assertThatZip;
 import static org.apache.james.webadmin.integration.vault.DeletedMessagesVaultRequests.exportVaultContent;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.hasSize;
 
 import java.io.ByteArrayInputStream;
 import java.util.List;
@@ -48,12 +46,10 @@ import org.apache.james.core.Username;
 import org.apache.james.jmap.AccessToken;
 import org.apache.james.jmap.draft.JmapGuiceProbe;
 import org.apache.james.linshare.client.Document;
-import org.apache.james.linshare.client.LinshareAPI;
 import org.apache.james.mailbox.DefaultMailboxes;
 import org.apache.james.mailbox.backup.ZipAssert;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.probe.MailboxProbe;
-import org.apache.james.modules.LinshareGuiceExtension;
 import org.apache.james.modules.MailboxProbeImpl;
 import org.apache.james.modules.protocols.ImapGuiceProbe;
 import org.apache.james.util.Port;
@@ -84,8 +80,6 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
         .exportTo(USER_1.getUsername())
         .query(MATCH_ALL_QUERY);
 
-    protected static final LinshareGuiceExtension linshareGuiceExtension = new LinshareGuiceExtension();
-
     @RegisterExtension
     IMAPMessageReader imapMessageReader = new IMAPMessageReader();
 
@@ -93,8 +87,7 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
     private AccessToken bartAccessToken;
     private GuiceJamesServer jmapServer;
     private RequestSpecification webAdminApi;
-    private RequestSpecification fakeSmtpRequestSpecification;
-    private LinshareAPI user1LinshareAPI;
+    private LinshareAPIForUserTesting user1LinshareAPI;
 
     @BeforeEach
     void setup(GuiceJamesServer jmapServer) throws Throwable {
@@ -119,10 +112,8 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
 
         homerAccessToken = authenticateJamesUser(baseUri(jmapPort), Username.of(HOMER), HOMER_PASSWORD);
         bartAccessToken = authenticateJamesUser(baseUri(jmapPort), Username.of(BART), BART_PASSWORD);
-        user1LinshareAPI = linshareGuiceExtension.getLinshareJunitExtension().getAPIFor(USER_1);
 
-        fakeSmtpRequestSpecification = given(linshareGuiceExtension.getLinshareJunitExtension()
-            .getLinshare().fakeSmtpRequestSpecification());
+        user1LinshareAPI = LinshareAPIForUserTesting.from(USER_1);
     }
 
     @Test
@@ -136,10 +127,9 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
 
         exportVaultContent(webAdminApi, EXPORT_ALL_HOMER_MESSAGES_TO_USER_1);
 
-        assertThat(user1LinshareAPI.receivedShares())
+        assertThat(user1LinshareAPI.listAllDocuments())
             .hasSize(1)
-            .allSatisfy(receivedShare -> assertThat(receivedShare.getDocument().getName()).endsWith(".zip"))
-            .allSatisfy(receivedShare -> assertThat(receivedShare.getSender().getMail()).isEqualTo(USER_1.getUsername()));
+            .allSatisfy(uploadedDoc -> assertThat(uploadedDoc.getName()).endsWith(".zip"));
     }
 
     @Test
@@ -158,67 +148,13 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
 
         exportVaultContent(webAdminApi, EXPORT_ALL_HOMER_MESSAGES_TO_USER_1);
 
-        assertThat(user1LinshareAPI.receivedShares())
+        assertThat(user1LinshareAPI.listAllDocuments())
             .hasSize(1)
-            .allSatisfy(receivedShare -> assertThat(receivedShare.getDocument().getName()).endsWith(".zip"))
-            .allSatisfy(receivedShare -> assertThat(receivedShare.getSender().getMail()).isEqualTo(USER_1.getUsername()));
-    }
-
-    @Test
-    void exportShouldSendAnEmailToShareeWhenJmapDelete() {
-        bartSendMessageToHomer();
-
-        WAIT_TEN_SECONDS.until(() -> listMessageIdsForAccount(homerAccessToken).size() == 1);
-
-        homerDeletesMessages(listMessageIdsForAccount(homerAccessToken));
-        WAIT_TEN_SECONDS.until(() -> listMessageIdsForAccount(homerAccessToken).isEmpty());
-
-        exportVaultContent(webAdminApi, EXPORT_ALL_HOMER_MESSAGES_TO_USER_1);
-
-        WAIT_TEN_SECONDS.untilAsserted(
-            () -> fakeSmtpRequestSpecification
-                .get("/api/email")
-            .then()
-                .body("", hasSize(2)));
-
-        fakeSmtpRequestSpecification
-            .get("/api/email")
-        .then()
-            .body("[1].subject", containsString("John Doe has shared a file with you"))
-            .body("[1].to", hasItem(USER_1.getUsername()));
-    }
-
-    @Test
-    void exportShouldSendAnEmailToShareeWhenImapDelete() throws Exception {
-        bartSendMessageToHomer();
-
-        WAIT_TEN_SECONDS.until(() -> listMessageIdsForAccount(homerAccessToken).size() == 1);
-
-        imapMessageReader.connect(LOCALHOST_IP, jmapServer.getProbe(ImapGuiceProbe.class).getImapPort())
-            .login(HOMER, HOMER_PASSWORD)
-            .select(IMAPMessageReader.INBOX)
-            .setFlagsForAllMessagesInMailbox("\\Deleted");
-        imapMessageReader.expunge();
-
-        WAIT_TEN_SECONDS.until(() -> listMessageIdsForAccount(homerAccessToken).isEmpty());
-
-        exportVaultContent(webAdminApi, EXPORT_ALL_HOMER_MESSAGES_TO_USER_1);
-
-        WAIT_TEN_SECONDS.untilAsserted(
-            () -> fakeSmtpRequestSpecification
-                .get("/api/email")
-            .then()
-                .body("", hasSize(2)));
-
-        fakeSmtpRequestSpecification
-            .get("/api/email")
-        .then()
-            .body("[1].subject", containsString("John Doe has shared a file with you"))
-            .body("[1].to", hasItem(USER_1.getUsername()));
+            .allSatisfy(uploadedDoc -> assertThat(uploadedDoc.getName()).endsWith(".zip"));
     }
 
     @Test
-    void exportShouldShareNonEmptyZipViaLinshareWhenJmapDelete() throws Exception {
+    void exportShouldShareNonEmptyZipViaLinshareWhenJmapDelete(LinshareAPIForTechnicalAccountTesting linshareAPIForTechnicalAccountTesting) throws Exception {
         bartSendMessageToHomer();
 
         WAIT_TEN_SECONDS.until(() -> listMessageIdsForAccount(homerAccessToken).size() == 1);
@@ -228,9 +164,9 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
 
         exportVaultContent(webAdminApi, EXPORT_ALL_HOMER_MESSAGES_TO_USER_1);
 
-        Document sharedDoc = user1LinshareAPI.receivedShares().get(0).getDocument();
-        byte[] sharedFile =  linshareGuiceExtension.getLinshareJunitExtension()
-            .downloadSharedFile(USER_1, sharedDoc.getId(), sharedDoc.getName());
+        Document sharedDoc = user1LinshareAPI.listAllDocuments().get(0);
+        byte[] sharedFile = linshareAPIForTechnicalAccountTesting
+            .downloadFileFrom(USER_1, sharedDoc.getId());
 
         try (ZipAssert zipAssert = assertThatZip(new ByteArrayInputStream(sharedFile))) {
             zipAssert.hasEntriesSize(1);
@@ -238,7 +174,7 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
     }
 
     @Test
-    void exportShouldShareNonEmptyZipViaLinshareWhenImapDelete() throws Exception {
+    void exportShouldShareNonEmptyZipViaLinshareWhenImapDelete(LinshareAPIForTechnicalAccountTesting linshareAPIForTechnicalAccountTesting) throws Exception {
         bartSendMessageToHomer();
 
         WAIT_TEN_SECONDS.until(() -> listMessageIdsForAccount(homerAccessToken).size() == 1);
@@ -253,9 +189,9 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
 
         exportVaultContent(webAdminApi, EXPORT_ALL_HOMER_MESSAGES_TO_USER_1);
 
-        Document sharedDoc = user1LinshareAPI.receivedShares().get(0).getDocument();
-        byte[] sharedFile =  linshareGuiceExtension.getLinshareJunitExtension()
-            .downloadSharedFile(USER_1, sharedDoc.getId(), sharedDoc.getName());
+        Document sharedDoc = user1LinshareAPI.listAllDocuments().get(0);
+        byte[] sharedFile = linshareAPIForTechnicalAccountTesting
+            .downloadFileFrom(USER_1, sharedDoc.getId());
 
         try (ZipAssert zipAssert = assertThatZip(new ByteArrayInputStream(sharedFile))) {
             zipAssert.hasEntriesSize(1);
@@ -270,7 +206,7 @@ public abstract class LinshareBlobExportMechanismIntegrationTest {
             "  [" +
             "    \"setMessages\"," +
             "    {" +
-            "      \"create\": { \"" + messageCreationId  + "\" : {" +
+            "      \"create\": { \"" + messageCreationId + "\" : {" +
             "        \"from\": { \"name\": \"user2\", \"email\": \"" + BART + "\"}," +
             "        \"to\": [{ \"name\": \"user1\", \"email\": \"" + HOMER + "\"}]," +
             "        \"subject\": \"" + SUBJECT + "\"," +
diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java
index 34d55f1..f3ca133 100644
--- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/ExportService.java
@@ -78,7 +78,7 @@ class ExportService {
         blobExport.blobId(blobId)
             .with(exportToAddress)
             .explanation(exportMessage(username))
-            .filePrefix(Optional.of("deleted-message-of-" + username.asString() + "_"))
+            .filePrefix(Optional.of(String.format("deleted-message-of-%s_", username.asString())))
             .fileExtension(FileExtension.ZIP)
             .export();
     }
diff --git a/third-party/linshare/src/main/java/org/apache/james/linshare/LinshareBlobExportMechanism.java b/third-party/linshare/src/main/java/org/apache/james/linshare/LinshareBlobExportMechanism.java
index f01c301..92c280a 100644
--- a/third-party/linshare/src/main/java/org/apache/james/linshare/LinshareBlobExportMechanism.java
+++ b/third-party/linshare/src/main/java/org/apache/james/linshare/LinshareBlobExportMechanism.java
@@ -32,9 +32,8 @@ import org.apache.james.blob.export.api.BlobExportMechanism;
 import org.apache.james.blob.export.api.ExportedFileNamesGenerator;
 import org.apache.james.blob.export.api.FileExtension;
 import org.apache.james.core.MailAddress;
-import org.apache.james.linshare.client.Document;
 import org.apache.james.linshare.client.LinshareAPI;
-import org.apache.james.linshare.client.ShareRequest;
+import org.apache.james.linshare.client.User;
 
 import com.google.common.io.Files;
 
@@ -55,31 +54,28 @@ public class LinshareBlobExportMechanism implements BlobExportMechanism {
     public ShareeStage blobId(BlobId blobId) {
         return mailAddress -> explanation -> fileCustomPrefix -> fileExtension -> () ->  {
             try {
-                exportBlob(blobId, mailAddress, fileCustomPrefix, fileExtension, explanation);
+                exportBlob(blobId, mailAddress, fileCustomPrefix, fileExtension);
             } catch (Exception e) {
-                throw new BlobExportException("Error while exporting blob " + blobId.asString() + " to " + mailAddress.asString(), e);
+                throw new BlobExportException(String.format("Error while exporting blob %s to %s", blobId.asString(),
+                    mailAddress.asString()), e);
             }
         };
     }
 
     private void exportBlob(BlobId blobId, MailAddress mailAddress, Optional<String> fileCustomPrefix,
-                            Optional<FileExtension> fileExtension, String explanation) throws IOException {
+                            Optional<FileExtension> fileExtension) throws IOException {
         String fileName = ExportedFileNamesGenerator.generateFileName(fileCustomPrefix, blobId, fileExtension);
         File tempFile = new File(tempDir, fileName);
         try {
             FileUtils.copyInputStreamToFile(blobStore.read(blobStore.getDefaultBucketName(), blobId), tempFile);
-            uploadAndShare(mailAddress, tempFile, explanation);
+            uploadDocumentToTargetMail(mailAddress, tempFile);
         } finally {
             FileUtils.forceDelete(tempFile);
         }
     }
 
-    private void uploadAndShare(MailAddress mailAddress, File tempFile, String explanation) {
-        Document document = linshareAPI.uploadDocument(tempFile);
-        linshareAPI.share(ShareRequest.builder()
-            .message(explanation)
-            .addDocumentId(document.getId())
-            .addRecipient(mailAddress)
-            .build());
+    private void uploadDocumentToTargetMail(MailAddress mailAddress, File tempFile) {
+        User targetUser = linshareAPI.getUserByMail(mailAddress.asString());
+        linshareAPI.uploadDocumentByDelegation(targetUser, tempFile);
     }
 }
diff --git a/third-party/linshare/src/main/java/org/apache/james/linshare/LinshareConfiguration.java b/third-party/linshare/src/main/java/org/apache/james/linshare/LinshareConfiguration.java
index a619284..44f3add 100644
--- a/third-party/linshare/src/main/java/org/apache/james/linshare/LinshareConfiguration.java
+++ b/third-party/linshare/src/main/java/org/apache/james/linshare/LinshareConfiguration.java
@@ -22,6 +22,7 @@ package org.apache.james.linshare;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.Objects;
+import java.util.UUID;
 
 import org.apache.commons.configuration2.Configuration;
 
@@ -30,67 +31,81 @@ import com.google.common.base.Preconditions;
 
 public class LinshareConfiguration {
 
-    public static final String URL = "blob.export.linshare.url";
-    public static final String TOKEN = "blob.export.linshare.token";
+    public static final String URL_PROPERTY = "blob.export.linshare.url";
+    public static final String UUID_PROPERTY = "blob.export.linshare.technical.account.uuid";
+    public static final String PASSWORD_PROPERTY = "blob.export.linshare.technical.account.password";
 
     public static class Builder {
         @FunctionalInterface
         public interface RequireUrl {
-            RequireAuthorizationToken url(URL url);
+            RequireBasicAuthorization url(URL url);
 
-            default RequireAuthorizationToken urlAsString(String url) throws MalformedURLException {
+            default RequireBasicAuthorization urlAsString(String url) throws MalformedURLException {
                 return url(new URL(url));
             }
         }
 
-        public interface RequireAuthorizationToken {
-            ReadyToBuild authorizationToken(AuthorizationToken token);
+        public interface RequireBasicAuthorization {
+            ReadyToBuild basicAuthorization(String uuid, String password);
         }
 
         public static class ReadyToBuild {
             private final URL url;
-            private final AuthorizationToken token;
+            private final UUID uuid;
+            private final String password;
 
-            ReadyToBuild(URL url, AuthorizationToken token) {
+            ReadyToBuild(URL url, UUID uuid, String password) {
                 this.url = url;
-                this.token = token;
+                this.uuid = uuid;
+                this.password = password;
             }
 
             public LinshareConfiguration build() {
-                return new LinshareConfiguration(url, token);
+                return new LinshareConfiguration(url, uuid, password);
             }
         }
     }
 
     public static Builder.RequireUrl builder() {
-        return url -> credential -> new Builder.ReadyToBuild(url, credential);
+        return url -> (uuid, password) -> new Builder.ReadyToBuild(url, UUID.fromString(uuid), password);
     }
 
     public static LinshareConfiguration from(Configuration configuration) throws MalformedURLException {
         return builder()
-            .urlAsString(configuration.getString(URL, null))
-            .authorizationToken(new AuthorizationToken(configuration.getString(TOKEN, null)))
+            .urlAsString(configuration.getString(URL_PROPERTY, null))
+            .basicAuthorization(
+                configuration.getString(UUID_PROPERTY),
+                configuration.getString(PASSWORD_PROPERTY))
             .build();
     }
 
     private final URL url;
-    private final AuthorizationToken token;
+    private final UUID uuid;
+    private final String password;
 
     @VisibleForTesting
-    LinshareConfiguration(URL url, AuthorizationToken token) {
-        Preconditions.checkNotNull(url, "'" + URL + "' can not be null");
-        Preconditions.checkNotNull(token, "'" + TOKEN + "' can not be null");
+    LinshareConfiguration(URL url, UUID uuid, String password) {
+        Preconditions.checkNotNull(url, "'%s' can not be null", URL_PROPERTY);
+        Preconditions.checkNotNull(uuid, "'%s' can not be null", UUID_PROPERTY);
+
+        Preconditions.checkNotNull(password, "'%s' can not be null", PASSWORD_PROPERTY);
+        Preconditions.checkArgument(!password.isEmpty(), "'%s' can not be empty", PASSWORD_PROPERTY);
 
         this.url = url;
-        this.token = token;
+        this.uuid = uuid;
+        this.password = password;
     }
 
     public URL getUrl() {
         return url;
     }
 
-    public AuthorizationToken getToken() {
-        return token;
+    public UUID getUuid() {
+        return uuid;
+    }
+
+    public String getPassword() {
+        return password;
     }
 
     @Override
@@ -99,13 +114,14 @@ public class LinshareConfiguration {
             LinshareConfiguration that = (LinshareConfiguration) o;
 
             return Objects.equals(this.url, that.url)
-                && Objects.equals(this.token, that.token);
+                && Objects.equals(this.uuid, that.uuid)
+                && Objects.equals(this.password, that.password);
         }
         return false;
     }
 
     @Override
     public final int hashCode() {
-        return Objects.hash(url, token);
+        return Objects.hash(url, uuid, password);
     }
 }
diff --git a/third-party/linshare/src/main/java/org/apache/james/linshare/client/LinshareAPI.java b/third-party/linshare/src/main/java/org/apache/james/linshare/client/LinshareAPI.java
index 7c1a3d2..9d7b1bd 100644
--- a/third-party/linshare/src/main/java/org/apache/james/linshare/client/LinshareAPI.java
+++ b/third-party/linshare/src/main/java/org/apache/james/linshare/client/LinshareAPI.java
@@ -24,9 +24,7 @@ import static org.apache.james.linshare.client.LinshareAPI.Headers.CONTENT_TYPE_
 import static org.apache.james.linshare.client.LinshareAPI.Headers.CONTENT_TYPE_MULTIPART;
 
 import java.io.File;
-import java.util.List;
 
-import org.apache.james.linshare.AuthorizationToken;
 import org.apache.james.linshare.LinshareConfiguration;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -35,9 +33,8 @@ import com.google.common.base.Preconditions;
 import feign.Feign;
 import feign.Logger;
 import feign.Param;
-import feign.RequestInterceptor;
 import feign.RequestLine;
-import feign.RequestTemplate;
+import feign.auth.BasicAuthRequestInterceptor;
 import feign.form.FormEncoder;
 import feign.jackson.JacksonDecoder;
 import feign.jackson.JacksonEncoder;
@@ -51,24 +48,13 @@ public interface LinshareAPI {
         String CONTENT_TYPE_APPLICATION_JSON = "Content-Type: application/json";
     }
 
-    class AuthorizationInterceptor implements RequestInterceptor {
-
-        private final AuthorizationToken authorizationToken;
-
-        AuthorizationInterceptor(AuthorizationToken authorizationToken) {
-            this.authorizationToken = authorizationToken;
-        }
-
-        @Override
-        public void apply(RequestTemplate template) {
-            template.header("Authorization", authorizationToken.asBearerHeader());
-        }
-    }
-
     @VisibleForTesting
     static LinshareAPI from(LinshareConfiguration configuration) {
         return Feign.builder()
-            .requestInterceptor(new AuthorizationInterceptor(configuration.getToken()))
+            .requestInterceptor(
+                new BasicAuthRequestInterceptor(
+                    configuration.getUuid().toString(),
+                    configuration.getPassword()))
             .logger(new Slf4jLogger(LinshareAPI.class))
             .logLevel(Logger.Level.FULL)
             .encoder(new FormEncoder(new JacksonEncoder()))
@@ -76,40 +62,18 @@ public interface LinshareAPI {
             .target(LinshareAPI.class, configuration.getUrl().toString());
     }
 
-    @RequestLine("GET /linshare/webservice/rest/user/v2/documents")
-    @feign.Headers(ACCEPT_APPLICATION_JSON)
-    List<Document> listAllDocuments();
+    @RequestLine("GET /linshare/webservice/rest/delegation/v2/users/{targetUserMail}")
+    @feign.Headers({ ACCEPT_APPLICATION_JSON, CONTENT_TYPE_APPLICATION_JSON })
+    User getUserByMail(@Param("targetUserMail") String mail);
 
-    @RequestLine("POST /linshare/webservice/rest/user/v2/documents")
+    @RequestLine("POST linshare/webservice/rest/delegation/v2/{userUuid}/documents")
     @feign.Headers({ ACCEPT_APPLICATION_JSON, CONTENT_TYPE_MULTIPART })
-    Document uploadDocument(@Param("file") File file, @Param("filesize") long fileSize);
+    Document uploadDocumentByDelegation(@Param("userUuid") String userUuid, @Param("file") File file, @Param("filesize") long fileSize);
 
-    default Document uploadDocument(File file) {
+    default Document uploadDocumentByDelegation(User targetUser, File file) {
         Preconditions.checkNotNull(file);
         Preconditions.checkArgument(file.exists(), "File to upload does not exist: %s", file.getAbsolutePath());
 
-        return uploadDocument(file, file.length());
+        return uploadDocumentByDelegation(targetUser.getUuid(), file, file.length());
     }
-
-    @RequestLine("POST /linshare/webservice/rest/user/v2/shares")
-    @feign.Headers({ ACCEPT_APPLICATION_JSON, CONTENT_TYPE_APPLICATION_JSON })
-    List<ShareResult> share(ShareRequest request);
-
-    @RequestLine("DELETE /linshare/webservice/rest/user/v2/documents/{documentId}")
-    @feign.Headers({ ACCEPT_APPLICATION_JSON, CONTENT_TYPE_APPLICATION_JSON })
-    Document delete(@Param("documentId") String documentId);
-
-    default Document delete(Document.DocumentId documentId) {
-        return delete(documentId.asString());
-    }
-
-    @VisibleForTesting
-    default void deleteAllDocuments() {
-        listAllDocuments()
-            .forEach(document -> delete(document.getId()));
-    }
-
-    @RequestLine("GET /linshare/webservice/rest/user/v2/received_shares")
-    @feign.Headers({ ACCEPT_APPLICATION_JSON, CONTENT_TYPE_APPLICATION_JSON })
-    List<ReceivedShare> receivedShares();
 }
diff --git a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareBlobExportMechanismTest.java b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareBlobExportMechanismTest.java
index 2988281..736707c 100644
--- a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareBlobExportMechanismTest.java
+++ b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareBlobExportMechanismTest.java
@@ -19,15 +19,12 @@
 
 package org.apache.james.linshare;
 
-import static io.restassured.RestAssured.given;
 import static org.apache.james.blob.api.BlobStore.StoragePolicy.LOW_COST;
-import static org.apache.james.linshare.LinshareFixture.USER_1;
+import static org.apache.james.linshare.LinshareExtension.LinshareAPIForTechnicalAccountTesting;
+import static org.apache.james.linshare.LinshareExtension.LinshareAPIForUserTesting;
 import static org.apache.james.linshare.LinshareFixture.USER_2;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.hasSize;
 
 import java.nio.charset.StandardCharsets;
 import java.util.Optional;
@@ -40,15 +37,10 @@ import org.apache.james.blob.memory.MemoryBlobStore;
 import org.apache.james.blob.memory.MemoryDumbBlobStore;
 import org.apache.james.core.MailAddress;
 import org.apache.james.linshare.client.Document;
-import org.apache.james.linshare.client.LinshareAPI;
-import org.awaitility.Awaitility;
-import org.awaitility.Duration;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
-import io.restassured.specification.RequestSpecification;
-
 class LinshareBlobExportMechanismTest {
     private static final String FILE_CONTENT = "content";
     private static final String EXPLANATION = "Explanation about the file being shared";
@@ -60,7 +52,7 @@ class LinshareBlobExportMechanismTest {
     private MemoryBlobStore blobStore;
     private LinshareBlobExportMechanism testee;
     private HashBlobId.Factory blobIdFactory;
-    private LinshareAPI user2API;
+    private LinshareAPIForUserTesting user2API;
 
     @BeforeEach
     void setUp() throws Exception {
@@ -68,14 +60,14 @@ class LinshareBlobExportMechanismTest {
         blobStore = new MemoryBlobStore(blobIdFactory, new MemoryDumbBlobStore());
 
         testee = new LinshareBlobExportMechanism(
-            linshareExtension.getAPIFor(USER_1),
+            linshareExtension.getDelegationAccountAPI(),
             blobStore);
 
-        user2API = linshareExtension.getAPIFor(USER_2);
+        user2API = LinshareAPIForUserTesting.from(USER_2);
     }
 
     @Test
-    void exportShouldShareTheDocumentViaLinshare() throws Exception {
+    void exportShouldUploadTheDocumentToTargetUserViaLinshare() throws Exception {
         BlobId blobId = blobStore.save(blobStore.getDefaultBucketName(), FILE_CONTENT, LOW_COST).block();
         String filePrefix = "deleted-message-of-bob@james.org-";
 
@@ -86,44 +78,14 @@ class LinshareBlobExportMechanismTest {
             .fileExtension(FILE_TEXT_EXTENSION)
             .export();
 
-        assertThat(user2API.receivedShares())
+        assertThat(user2API.listAllDocuments())
             .hasSize(1)
-            .allSatisfy(receivedShare -> assertThat(receivedShare.getDocument().getName()).endsWith(".txt"))
-            .allSatisfy(receivedShare -> assertThat(receivedShare.getDocument().getName()).startsWith(filePrefix))
-            .allSatisfy(receivedShare -> assertThat(receivedShare.getSender().getMail()).isEqualTo(USER_1.getUsername()));
-    }
-
-    @Test
-    void exportShouldSendAnEmailToSharee() throws Exception {
-        BlobId blobId = blobStore.save(blobStore.getDefaultBucketName(), FILE_CONTENT, LOW_COST).block();
-
-        testee.blobId(blobId)
-            .with(new MailAddress(USER_2.getUsername()))
-            .explanation(EXPLANATION)
-            .noFileCustomPrefix()
-            .fileExtension(FILE_TEXT_EXTENSION)
-            .export();
-
-        RequestSpecification request = given(linshareExtension.getLinshare().fakeSmtpRequestSpecification());
-
-        Awaitility.waitAtMost(Duration.TEN_SECONDS)
-            .pollInterval(Duration.ONE_SECOND)
-            .untilAsserted(
-                () -> request
-                    .get("/api/email")
-                .then()
-                    .body("", hasSize(2)));
-
-        request
-            .get("/api/email")
-        .then()
-            .body("[1].subject", containsString("John Doe has shared a file with you"))
-            .body("[1].to", hasItem(USER_2.getUsername()))
-            .body("[1].html", containsString(EXPLANATION));
+            .allSatisfy(receivedShare -> assertThat(receivedShare.getName()).endsWith(".txt"))
+            .allSatisfy(receivedShare -> assertThat(receivedShare.getName()).startsWith(filePrefix));
     }
 
     @Test
-    void exportShouldShareTheDocumentAndAllowDownloadViaLinshare() throws Exception {
+    void exportShouldUploadTheDocumentAndAllowDownloadViaLinshare(LinshareAPIForTechnicalAccountTesting delegationAPIForTesting) throws Exception {
         BlobId blobId = blobStore.save(blobStore.getDefaultBucketName(), FILE_CONTENT, LOW_COST).block();
 
         testee.blobId(blobId)
@@ -133,8 +95,8 @@ class LinshareBlobExportMechanismTest {
             .fileExtension(FILE_TEXT_EXTENSION)
             .export();
 
-        Document sharedDoc = user2API.receivedShares().get(0).getDocument();
-        byte[] sharedFile =  linshareExtension.downloadSharedFile(USER_2, sharedDoc.getId(), sharedDoc.getName());
+        Document sharedDoc = user2API.listAllDocuments().get(0);
+        byte[] sharedFile = delegationAPIForTesting.downloadFileFrom(USER_2, sharedDoc.getId());
         assertThat(sharedFile).isEqualTo(FILE_CONTENT.getBytes(StandardCharsets.UTF_8));
     }
 
@@ -164,7 +126,7 @@ class LinshareBlobExportMechanismTest {
             .fileExtension(FILE_TEXT_EXTENSION)
             .export();
 
-        Document sharedDoc = user2API.receivedShares().get(0).getDocument();
+        Document sharedDoc = user2API.listAllDocuments().get(0);
         assertThat(sharedDoc.getName())
             .startsWith(filePrefix);
     }
diff --git a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareConfigurationTest.java b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareConfigurationTest.java
index 81d5d66..093bceb 100644
--- a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareConfigurationTest.java
+++ b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareConfigurationTest.java
@@ -24,6 +24,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.UUID;
 
 import org.apache.commons.configuration2.PropertiesConfiguration;
 import org.junit.jupiter.api.Test;
@@ -31,6 +32,12 @@ import org.junit.jupiter.api.Test;
 import nl.jqno.equalsverifier.EqualsVerifier;
 
 class LinshareConfigurationTest {
+
+    private final static String EMPTY_STRING = "";
+    private final static String SOME_RANDOM_STRING = "laksdhfdksd";
+    private final String DEFAULT_URL = "http://127.0.0.1:8080";
+    private final static String DEFAULT_UUID = UUID.randomUUID().toString();
+
     @Test
     void shouldMatchBeanContract() {
         EqualsVerifier.forClass(LinshareConfiguration.class)
@@ -40,17 +47,59 @@ class LinshareConfigurationTest {
     @Test
     void fromShouldThrowWhenUrlIsNull() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty("blob.export.linshare.token", "token");
-        configuration.addProperty("blob.export.linshare.url", null);
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, null);
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, DEFAULT_UUID);
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, LinshareFixture.TECHNICAL_ACCOUNT.getPassword());
 
         assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(MalformedURLException.class);
     }
 
     @Test
-    void fromShouldThrowWhenTokenIsNull() {
+    void fromShouldThrowWhenBasicAuthIsNull() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, DEFAULT_URL);
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, null);
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, null);
+
+        assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenUUIDIsNull() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty("blob.export.linshare.token", null);
-        configuration.addProperty("blob.export.linshare.url", "http://127.0.0.1:8080");
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, SOME_RANDOM_STRING);
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, null);
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, DEFAULT_URL);
+
+        assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenUUIDIsEmpty() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, EMPTY_STRING);
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, SOME_RANDOM_STRING);
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, DEFAULT_URL);
+
+        assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenUUIDIsWrongFormat() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, SOME_RANDOM_STRING);
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, SOME_RANDOM_STRING);
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, DEFAULT_URL);
+
+        assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenUUIDIsTooLong() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, "way-too-long-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, SOME_RANDOM_STRING);
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, DEFAULT_URL);
 
         assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(IllegalArgumentException.class);
     }
@@ -58,33 +107,46 @@ class LinshareConfigurationTest {
     @Test
     void fromShouldThrowWhenURLIsInvalid() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty("blob.export.linshare.token", "token");
-        configuration.addProperty("blob.export.linshare.url", "invalid");
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, DEFAULT_UUID);
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, LinshareFixture.TECHNICAL_ACCOUNT.getPassword());
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, "invalid");
 
         assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(MalformedURLException.class);
     }
 
     @Test
-    void fromShouldThrowWhenTokenIsEmpty() {
+    void fromShouldThrowWhenPasswordIsNull() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, DEFAULT_UUID);
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, null);
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, DEFAULT_URL);
+
+        assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void fromShouldThrowWhenPasswordIsEmpty() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty("blob.export.linshare.token", "");
-        configuration.addProperty("blob.export.linshare.url", "http://127.0.0.1:8080");
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, DEFAULT_UUID);
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, EMPTY_STRING);
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, DEFAULT_URL);
 
         assertThatThrownBy(() -> LinshareConfiguration.from(configuration)).isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     void fromShouldReturnProvidedConfiguration() throws Exception {
-        String token = "token";
-        String url = "http://127.0.0.1:8080";
+        String password = LinshareFixture.TECHNICAL_ACCOUNT.getPassword();
+        String url = DEFAULT_URL;
 
         PropertiesConfiguration configuration = new PropertiesConfiguration();
-        configuration.addProperty("blob.export.linshare.token", token);
-        configuration.addProperty("blob.export.linshare.url", url);
+        configuration.addProperty(LinshareConfiguration.UUID_PROPERTY, DEFAULT_UUID);
+        configuration.addProperty(LinshareConfiguration.PASSWORD_PROPERTY, password);
+        configuration.addProperty(LinshareConfiguration.URL_PROPERTY, url);
 
         assertThat(LinshareConfiguration.from(configuration)).isEqualTo(LinshareConfiguration.builder()
             .url(new URL(url))
-            .authorizationToken(new AuthorizationToken(token))
+            .basicAuthorization(DEFAULT_UUID, password)
             .build());
     }
 }
diff --git a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareExtension.java b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareExtension.java
index 103ef3d..92dade8 100644
--- a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareExtension.java
+++ b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareExtension.java
@@ -19,6 +19,7 @@
 package org.apache.james.linshare;
 
 import static org.apache.james.linshare.LinshareFixture.ADMIN_ACCOUNT;
+import static org.apache.james.linshare.LinshareFixture.TECHNICAL_ACCOUNT;
 import static org.apache.james.linshare.LinshareFixture.USER_1;
 import static org.apache.james.linshare.LinshareFixture.USER_CREDENTIAL_MAP;
 import static org.apache.james.linshare.client.LinshareAPI.Headers.ACCEPT_APPLICATION_JSON;
@@ -26,6 +27,7 @@ import static org.apache.james.linshare.client.LinshareAPI.Headers.CONTENT_TYPE_
 
 import java.util.List;
 import java.util.Optional;
+import java.util.UUID;
 
 import org.apache.james.linshare.client.Document;
 import org.apache.james.linshare.client.LinshareAPI;
@@ -37,8 +39,12 @@ import org.apache.james.utils.FakeSmtp;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
 import org.junit.jupiter.api.extension.BeforeEachCallback;
 import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
 
-import com.github.fge.lambdas.Throwing;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
 
 import feign.Feign;
 import feign.Headers;
@@ -51,43 +57,32 @@ import feign.jackson.JacksonDecoder;
 import feign.jackson.JacksonEncoder;
 import feign.slf4j.Slf4jLogger;
 
-public class LinshareExtension implements BeforeEachCallback, BeforeAllCallback {
+public class LinshareExtension implements BeforeEachCallback, BeforeAllCallback, ParameterResolver {
 
-    private interface LinshareAPIForTesting {
+    private static final Linshare linshare = LinshareSingleton.singleton;
 
-        String CONTENT_DISPOSITION_ATTACHMENT = "Content-Disposition: attachment; filename=\"{filename}\"";
-        String CONTENT_TYPE_APPLICATION_OCTET_STREAM = "Content-Type: application/octet-stream";
+    private UUID technicalAccountUUID;
 
-        static LinshareAPIForTesting from(LinshareFixture.Credential credential, Linshare linshare) {
+    public interface LinshareAPIForAdminTesting {
+        @VisibleForTesting
+        static LinshareAPIForAdminTesting from(LinshareFixture.Credential credential) {
 
             return Feign.builder()
                 .requestInterceptor(new BasicAuthRequestInterceptor(credential.getUsername(), credential.getPassword()))
-                .logger(new Slf4jLogger(LinshareAPIForTesting.class))
+                .logger(new Slf4jLogger(LinshareAPIForAdminTesting.class))
                 .logLevel(Logger.Level.FULL)
                 .encoder(new FormEncoder(new JacksonEncoder()))
                 .decoder(CombinedDecoder.builder()
                     .defaultDecoder(new JacksonDecoder())
                     .registerSingleTypeDecoder(new ByteArrayDecoder())
                     .build())
-                .target(LinshareAPIForTesting.class, linshare.getUrl());
+                .target(LinshareAPIForAdminTesting.class, linshare.getUrl());
         }
 
-        @RequestLine("GET /linshare/webservice/rest/user/v2/authentication/jwt")
-        @Headers(ACCEPT_APPLICATION_JSON)
-        AuthorizationToken jwt();
-
-        @RequestLine("GET /linshare/webservice/rest/user/v2/users")
-        @Headers(ACCEPT_APPLICATION_JSON)
-        List<User> allUsers();
-
         @RequestLine("GET /linshare/webservice/rest/admin/technical_accounts")
         @Headers(ACCEPT_APPLICATION_JSON)
         List<TechnicalAccountResponse> allTechnicalAccounts();
 
-        @RequestLine("GET /linshare/webservice/rest/user/v2/received_shares/{documentId}/download")
-        @Headers({ CONTENT_TYPE_APPLICATION_OCTET_STREAM, CONTENT_DISPOSITION_ATTACHMENT })
-        byte[] downloadShare(@Param("documentId") String documentId, @Param("filename") String filename);
-
         @RequestLine("POST /linshare/webservice/rest/admin/technical_accounts")
         @Headers({ ACCEPT_APPLICATION_JSON, CONTENT_TYPE_APPLICATION_JSON })
         TechnicalAccountResponse createTechnicalAccount(TechnicalAccountCreationRequest accountCreationRequest);
@@ -97,7 +92,72 @@ public class LinshareExtension implements BeforeEachCallback, BeforeAllCallback
         TechnicalAccountResponse grantTechnicalAccountPermissions(TechnicalAccountGrantPermissionsRequest accountGrantPermissionsRequest);
     }
 
-    private final Linshare linshare = LinshareSingleton.singleton;
+    public interface LinshareAPIForTechnicalAccountTesting {
+        @VisibleForTesting
+        static LinshareAPIForTechnicalAccountTesting from(LinshareFixture.Credential credential) {
+
+            return Feign.builder()
+                .requestInterceptor(new BasicAuthRequestInterceptor(credential.getUsername(), credential.getPassword()))
+                .logger(new Slf4jLogger(LinshareAPIForTechnicalAccountTesting.class))
+                .logLevel(Logger.Level.FULL)
+                .encoder(new FormEncoder(new JacksonEncoder()))
+                .decoder(CombinedDecoder.builder()
+                    .defaultDecoder(new JacksonDecoder())
+                    .registerSingleTypeDecoder(new ByteArrayDecoder())
+                    .build())
+                .target(LinshareAPIForTechnicalAccountTesting.class, linshare.getUrl());
+        }
+
+        @RequestLine("GET /linshare/webservice/rest/user/documents/{documentId}/download")
+        @Headers({ACCEPT_APPLICATION_JSON, CONTENT_TYPE_APPLICATION_JSON})
+        byte[] download(@Param("documentId") String documentId);
+
+        default byte[] downloadFileFrom(LinshareFixture.Credential credential, Document.DocumentId document) {
+            return from(credential).download(document.asString());
+        }
+    }
+
+    public interface LinshareAPIForUserTesting {
+        @VisibleForTesting
+         static LinshareAPIForUserTesting from(LinshareFixture.Credential credential) {
+
+            return Feign.builder()
+                .requestInterceptor(new BasicAuthRequestInterceptor(credential.getUsername(), credential.getPassword()))
+                .logger(new Slf4jLogger(LinshareAPIForUserTesting.class))
+                .logLevel(Logger.Level.FULL)
+                .encoder(new FormEncoder(new JacksonEncoder()))
+                .decoder(CombinedDecoder.builder()
+                    .defaultDecoder(new JacksonDecoder())
+                    .registerSingleTypeDecoder(new ByteArrayDecoder())
+                    .build())
+                .target(LinshareAPIForUserTesting.class, linshare.getUrl());
+        }
+
+        @RequestLine("GET /linshare/webservice/rest/user/v2/documents")
+        @feign.Headers(ACCEPT_APPLICATION_JSON)
+        List<Document> listAllDocuments();
+
+        @RequestLine("DELETE /linshare/webservice/rest/user/v2/documents/{documentId}")
+        @feign.Headers({ ACCEPT_APPLICATION_JSON, CONTENT_TYPE_APPLICATION_JSON })
+        Document delete(@Param("documentId") String documentId);
+
+        default Document delete(Document.DocumentId documentId) {
+            return delete(documentId.asString());
+        }
+
+        default void deleteAllDocuments() {
+            listAllDocuments().forEach(document -> delete(document.getId()));
+        }
+
+        @RequestLine("GET /linshare/webservice/rest/user/v2/users")
+        @Headers(ACCEPT_APPLICATION_JSON)
+        List<User> allUsers();
+    }
+
+    @Override
+    public void beforeAll(ExtensionContext extensionContext) {
+        createTechnicalAccount(TechnicalAccountCreationRequest.defaultAccount());
+    }
 
     @Override
     public void beforeEach(ExtensionContext context) {
@@ -105,56 +165,65 @@ public class LinshareExtension implements BeforeEachCallback, BeforeAllCallback
         FakeSmtp.clean(linshare.fakeSmtpRequestSpecification());
     }
 
-    public Linshare getLinshare() {
-        return linshare;
+    @Override
+    public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        return (parameterContext.getParameter().getType() == LinshareAPIForTechnicalAccountTesting.class);
+    }
+
+    @Override
+    public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+        Preconditions.checkArgument(parameterContext.getParameter().getType() == LinshareAPIForTechnicalAccountTesting.class);
+        return getDelegationAccountTestingAPI();
     }
 
-    public LinshareAPI getAPIFor(LinshareFixture.Credential credential) throws Exception {
-        return LinshareAPI.from(configurationWithJwtFor(credential));
+    public LinshareAPI getDelegationAccountAPI() throws Exception {
+        return LinshareAPI.from(configurationWithBasicAuthFor(
+            new LinshareFixture.Credential(
+                technicalAccountUUID.toString(),
+                TECHNICAL_ACCOUNT.getPassword())));
     }
 
-    public LinshareConfiguration configurationWithJwtFor(LinshareFixture.Credential credential) throws Exception {
-        AuthorizationToken token = LinshareAPIForTesting.from(credential, linshare).jwt();
+    public LinshareAPIForTechnicalAccountTesting getDelegationAccountTestingAPI() {
+        return LinshareAPIForTechnicalAccountTesting.from(
+            new LinshareFixture.Credential(
+                technicalAccountUUID.toString(),
+                TECHNICAL_ACCOUNT.getPassword()));
+    }
 
+    public LinshareConfiguration configurationWithBasicAuthFor(LinshareFixture.Credential credential) throws Exception {
         return LinshareConfiguration.builder()
             .urlAsString(linshare.getUrl())
-            .authorizationToken(token)
+            .basicAuthorization(credential.getUsername(), credential.getPassword())
             .build();
     }
 
-    public byte[] downloadSharedFile(LinshareFixture.Credential credential, Document.DocumentId document, String filename) {
-        return LinshareAPIForTesting.from(credential, linshare)
-            .downloadShare(document.asString(), filename);
-    }
-
-    @Override
-    public void beforeAll(ExtensionContext extensionContext){
-        createTechnicalAccount(TechnicalAccountCreationRequest.defaultAccount());
-    }
-
-    private void createTechnicalAccount(TechnicalAccountCreationRequest technicalAccountDTO) {
-        TechnicalAccountResponse technicalAccountResponse = LinshareAPIForTesting.from(ADMIN_ACCOUNT, linshare).createTechnicalAccount(technicalAccountDTO);
+    private void createTechnicalAccount(TechnicalAccountCreationRequest technicalAccountCreationRequest) {
+        TechnicalAccountResponse technicalAccountResponse = LinshareAPIForAdminTesting.from(ADMIN_ACCOUNT).createTechnicalAccount(technicalAccountCreationRequest);
 
         TechnicalAccountGrantPermissionsRequest technicalAccountGrantPermissionsRequest = new TechnicalAccountGrantPermissionsRequest(technicalAccountResponse);
-        LinshareAPIForTesting.from(ADMIN_ACCOUNT, linshare).grantTechnicalAccountPermissions(technicalAccountGrantPermissionsRequest);
-    }
-
-    public List<TechnicalAccountResponse> getAllTechnicalAccounts(LinshareFixture.Credential credential) {
-        return LinshareAPIForTesting.from(credential, linshare).allTechnicalAccounts();
+        LinshareAPIForAdminTesting.from(ADMIN_ACCOUNT).grantTechnicalAccountPermissions(technicalAccountGrantPermissionsRequest);
+        this.technicalAccountUUID = UUID.fromString(technicalAccountResponse.getUuid());
     }
 
     private void deleteAllUsersDocuments() {
-        LinshareAPIForTesting.from(USER_1, linshare)
+        LinshareAPIForUserTesting.from(USER_1)
             .allUsers()
             .stream()
             .map(this::getUsernamePassword)
-            .map(Throwing.function(this::configurationWithJwtFor))
-            .map(LinshareAPI::from)
-            .forEach(LinshareAPI::deleteAllDocuments);
+            .map(LinshareAPIForUserTesting::from)
+            .forEach(LinshareAPIForUserTesting::deleteAllDocuments);
     }
 
     private LinshareFixture.Credential getUsernamePassword(User user) {
         return Optional.ofNullable(USER_CREDENTIAL_MAP.get(user.getMail()))
             .orElseThrow(() -> new RuntimeException("cannot get token of user " + user.getMail()));
     }
+
+    public UUID getTechnicalAccountUUID() {
+        return technicalAccountUUID;
+    }
+
+    public Linshare getLinshare() {
+        return linshare;
+    }
 }
diff --git a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareTest.java b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareTest.java
index 27d55e4..0e9fd3a 100644
--- a/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareTest.java
+++ b/third-party/linshare/src/test/java/org/apache/james/linshare/LinshareTest.java
@@ -22,9 +22,11 @@ import static io.restassured.RestAssured.given;
 import static io.restassured.config.EncoderConfig.encoderConfig;
 import static io.restassured.config.RestAssuredConfig.newConfig;
 import static org.apache.james.linshare.LinshareFixture.ACCOUNT_ENABLED;
+import static org.apache.james.linshare.LinshareFixture.ADMIN_ACCOUNT;
 import static org.apache.james.linshare.LinshareFixture.TECHNICAL_ACCOUNT;
 import static org.apache.james.linshare.LinshareFixture.TECHNICAL_PERMISSIONS;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.apache.james.linshare.LinshareExtension.LinshareAPIForAdminTesting;
 
 import java.nio.charset.StandardCharsets;
 import java.util.List;
@@ -69,7 +71,7 @@ class LinshareTest {
 
     @Test
     void linshareShouldHaveATechnicalAccountConfigured() {
-        List<TechnicalAccountResponse> technicalAccounts = linshareExtension.getAllTechnicalAccounts(LinshareFixture.ADMIN_ACCOUNT);
+        List<TechnicalAccountResponse> technicalAccounts = LinshareAPIForAdminTesting.from(ADMIN_ACCOUNT).allTechnicalAccounts();
 
         assertThat(technicalAccounts).anySatisfy(account -> SoftAssertions.assertSoftly(softly -> {
             softly.assertThat(account.getName()).isEqualTo(TECHNICAL_ACCOUNT.getUsername());
diff --git a/third-party/linshare/src/test/java/org/apache/james/linshare/client/LinshareAPITest.java b/third-party/linshare/src/test/java/org/apache/james/linshare/client/LinshareAPITest.java
index b93e4b8..0ca47af 100644
--- a/third-party/linshare/src/test/java/org/apache/james/linshare/client/LinshareAPITest.java
+++ b/third-party/linshare/src/test/java/org/apache/james/linshare/client/LinshareAPITest.java
@@ -19,60 +19,47 @@
 
 package org.apache.james.linshare.client;
 
-import static io.restassured.RestAssured.given;
+import static org.apache.james.linshare.LinshareExtension.LinshareAPIForUserTesting;
 import static org.apache.james.linshare.LinshareFixture.USER_1;
 import static org.apache.james.linshare.LinshareFixture.USER_2;
-import static org.apache.james.linshare.LinshareFixture.USER_3;
-import static org.apache.james.linshare.LinshareFixture.USER_4;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.hasSize;
 
 import java.io.File;
 import java.nio.file.Files;
-import java.util.Collection;
 import java.util.List;
 
-import org.apache.commons.io.FileUtils;
-import org.apache.james.core.MailAddress;
 import org.apache.james.linshare.LinshareExtension;
 import org.assertj.core.api.SoftAssertions;
-import org.awaitility.Awaitility;
-import org.awaitility.Duration;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
-import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
-
-import com.github.steveash.guavate.Guavate;
 
 import feign.FeignException;
-import io.restassured.specification.RequestSpecification;
 
 class LinshareAPITest {
-    private static final String MESSAGE = "message";
+    private static final String USER1_MAIL = "user1@linshare.org";
+    private static final String USER2_MAIL = "user2@linshare.org";
+
 
     @RegisterExtension
     static LinshareExtension linshareExtension = new LinshareExtension();
 
-    private LinshareAPI user1LinshareAPI;
-    private LinshareAPI user2LinshareAPI;
-    private LinshareAPI user3LinshareAPI;
-    private LinshareAPI user4LinshareAPI;
+    private LinshareAPIForUserTesting user1LinshareAPI;
+    private LinshareAPIForUserTesting user2LinshareAPI;
+    private LinshareAPI technicalLinshareAPI;
 
     @BeforeEach
     void setup() throws Exception {
-        user1LinshareAPI = linshareExtension.getAPIFor(USER_1);
-        user2LinshareAPI = linshareExtension.getAPIFor(USER_2);
-        user3LinshareAPI = linshareExtension.getAPIFor(USER_3);
-        user4LinshareAPI = linshareExtension.getAPIFor(USER_4);
+        user1LinshareAPI = LinshareAPIForUserTesting.from(USER_1);
+        user2LinshareAPI = LinshareAPIForUserTesting.from(USER_2);
+        technicalLinshareAPI = linshareExtension.getDelegationAccountAPI();
     }
 
     @Test
     void uploadDocumentShouldReturnUploaded() throws Exception {
         File uploadFile = templateFile();
-        Document uploadedDocument = user1LinshareAPI.uploadDocument(uploadFile);
+        Document uploadedDocument = uploadDocumentToUserSpace(USER1_MAIL, uploadFile);
 
         SoftAssertions.assertSoftly(softly -> {
             softly.assertThat(uploadedDocument.getName()).isEqualTo(uploadFile.getName());
@@ -82,11 +69,11 @@ class LinshareAPITest {
 
     @Test
     void uploadDocumentShouldMakeListingReturnUploaded() throws Exception {
-        Document uploadedDocument = user1LinshareAPI.uploadDocument(templateFile());
+        Document uploadedDocument = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
 
         assertThat(user1LinshareAPI.listAllDocuments())
             .hasSize(1)
-            .containsExactly(uploadedDocument);
+            .containsExactlyInAnyOrder(uploadedDocument);
     }
 
     @Test
@@ -97,27 +84,27 @@ class LinshareAPITest {
 
     @Test
     void listAllShouldReturnAllUploadedDocuments() throws Exception {
-        Document firstDocument = user1LinshareAPI.uploadDocument(templateFile());
-        Document secondDocument = user1LinshareAPI.uploadDocument(templateFile());
+        Document firstDocument = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
+        Document secondDocument = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
 
         assertThat(user1LinshareAPI.listAllDocuments())
-            .containsExactly(firstDocument, secondDocument);
+            .containsExactlyInAnyOrder(firstDocument, secondDocument);
     }
 
     @Test
     void listAllShouldNotReturnDocumentsOfOtherUsers() throws Exception {
-        Document firstDocument = user1LinshareAPI.uploadDocument(templateFile());
-        Document secondDocument = user1LinshareAPI.uploadDocument(templateFile());
+        Document firstDocument = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
+        Document secondDocument = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
 
-        user2LinshareAPI.uploadDocument(templateFile());
+        uploadDocumentToUserSpace(USER2_MAIL, templateFile());
 
         assertThat(user1LinshareAPI.listAllDocuments())
-            .containsExactly(firstDocument, secondDocument);
+            .containsExactlyInAnyOrder(firstDocument, secondDocument);
     }
 
     @Test
     void deleteShouldDeleteUploadedDocument() throws Exception {
-        Document firstDocument = user1LinshareAPI.uploadDocument(templateFile());
+        Document firstDocument = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
         user1LinshareAPI.delete(firstDocument.getId());
 
         assertThat(user1LinshareAPI.listAllDocuments())
@@ -126,17 +113,17 @@ class LinshareAPITest {
 
     @Test
     void deleteShouldNotDeleteOtherUserDocuments() throws Exception {
-        Document user1Document = user1LinshareAPI.uploadDocument(templateFile());
-        Document user2Document = user2LinshareAPI.uploadDocument(templateFile());
+        Document user1Document = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
+        Document user2Document = uploadDocumentToUserSpace(USER2_MAIL, templateFile());
         user1LinshareAPI.delete(user1Document.getId());
 
         assertThat(user2LinshareAPI.listAllDocuments())
-            .containsExactly(user2Document);
+            .containsExactlyInAnyOrder(user2Document);
     }
 
     @Test
     void deleteShouldReturnErrorWhenDeleteOtherUserDocuments() throws Exception {
-        Document user1Document = user1LinshareAPI.uploadDocument(templateFile());
+        Document user1Document = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
 
         assertThatThrownBy(() -> user2LinshareAPI.delete(user1Document.getId()))
             .isInstanceOf(FeignException.Forbidden.class);
@@ -144,8 +131,8 @@ class LinshareAPITest {
 
     @Test
     void deleteAllShouldClearAllDocumentsOfAnUser() throws Exception {
-        user1LinshareAPI.uploadDocument(templateFile());
-        user1LinshareAPI.uploadDocument(templateFile());
+        uploadDocumentToUserSpace(USER1_MAIL, templateFile());
+        uploadDocumentToUserSpace(USER1_MAIL, templateFile());
 
         user1LinshareAPI.deleteAllDocuments();
 
@@ -154,112 +141,27 @@ class LinshareAPITest {
     }
 
     @Test
-    void shareShouldTriggerAnEmail() throws Exception {
-        Document user1Document = user1LinshareAPI.uploadDocument(templateFile());
-
-        String message = "Very specific message";
-        ShareRequest shareRequest = ShareRequest.builder()
-            .message(message)
-            .addDocumentId(user1Document.getId())
-            .addRecipient(new MailAddress(USER_2.getUsername()))
-            .build();
-
-        user1LinshareAPI.share(shareRequest);
-
-        RequestSpecification request = given(linshareExtension.getLinshare().fakeSmtpRequestSpecification());
-
-        Awaitility.waitAtMost(Duration.TEN_SECONDS)
-            .pollInterval(Duration.ONE_SECOND)
-            .untilAsserted(
-                () -> request
-                    .get("/api/email")
-                .then()
-                    .body("", hasSize(2)));
-
-        request
-            .get("/api/email")
-        .then()
-            .body("[1].subject", containsString("John Doe has shared a file with you"))
-            .body("[1].html", containsString(message));
-    }
-
-
-    @Test
-    void shareShouldShareToTargetedRecipient() throws Exception {
-        Document user1Document = user1LinshareAPI.uploadDocument(templateFile());
+    void uploadDocumentShouldUploadToUserSpace() throws Exception {
+        Document uploadedDocument = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
 
-        ShareRequest shareRequest = ShareRequest.builder()
-            .message(MESSAGE)
-            .addDocumentId(user1Document.getId())
-            .addRecipient(new MailAddress(USER_2.getUsername()))
-            .build();
-
-        user1LinshareAPI.share(shareRequest);
-        assertThat(user2LinshareAPI.receivedShares())
-            .hasSize(1)
-            .allSatisfy(shareReceived -> {
-                Document sharedDoc = shareReceived.getDocument();
-                assertThat(sharedDoc.getName()).isEqualTo(user1Document.getName());
-                assertThat(sharedDoc.getSize()).isEqualTo(user1Document.getSize());
-            });
+        List<Document> user1Documents = user1LinshareAPI.listAllDocuments();
+        assertThat(user1Documents).containsAnyOf(uploadedDocument);
     }
 
     @Test
-    void shareShouldWorkWithMultipleRecipients() throws Exception {
-        Document user1Document = user1LinshareAPI.uploadDocument(templateFile());
-
-        ShareRequest shareRequest = ShareRequest.builder()
-            .message(MESSAGE)
-            .addDocumentId(user1Document.getId())
-            .addRecipient(new MailAddress(USER_2.getUsername()))
-            .addRecipient(new MailAddress(USER_3.getUsername()))
-            .addRecipient(new MailAddress(USER_4.getUsername()))
-            .build();
-
-        user1LinshareAPI.share(shareRequest);
-        List<ReceivedShare> user2Shares = user2LinshareAPI.receivedShares();
-        List<ReceivedShare> user3Shares = user3LinshareAPI.receivedShares();
-        List<ReceivedShare> user4Shares = user4LinshareAPI.receivedShares();
-
-        assertThat(user2Shares)
-            .hasSameSizeAs(user3Shares)
-            .hasSameSizeAs(user4Shares)
-            .hasSize(1);
-
-        assertThat(sharedDocs(user2Shares, user3Shares, user4Shares))
-            .allSatisfy(sharedDoc -> {
-                assertThat(sharedDoc.getName()).isEqualTo(user1Document.getName());
-                assertThat(sharedDoc.getSize()).isEqualTo(user1Document.getSize());
-            });
-    }
-
-    @Test
-    void downloadShareShouldGetUploadedSharedFile() throws Exception {
-        File user1File = templateFile();
-        Document user1Document = user1LinshareAPI.uploadDocument(user1File);
-
-        ShareRequest shareRequest = ShareRequest.builder()
-            .message(MESSAGE)
-            .addDocumentId(user1Document.getId())
-            .addRecipient(new MailAddress(USER_2.getUsername()))
-            .build();
+    void uploadDocumentShouldPerformByDelegationAccount() throws Exception {
+        Document uploadedDocument = uploadDocumentToUserSpace(USER1_MAIL, templateFile());
 
-        user1LinshareAPI.share(shareRequest);
-        Document sharedDoc = user2LinshareAPI.receivedShares().get(0).getDocument();
+        List<Document> user1Documents = user1LinshareAPI.listAllDocuments();
+        assertThat(user1Documents).containsAnyOf(uploadedDocument);
+    }
 
-        byte[] sharedFile =  linshareExtension.downloadSharedFile(USER_2, sharedDoc.getId(), sharedDoc.getName());
-        assertThat(sharedFile).isEqualTo(FileUtils.readFileToByteArray(user1File));
+    private Document uploadDocumentToUserSpace(String targetUserEmail, File userFile) {
+        User targetUser = technicalLinshareAPI.getUserByMail(targetUserEmail);
+        return technicalLinshareAPI.uploadDocumentByDelegation(targetUser, userFile);
     }
 
     private File templateFile() throws Exception {
         return Files.createTempFile("linshare-api-test", ".temp").toFile();
     }
-
-    @SafeVarargs
-    private final List<Document> sharedDocs(List<ReceivedShare>... shares) {
-        return ImmutableList.copyOf(shares).stream()
-            .flatMap(Collection::stream)
-            .map(ReceivedShare::getDocument)
-            .collect(Guavate.toImmutableList());
-    }
 }
diff --git a/third-party/linshare/src/test/java/org/apache/james/linshare/client/TechnicalAccountCreationRequest.java b/third-party/linshare/src/test/java/org/apache/james/linshare/client/TechnicalAccountCreationRequest.java
index ad52803..f92c2b3 100644
--- a/third-party/linshare/src/test/java/org/apache/james/linshare/client/TechnicalAccountCreationRequest.java
+++ b/third-party/linshare/src/test/java/org/apache/james/linshare/client/TechnicalAccountCreationRequest.java
@@ -72,5 +72,4 @@ public class TechnicalAccountCreationRequest {
     public String getRole() {
         return role;
     }
-
 }
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index c1ca041..e77d568 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -16,6 +16,7 @@ Changes to apply between 3.4.x and 3.5.x will be reported here.
 
 Change list:
 
+ - [LinShare blob export mechanism should rely on delegation](#LinShare blob export mechanism should rely on delegation)
  - [RabbitMQ minimal version](#rabbitmq-minimal-version)
  - [Enforce usernames to be lower cased](#enforce-usernames-to-be-lower-cased)
  - [Cassandra keyspace creation configuration](#cassandra-keyspace-creation-configuration)
@@ -39,6 +40,26 @@ Even if this set of characters should be allowed for the local part of a Usernam
 
 However, the read of Usernames already existing with some of those characters is still allowed, to not introduce any breaking change.
 
+### LinShare blob export mechanism should rely on delegation
+Date 12/02/2020
+
+SHA-1 XXX
+
+Concerned products: Guice server, experimental LinShare blob export feature.
+
+JIRA: https://issues.apache.org/jira/browse/JAMES-3040
+
+Blob Export Configuration changed:
+
+File configuration need to be adjusted: `blob.properties`
+
+-You need to add new mandatory properties when `blob.export.implementation` property is set to `linshare`:
+```
+blob.export.linshare.technical.account.uuid
+blob.export.linshare.technical.account.password
+```
+
+-The legacy property `blob.export.linshare.token` will not used anymore, you can remove it.
 ### Hybrid blobStore replaces Union blobStore
 
 Date 6/01/2020


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org