JAMES-2162 Introduce JMAP Rights object

Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/672f24f1
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/672f24f1
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/672f24f1

Branch: refs/heads/master
Commit: 672f24f1e1034d02f9e11bfae0674a5d8409272d
Parents: 6e9ad73
Author: benwa <[email protected]>
Authored: Wed Sep 27 17:43:00 2017 +0700
Committer: benwa <[email protected]>
Committed: Tue Oct 3 07:52:07 2017 +0700

----------------------------------------------------------------------
 .../apache/james/mailbox/model/MailboxACL.java  |   5 +
 .../apache/james/jmap/model/mailbox/Rights.java | 240 +++++++++++++++++++
 .../james/jmap/model/mailbox/RightsTest.java    | 164 +++++++++++++
 3 files changed, 409 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/672f24f1/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
----------------------------------------------------------------------
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
index d1e1487..a831302 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxACL.java
@@ -333,6 +333,11 @@ public class MailboxACL {
         public Entry(String key, String value) throws 
UnsupportedRightException {
             this(EntryKey.deserialize(key), 
Rfc4314Rights.fromSerializedRfc4314Rights(value));
         }
+
+        public Entry(String key, Right... rights) throws 
UnsupportedRightException {
+            this(EntryKey.deserialize(key), new Rfc4314Rights(rights));
+        }
+
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/james-project/blob/672f24f1/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
new file mode 100644
index 0000000..d44e4d3
--- /dev/null
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/mailbox/Rights.java
@@ -0,0 +1,240 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.mailbox;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.BinaryOperator;
+
+import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.MailboxACL.EntryKey;
+import org.apache.james.mailbox.model.MailboxACL.Rfc4314Rights;
+import org.apache.james.util.GuavaUtils;
+import org.apache.james.util.OptionalUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+
+public class Rights {
+    public enum Right {
+        Administer(MailboxACL.Right.Administer),
+        Expunge(MailboxACL.Right.PerformExpunge),
+        Insert(MailboxACL.Right.Insert),
+        Lookup(MailboxACL.Right.Lookup),
+        Read(MailboxACL.Right.Read),
+        Seen(MailboxACL.Right.WriteSeenFlag),
+        DeleteMessages(MailboxACL.Right.DeleteMessages),
+        Write(MailboxACL.Right.Write);
+
+        private final MailboxACL.Right right;
+
+        Right(MailboxACL.Right right) {
+            this.right = right;
+        }
+
+        @JsonValue
+        public char asCharacter() {
+            return right.asCharacter();
+        }
+
+        public MailboxACL.Right toMailboxRight() {
+            return right;
+        }
+
+        public static Optional<Right> forRight(MailboxACL.Right right) {
+            return OptionalUtils.peekOnEmpty(
+                Arrays.stream(values())
+                    .filter(jmapRight -> jmapRight.right == right)
+                    .findAny(),
+                () -> LOGGER.warn("Non handled right '" + right + "'"));
+        }
+
+        public static Right forChar(char c) {
+            return Arrays.stream(values())
+                .filter(right -> right.asCharacter() == c)
+                .findAny()
+                .orElseThrow(() -> new IllegalArgumentException("No matching 
right for '" + c + "'"));
+        }
+    }
+
+    public static class Username {
+        private final String value;
+
+     //   @JsonCreator
+        public Username(String value) {
+            this.value = value;
+        }
+
+        @JsonValue
+        public String getValue() {
+            return value;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof Username) {
+                Username username = (Username) o;
+
+                return Objects.equals(this.value, username.value);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(value);
+        }
+    }
+
+    public static class Builder {
+        private Multimap<Username, Right> rights;
+
+        public Builder() {
+            rights = ArrayListMultimap.create();
+        }
+
+        public Builder delegateTo(Username username, Right... rights) {
+            delegateTo(username, Arrays.asList(rights));
+            return this;
+        }
+
+        public Builder delegateTo(Username username, Collection<Right> rights) 
{
+            this.rights.putAll(username, rights);
+            return this;
+        }
+
+        public Builder combine(Builder builder) {
+            this.rights.putAll(builder.rights);
+            return this;
+        }
+
+        public Rights build() {
+            return new Rights(rights);
+        }
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static Rights fromACL(MailboxACL acl) {
+        return acl.getEntries()
+            .entrySet()
+            .stream()
+            .filter(entry -> isSupported(entry.getKey()))
+            .map(Rights::toRightsBuilder)
+            .reduce(builder(), Builder::combine)
+            .build();
+    }
+
+    private static Builder toRightsBuilder(Map.Entry<EntryKey, 
MailboxACL.Rfc4314Rights> entry) {
+        return builder().delegateTo(
+            new Username(entry.getKey().getName()),
+            fromACL(entry.getValue()));
+    }
+
+    private static List<Right> fromACL(MailboxACL.Rfc4314Rights rights) {
+        return rights.list()
+            .stream()
+            .flatMap(right -> OptionalUtils.toStream(Right.forRight(right)))
+            .collect(Guavate.toImmutableList());
+    }
+
+    private static boolean isSupported(EntryKey key) {
+        if (key.isNegative()) {
+            LOGGER.info("Negative keys are not supported");
+            return false;
+        }
+        if (key.getNameType() != MailboxACL.NameType.user) {
+            LOGGER.info(key.getNameType() + " is not supported. Only 'user' 
is.");
+            return false;
+        }
+        return true;
+    }
+
+    public static final Rights EMPTY = new Rights(ArrayListMultimap.create());
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(Rights.class);
+
+    private final Multimap<Username, Right> rights;
+
+    @JsonCreator
+    public Rights(Map<Username, List<Right>> rights) {
+        this(GuavaUtils.toMultimap(rights));
+    }
+
+    private Rights(Multimap<Username, Right> rights) {
+        this.rights = rights;
+    }
+
+    @JsonAnyGetter
+    public Map<Username, Collection<Right>> getRights() {
+        return rights.asMap();
+    }
+
+    public MailboxACL toMailboxAcl() {
+        BinaryOperator<MailboxACL> union = 
Throwing.binaryOperator(MailboxACL::union);
+
+        return rights.asMap()
+            .entrySet()
+            .stream()
+            .map(entry -> new MailboxACL(
+                ImmutableMap.of(
+                    EntryKey.createUser(entry.getKey().value),
+                    toMailboxAclRights(entry.getValue()))))
+            .reduce(MailboxACL.EMPTY, union);
+    }
+
+    private Rfc4314Rights toMailboxAclRights(Collection<Right> rights) {
+        BinaryOperator<Rfc4314Rights> union = 
Throwing.binaryOperator(Rfc4314Rights::union);
+
+        return rights.stream()
+            .map(Right::toMailboxRight)
+            .map(Throwing.function(Rfc4314Rights::new))
+            .reduce(new Rfc4314Rights(), union);
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof Rights) {
+            Rights that = (Rights) o;
+
+            return Objects.equals(this.rights, that.rights);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(rights);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/672f24f1/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/mailbox/RightsTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/mailbox/RightsTest.java
 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/mailbox/RightsTest.java
new file mode 100644
index 0000000..dae6130
--- /dev/null
+++ 
b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/mailbox/RightsTest.java
@@ -0,0 +1,164 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.model.mailbox;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.MailboxACL.Entry;
+import org.apache.james.mailbox.model.MailboxACL.EntryKey;
+import org.apache.james.mailbox.model.MailboxACL.Rfc4314Rights;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class RightsTest {
+
+    public static final boolean NEGATIVE = true;
+
+    @Test
+    public void rightsShouldMatchBeanContract() {
+        EqualsVerifier.forClass(Rights.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void usernameShouldMatchBeanContract() {
+        EqualsVerifier.forClass(Rights.Username.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void forCharShouldReturnRightWhenA() {
+        assertThat(Rights.Right.forChar('a'))
+            .isEqualTo(Rights.Right.Administer);
+    }
+
+    @Test
+    public void forCharShouldReturnRightWhenE() {
+        assertThat(Rights.Right.forChar('e'))
+            .isEqualTo(Rights.Right.Expunge);
+    }
+
+    @Test
+    public void forCharShouldReturnRightWhenI() {
+        assertThat(Rights.Right.forChar('i'))
+            .isEqualTo(Rights.Right.Insert);
+    }
+
+    @Test
+    public void forCharShouldReturnRightWhenL() {
+        assertThat(Rights.Right.forChar('l'))
+            .isEqualTo(Rights.Right.Lookup);
+    }
+
+    @Test
+    public void forCharShouldReturnRightWhenR() {
+        assertThat(Rights.Right.forChar('r'))
+            .isEqualTo(Rights.Right.Read);
+    }
+
+    @Test
+    public void forCharShouldReturnRightWhenW() {
+        assertThat(Rights.Right.forChar('w'))
+            .isEqualTo(Rights.Right.Write);
+    }
+
+    @Test
+    public void forCharShouldReturnRightWhenT() {
+        assertThat(Rights.Right.forChar('t'))
+            .isEqualTo(Rights.Right.DeleteMessages);
+    }
+
+    @Test
+    public void forCharShouldThrowOnUnsupportedRight() {
+        assertThatThrownBy(() -> Rights.Right.forChar('k'))
+            .isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void fromACLShouldFilterOutGroups() throws Exception {
+        MailboxACL acl = new MailboxACL(ImmutableMap.of(
+            EntryKey.createGroup("group"), 
Rfc4314Rights.fromSerializedRfc4314Rights("aet")));
+
+        assertThat(Rights.fromACL(acl))
+            .isEqualTo(Rights.EMPTY);
+    }
+
+    @Test
+    public void fromACLShouldFilterNegatedUsers() throws Exception {
+        MailboxACL acl = new MailboxACL(ImmutableMap.of(
+            EntryKey.createUser("user", NEGATIVE), 
Rfc4314Rights.fromSerializedRfc4314Rights("aet")));
+
+        assertThat(Rights.fromACL(acl))
+            .isEqualTo(Rights.EMPTY);
+    }
+
+    @Test
+    public void fromACLShouldAcceptUsers() throws Exception {
+        MailboxACL acl = new MailboxACL(ImmutableMap.of(
+            EntryKey.createUser("user"), 
Rfc4314Rights.fromSerializedRfc4314Rights("aet")));
+
+        assertThat(Rights.fromACL(acl))
+            .isEqualTo(Rights.builder()
+                .delegateTo(new Rights.Username("user"), 
Rights.Right.Administer, Rights.Right.Expunge, Rights.Right.DeleteMessages)
+                .build());
+    }
+
+    @Test
+    public void fromACLShouldFilterOutUnknownRights() throws Exception {
+        MailboxACL acl = new MailboxACL(ImmutableMap.of(
+            EntryKey.createUser("user"), 
Rfc4314Rights.fromSerializedRfc4314Rights("aetpk")));
+
+        assertThat(Rights.fromACL(acl))
+            .isEqualTo(Rights.builder()
+                .delegateTo(new Rights.Username("user"), 
Rights.Right.Administer, Rights.Right.Expunge, Rights.Right.DeleteMessages)
+                .build());
+    }
+
+    @Test
+    public void toMailboxAclShouldReturnEmptyAclWhenEmpty() {
+        Rights rights = Rights.EMPTY;
+
+        assertThat(rights.toMailboxAcl())
+            .isEqualTo(new MailboxACL());
+    }
+
+    @Test
+    public void toMailboxAclShouldReturnAclConversion() throws Exception {
+        String user1 = "user1";
+        String user2 = "user2";
+        Rights rights = Rights.builder()
+            .delegateTo(new Rights.Username(user1), Rights.Right.Administer, 
Rights.Right.DeleteMessages)
+            .delegateTo(new Rights.Username(user2), Rights.Right.Expunge, 
Rights.Right.Lookup)
+            .build();
+
+        assertThat(rights.toMailboxAcl())
+            .isEqualTo(new MailboxACL(
+                new Entry(user1, MailboxACL.Right.Administer, 
MailboxACL.Right.DeleteMessages),
+                new Entry(user2, MailboxACL.Right.PerformExpunge, 
MailboxACL.Right.Lookup)));
+    }
+
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to