JAMES-2162 Allow to update "sharedWith" JMAP mailbox property
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/69d783e6 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/69d783e6 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/69d783e6 Branch: refs/heads/master Commit: 69d783e6ff1fcf0c8cb863332be0cd215e0c53d4 Parents: 0ac2d41 Author: benwa <btell...@linagora.com> Authored: Wed Sep 27 17:44:46 2017 +0700 Committer: benwa <btell...@linagora.com> Committed: Tue Oct 3 07:52:12 2017 +0700 ---------------------------------------------------------------------- .../integration/SetMailboxesMethodTest.java | 478 ++++++++++++++++--- .../james/jmap/json/ObjectMapperFactory.java | 22 + .../methods/SetMailboxesUpdateProcessor.java | 6 +- .../model/mailbox/MailboxUpdateRequest.java | 32 +- .../apache/james/jmap/model/mailbox/Rights.java | 1 - .../jmap/json/ObjectMapperFactoryTest.java | 57 +++ 6 files changed, 519 insertions(+), 77 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/69d783e6/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java index 87d0c94..cc48707 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMailboxesMethodTest.java @@ -32,6 +32,7 @@ import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; @@ -43,20 +44,23 @@ import org.apache.james.GuiceJamesServer; import org.apache.james.jmap.DefaultMailboxes; import org.apache.james.jmap.HttpJmapAuthentication; import org.apache.james.jmap.api.access.AccessToken; +import org.apache.james.mailbox.model.MailboxACL.Right; import org.apache.james.mailbox.model.MailboxConstants; import org.apache.james.mailbox.model.MailboxId; import org.apache.james.mailbox.store.mail.model.Mailbox; import org.apache.james.mailbox.store.probe.MailboxProbe; import org.apache.james.modules.MailboxProbeImpl; import org.apache.james.probe.DataProbe; -import org.apache.james.utils.JmapGuiceProbe; import org.apache.james.utils.DataProbeImpl; +import org.apache.james.utils.JmapGuiceProbe; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.jayway.restassured.RestAssured; import com.jayway.restassured.builder.RequestSpecBuilder; import com.jayway.restassured.http.ContentType; @@ -65,7 +69,13 @@ public abstract class SetMailboxesMethodTest { private static final String NAME = "[0][0]"; private static final String ARGUMENTS = "[0][1]"; + private static final String FIRST_MAILBOX = ARGUMENTS + ".list[0]"; private static final String USERS_DOMAIN = "domain.tld"; + + private static final String ADMINISTER = String.valueOf(Right.Administer.asCharacter()); + private static final String WRITE = String.valueOf(Right.Write.asCharacter()); + private static final String DELETE_MESSAGES = String.valueOf(Right.DeleteMessages.asCharacter()); + private static int MAILBOX_NAME_LENGTH_64K = 65536; protected abstract GuiceJamesServer createJmapServer(); @@ -135,11 +145,11 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) - .when() + .when() .post("/jmap") - .then() + .then() .statusCode(200) .body(NAME, equalTo("mailboxesSet")) .body(ARGUMENTS + ".notCreated", aMapWithSize(1)) @@ -169,7 +179,7 @@ public abstract class SetMailboxesMethodTest { " ]" + "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -201,7 +211,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -231,7 +241,7 @@ public abstract class SetMailboxesMethodTest { " ]" + "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -260,7 +270,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -292,7 +302,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap"); @@ -322,7 +332,7 @@ public abstract class SetMailboxesMethodTest { " ]" + "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .post("/jmap"); @@ -346,7 +356,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -373,7 +383,7 @@ public abstract class SetMailboxesMethodTest { "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .post("/jmap"); @@ -395,7 +405,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -422,7 +432,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -444,7 +454,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -472,7 +482,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -500,7 +510,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -527,7 +537,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -554,13 +564,13 @@ public abstract class SetMailboxesMethodTest { "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .then() .post("/jmap"); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {}, \"#0\"]]") .when() .post("/jmap") @@ -574,7 +584,7 @@ public abstract class SetMailboxesMethodTest { public void setMailboxesShouldReturnCreatedMailboxWhenChildOfInboxMailbox() { String inboxId = with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {}, \"#0\"]]") .when() .post("/jmap") @@ -599,7 +609,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -632,13 +642,13 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap"); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {}, \"#0\"]]") .when() .post("/jmap") @@ -666,7 +676,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -700,7 +710,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -737,7 +747,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -771,7 +781,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -806,7 +816,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -841,7 +851,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -871,7 +881,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -896,7 +906,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -904,7 +914,7 @@ public abstract class SetMailboxesMethodTest { .statusCode(200); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {}, \"#0\"]]") .when() .post("/jmap") @@ -928,7 +938,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -958,7 +968,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -986,7 +996,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1018,7 +1028,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1047,7 +1057,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1082,7 +1092,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1113,7 +1123,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1143,7 +1153,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1154,6 +1164,344 @@ public abstract class SetMailboxesMethodTest { } @Test + public void updateShouldReturnOkWhenClearingSharedWith() { + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + String requestBody = + "[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + given() + .header("Authorization", accessToken.serialize()) + .body(requestBody) + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxesSet")) + .body(ARGUMENTS + ".updated", contains(mailboxId)); + } + + @Test + public void updateShouldReturnOkWhenSettingNewACL() { + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + + given() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"user\": [\"a\", \"w\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxesSet")) + .body(ARGUMENTS + ".updated", contains(mailboxId)); + } + + @Test + public void updateShouldRejectInvalidRights() { + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + + given() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"user\": [\"aw\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .when() + .post("/jmap").prettyPeek() + .then() + .statusCode(200) + .body(NAME, equalTo("error")) + .body(ARGUMENTS + ".type", is("invalidArguments")) + .body(ARGUMENTS + ".description", containsString("Rights should be represented as single value characters")); + } + + @Test + public void updateShouldRejectUnhandledRight() { + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + + given() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"user\": [\"p\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("error")) + .body(ARGUMENTS + ".type", is("invalidArguments")) + .body(ARGUMENTS + ".description", containsString("No matching right for 'p'")); + } + + @Test + public void updateShouldRejectNonExistingRights() { + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + + given() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"user\": [\"z\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("error")) + .body(ARGUMENTS + ".type", is("invalidArguments")) + .body(ARGUMENTS + ".description", containsString("No matching right for 'z'")); + } + + @Test + public void updateShouldApplyWhenSettingNewACL() { + String myBox = "myBox"; + String user = "user"; + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, myBox); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + + with() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"" + user + "\": [\"a\", \"w\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .post("/jmap"); + + given() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(FIRST_MAILBOX + ".name", equalTo(myBox)) + .body(FIRST_MAILBOX + ".sharedWith", hasEntry(user, ImmutableList.of(ADMINISTER, WRITE))); + } + + @Test + public void updateShouldModifyStoredDataWhenUpdatingACL() { + String myBox = "myBox"; + String user = "user"; + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, myBox); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + + with() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"" + user + "\": [\"a\", \"w\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .post("/jmap"); + + with() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"" + user + "\": [\"a\", \"t\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .post("/jmap"); + + given() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(FIRST_MAILBOX + ".name", equalTo(myBox)) + .body(FIRST_MAILBOX + ".sharedWith", hasEntry(user, ImmutableList.of(ADMINISTER, DELETE_MESSAGES))); + } + + @Test + public void updateShouldClearStoredDataWhenDeleteACL() { + String myBox = "myBox"; + String user = "user"; + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, myBox); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + + with() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"" + user + "\": [\"a\", \"w\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .post("/jmap"); + + with() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .post("/jmap"); + + given() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(FIRST_MAILBOX + ".name", equalTo(myBox)) + .body(FIRST_MAILBOX + ".sharedWith", is(ImmutableMap.of())); + } + + @Test + public void updateShouldModifyStoredDataWhenSwitchingACLUser() { + String myBox = "myBox"; + String user1 = "user1"; + String user2 = "user2"; + jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, myBox); + Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); + String mailboxId = mailbox.getMailboxId().serialize(); + + with() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"" + user1 + "\": [\"a\", \"w\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .post("/jmap"); + + with() + .header("Authorization", accessToken.serialize()) + .body("[" + + " [ \"setMailboxes\"," + + " {" + + " \"update\": {" + + " \"" + mailboxId + "\" : {" + + " \"sharedWith\" : {\"" + user2 + "\": [\"a\", \"w\"]}" + + " }" + + " }" + + " }," + + " \"#0\"" + + " ]" + + "]") + .post("/jmap"); + + given() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(FIRST_MAILBOX + ".name", equalTo(myBox)) + .body(FIRST_MAILBOX + ".sharedWith", hasEntry(user2, ImmutableList.of(ADMINISTER, WRITE))); + } + + @Test public void setMailboxesShouldUpdateMailboxNameWhenNameUpdateAskedOnExistingMailbox() { jmapServer.getProbe(MailboxProbeImpl.class).createMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); Mailbox mailbox = jmapServer.getProbe(MailboxProbeImpl.class).getMailbox(MailboxConstants.USER_NAMESPACE, username, "myBox"); @@ -1173,12 +1521,12 @@ public abstract class SetMailboxesMethodTest { "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .post("/jmap"); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") .when() .post("/jmap") @@ -1214,7 +1562,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1249,12 +1597,12 @@ public abstract class SetMailboxesMethodTest { "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .post("/jmap"); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") .when() .post("/jmap") @@ -1292,7 +1640,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1329,12 +1677,12 @@ public abstract class SetMailboxesMethodTest { "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .post("/jmap"); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") .when() .post("/jmap") @@ -1368,7 +1716,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1401,12 +1749,12 @@ public abstract class SetMailboxesMethodTest { "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .post("/jmap"); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") .when() .post("/jmap") @@ -1445,7 +1793,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1483,12 +1831,12 @@ public abstract class SetMailboxesMethodTest { "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .post("/jmap"); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") .when() .post("/jmap") @@ -1520,7 +1868,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1553,7 +1901,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1595,7 +1943,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1630,7 +1978,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1665,7 +2013,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1698,12 +2046,12 @@ public abstract class SetMailboxesMethodTest { "]"; with() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .post("/jmap"); given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") .when() .post("/jmap") @@ -1735,7 +2083,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1769,7 +2117,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") @@ -1810,7 +2158,7 @@ public abstract class SetMailboxesMethodTest { "]"; given() - .header("Authorization", this.accessToken.serialize()) + .header("Authorization", accessToken.serialize()) .body(requestBody) .when() .post("/jmap") http://git-wip-us.apache.org/repos/asf/james-project/blob/69d783e6/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/ObjectMapperFactory.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/ObjectMapperFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/ObjectMapperFactory.java index 71a1b2b..c48527e 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/ObjectMapperFactory.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/ObjectMapperFactory.java @@ -24,6 +24,7 @@ import java.util.Set; import javax.inject.Inject; +import org.apache.james.jmap.model.mailbox.Rights; import org.apache.james.mailbox.model.MailboxId; import org.apache.james.mailbox.model.MessageId; @@ -43,6 +44,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.datatype.guava.GuavaModule; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; public class ObjectMapperFactory { @@ -66,6 +68,8 @@ public class ObjectMapperFactory { mailboxIdModule.addSerializer(MessageId.class, new MessageIdSerializer()); mailboxIdModule.addKeyDeserializer(MessageId.class, new MessageIdKeyDeserializer(messageIdFactory)); mailboxIdModule.addKeySerializer(MessageId.class, new MessageIdKeySerializer()); + mailboxIdModule.addKeyDeserializer(Rights.Username.class, new UsernameKeyDeserializer()); + mailboxIdModule.addDeserializer(Rights.Right.class, new RightDeserializer()); jacksonModules = JACKSON_BASE_MODULES.add(mailboxIdModule).build(); } @@ -102,6 +106,24 @@ public class ObjectMapperFactory { } } + public static class UsernameKeyDeserializer extends KeyDeserializer { + @Override + public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException { + return new Rights.Username(key); + } + } + + public static class RightDeserializer extends JsonDeserializer<Rights.Right> { + + @Override + public Rights.Right deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + String nodeValue = p.getValueAsString(); + Preconditions.checkArgument(nodeValue.length() == 1, "Rights should be represented as single value characters"); + + return Rights.Right.forChar(nodeValue.charAt(0)); + } + } + public static class MailboxIdKeyDeserializer extends KeyDeserializer { private MailboxId.Factory factory; http://git-wip-us.apache.org/repos/asf/james-project/blob/69d783e6/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesUpdateProcessor.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesUpdateProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesUpdateProcessor.java index 459753c..0e2ad14 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesUpdateProcessor.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMailboxesUpdateProcessor.java @@ -83,8 +83,7 @@ public class SetMailboxesUpdateProcessor implements SetMailboxesProcessor { SetMailboxesResponse.Builder responseBuilder = SetMailboxesResponse.builder(); request.getUpdate() - .entrySet() - .forEach(update -> handleUpdate(update.getKey(), update.getValue(), responseBuilder, mailboxSession)); + .forEach((key, value) -> handleUpdate(key, value, responseBuilder, mailboxSession)); timeMetric.stopAndPublish(); return responseBuilder.build(); } @@ -210,6 +209,9 @@ public class SetMailboxesUpdateProcessor implements SetMailboxesProcessor { private void updateMailbox(Mailbox mailbox, MailboxUpdateRequest updateRequest, MailboxSession mailboxSession) throws MailboxException { MailboxPath originMailboxPath = mailboxManager.getMailbox(mailbox.getId(), mailboxSession).getMailboxPath(); MailboxPath destinationMailboxPath = computeNewMailboxPath(mailbox, originMailboxPath, updateRequest, mailboxSession); + if (updateRequest.getSharedWith().isPresent()) { + mailboxManager.setRights(originMailboxPath, updateRequest.getSharedWith().get().toMailboxAcl(), mailboxSession); + } if (!originMailboxPath.equals(destinationMailboxPath)) { mailboxManager.renameMailbox(originMailboxPath, destinationMailboxPath, mailboxSession); http://git-wip-us.apache.org/repos/asf/james-project/blob/69d783e6/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/MailboxUpdateRequest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/MailboxUpdateRequest.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/MailboxUpdateRequest.java index 65af555..81cfc4a 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/MailboxUpdateRequest.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/MailboxUpdateRequest.java @@ -46,12 +46,14 @@ public class MailboxUpdateRequest { private Optional<MailboxId> parentId; private Optional<Role> role; private Optional<SortOrder> sortOrder; + private Optional<Rights> sharedWith; private Builder() { name = Optional.empty(); role = Optional.empty(); sortOrder = Optional.empty(); parentId = Optional.empty(); + sharedWith = Optional.empty(); } public Builder name(String name) throws MailboxException { @@ -80,9 +82,14 @@ public class MailboxUpdateRequest { throw new NotImplementedException(); } + public Builder sharedWith(Rights rights) { + Preconditions.checkNotNull(rights); + this.sharedWith = Optional.of(rights); + return this; + } public MailboxUpdateRequest build() { - return new MailboxUpdateRequest(name, parentId, role, sortOrder); + return new MailboxUpdateRequest(name, parentId, role, sortOrder, sharedWith); } } @@ -90,14 +97,16 @@ public class MailboxUpdateRequest { private final Optional<MailboxId> parentId; private final Optional<Role> role; private final Optional<SortOrder> sortOrder; + private final Optional<Rights> sharedWith; @VisibleForTesting - MailboxUpdateRequest(Optional<String> name, Optional<MailboxId> parentId, Optional<Role> role, Optional<SortOrder> sortOrder) { + MailboxUpdateRequest(Optional<String> name, Optional<MailboxId> parentId, Optional<Role> role, Optional<SortOrder> sortOrder, Optional<Rights> sharedWith) { this.name = name; this.parentId = parentId; this.role = role; this.sortOrder = sortOrder; + this.sharedWith = sharedWith; } public Optional<String> getName() { @@ -116,6 +125,9 @@ public class MailboxUpdateRequest { return sortOrder; } + public Optional<Rights> getSharedWith() { + return sharedWith; + } @Override public final boolean equals(Object obj) { @@ -124,23 +136,25 @@ public class MailboxUpdateRequest { return Objects.equals(this.name, other.name) && Objects.equals(this.parentId, other.parentId) && Objects.equals(this.role, other.role) - && Objects.equals(this.sortOrder, other.sortOrder); + && Objects.equals(this.sortOrder, other.sortOrder) + && Objects.equals(this.sharedWith, other.sharedWith); } return false; } @Override public final int hashCode() { - return Objects.hash(name, parentId, role, sortOrder); + return Objects.hash(name, parentId, role, sortOrder, sharedWith); } @Override public String toString() { return MoreObjects.toStringHelper(getClass()) - .add("name", name) - .add("parentId", parentId) - .add("role", role) - .add("sortOrder", sortOrder) - .toString(); + .add("name", name) + .add("parentId", parentId) + .add("role", role) + .add("sortOrder", sortOrder) + .add("sharedWith", sharedWith) + .toString(); } } http://git-wip-us.apache.org/repos/asf/james-project/blob/69d783e6/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/Rights.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/Rights.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/Rights.java index d44e4d3..bd27bd0 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/Rights.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/Rights.java @@ -89,7 +89,6 @@ public class Rights { public static class Username { private final String value; - // @JsonCreator public Username(String value) { this.value = value; } http://git-wip-us.apache.org/repos/asf/james-project/blob/69d783e6/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ObjectMapperFactoryTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ObjectMapperFactoryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ObjectMapperFactoryTest.java index 0985081..9e892fc 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ObjectMapperFactoryTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ObjectMapperFactoryTest.java @@ -19,9 +19,11 @@ package org.apache.james.jmap.json; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.Map; +import org.apache.james.jmap.model.mailbox.Rights; import org.apache.james.mailbox.inmemory.InMemoryId; import org.apache.james.mailbox.inmemory.InMemoryMessageId; import org.apache.james.mailbox.model.MailboxId; @@ -72,6 +74,61 @@ public class ObjectMapperFactoryTest { assertThat(actual).isEqualTo(expectedJson); } + @Test + public void readValueShouldParseRightObject() throws Exception { + Rights.Right actual = testee.forParsing() + .readValue("\"a\"", Rights.Right.class); + + assertThat(actual) + .isEqualTo(Rights.Right.Administer); + } + + @Test + public void readValueShouldParseUsernameObject() throws Exception { + String username = "username"; + Rights.Username actual = testee.forParsing() + .readValue("\"" + username + "\"", Rights.Username.class); + + assertThat(actual) + .isEqualTo(new Rights.Username(username)); + } + + @Test + public void readValueShouldParseRightsObject() throws Exception { + String username = "username"; + Rights actual = testee.forParsing() + .readValue("{\"" + username + "\" : [\"a\", \"e\"]}", Rights.class); + + assertThat(actual) + .isEqualTo(Rights.builder() + .delegateTo(new Rights.Username(username), Rights.Right.Administer, Rights.Right.Expunge) + .build()); + } + + @Test + public void readValueShouldRejectMultiCharacterRights() throws Exception { + assertThatThrownBy(() -> + testee.forParsing() + .readValue("\"ae\"", Rights.Right.class)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void readValueShouldRejectUnsupportedRights() throws Exception { + assertThatThrownBy(() -> + testee.forParsing() + .readValue("\"p\"", Rights.Right.class)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void readValueShouldRejectUnExistingRights() throws Exception { + assertThatThrownBy(() -> + testee.forParsing() + .readValue("\"z\"", Rights.Right.class)) + .isInstanceOf(IllegalArgumentException.class); + } + public static class MailboxIdTestContainer { public MailboxId mailboxId; --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org