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 ma...@apache.org on 2017/09/29 07:22:02 UTC
[24/31] james-project git commit: MAILBOX-307 Removing unneeded
interface-sception for ACLs
MAILBOX-307 Removing unneeded interface-sception for ACLs
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/4e10f10b
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/4e10f10b
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/4e10f10b
Branch: refs/heads/master
Commit: 4e10f10b6e5b67972bd5b9cbdfaeb1b3eb7dcef9
Parents: 91d303b
Author: benwa <bt...@linagora.com>
Authored: Wed Sep 27 09:41:23 2017 +0700
Committer: Matthieu Baechler <ma...@apache.org>
Committed: Fri Sep 29 09:20:40 2017 +0200
----------------------------------------------------------------------
.../apache/james/mailbox/MailboxManager.java | 11 +-
.../james/mailbox/acl/MailboxACLResolver.java | 11 +-
.../mailbox/acl/UnionMailboxACLResolver.java | 94 ++-
.../exception/UnsupportedRightException.java | 4 +-
.../apache/james/mailbox/model/MailboxACL.java | 823 +++++++++++++++----
.../james/mailbox/model/SimpleMailboxACL.java | 679 ---------------
.../acl/UnionMailboxACLResolverTest.java | 611 +++++++-------
.../mailbox/model/MailboxACLEntryKeyTest.java | 157 ++++
.../james/mailbox/model/MailboxACLTest.java | 224 +++++
.../james/mailbox/model/Rfc4314RightsTest.java | 75 +-
.../model/SimpleMailboxACLEntryKeyTest.java | 157 ----
.../mailbox/model/SimpleMailboxACLTest.java | 226 -----
.../mailbox/caching/CachingMailboxMapper.java | 3 +-
.../cassandra/CassandraMailboxManager.java | 4 +-
.../cassandra/mail/CassandraACLMapper.java | 13 +-
.../cassandra/mail/CassandraMailboxMapper.java | 2 +-
.../cassandra/mail/CassandraACLMapperTest.java | 91 +-
.../mailbox/hbase/mail/HBaseMailboxMapper.java | 2 +-
.../mailbox/hbase/mail/model/HBaseMailbox.java | 3 +-
.../mailbox/jcr/mail/JCRMailboxMapper.java | 2 +-
.../mailbox/jcr/mail/model/JCRMailbox.java | 3 +-
.../mailbox/jpa/mail/JPAMailboxMapper.java | 2 +-
.../mailbox/jpa/mail/model/JPAMailbox.java | 3 +-
.../jpa/mail/TransactionalMailboxMapper.java | 3 +-
.../LuceneMailboxMessageSearchIndexTest.java | 3 +-
.../james/mailbox/maildir/MaildirFolder.java | 17 +-
.../maildir/mail/MaildirMailboxMapper.java | 2 +-
.../inmemory/mail/InMemoryMailboxMapper.java | 2 +-
.../mailbox/store/StoreMailboxManager.java | 25 +-
.../mailbox/store/StoreMessageManager.java | 7 +-
.../json/SimpleMailboxACLJsonConverter.java | 11 +-
.../james/mailbox/store/mail/MailboxMapper.java | 2 +-
.../store/mail/model/impl/SimpleMailbox.java | 5 +-
.../store/TestMailboxSessionMapperFactory.java | 2 +-
.../store/json/MailboxACLJsonConverterTest.java | 140 ++++
.../json/SimpleMailboxACLJsonConverterTest.java | 139 ----
.../store/mail/model/ListMailboxAssertTest.java | 2 +-
.../store/mail/model/MailboxMapperACLTest.java | 103 ++-
.../mpt/imapmailbox/GrantRightsOnHost.java | 2 +-
.../mpt/imapmailbox/suite/ACLCommands.java | 5 +-
.../mpt/imapmailbox/suite/ACLIntegration.java | 68 +-
.../suite/ACLScriptedTestProtocol.java | 7 +-
.../cyrus/host/GrantRightsOnCyrusHost.java | 5 +-
.../james/imap/encode/ACLResponseEncoder.java | 8 +-
.../imap/encode/ListRightsResponseEncoder.java | 6 +-
.../imap/encode/MyRightsResponseEncoder.java | 4 +-
.../imap/message/response/ACLResponse.java | 16 +-
.../message/response/ListRightsResponse.java | 10 +-
.../imap/message/response/MyRightsResponse.java | 8 +-
.../imap/processor/DeleteACLProcessor.java | 15 +-
.../james/imap/processor/GetACLProcessor.java | 8 +-
.../james/imap/processor/GetQuotaProcessor.java | 6 +-
.../imap/processor/GetQuotaRootProcessor.java | 6 +-
.../imap/processor/ListRightsProcessor.java | 17 +-
.../james/imap/processor/MyRightsProcessor.java | 18 +-
.../james/imap/processor/SetACLProcessor.java | 25 +-
.../imap/processor/DeleteACLProcessorTest.java | 22 +-
.../imap/processor/GetACLProcessorTest.java | 13 +-
.../imap/processor/GetQuotaProcessorTest.java | 8 +-
.../processor/GetQuotaRootProcessorTest.java | 7 +-
.../imap/processor/ListRightsProcessorTest.java | 27 +-
.../imap/processor/SetACLProcessorTest.java | 29 +-
.../base/MailboxEventAnalyserTest.java | 16 +-
...ltMailboxesProvisioningFilterThreadTest.java | 17 +-
64 files changed, 1908 insertions(+), 2128 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/4e10f10b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
index 2963ad6..3ef58b7 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
@@ -40,7 +40,6 @@ import org.apache.james.mailbox.model.MailboxQuery;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
-import org.apache.james.mailbox.model.SimpleMailboxACL;
/**
* <p>
@@ -348,7 +347,7 @@ public interface MailboxManager extends RequestAware, MailboxListenerSupport {
* mailbox; false otherwise.
* @throws MailboxException
*/
- boolean hasRight(MailboxPath mailboxPath, MailboxACL.MailboxACLRight right, MailboxSession session) throws MailboxException;
+ boolean hasRight(MailboxPath mailboxPath, MailboxACL.Right right, MailboxSession session) throws MailboxException;
/**
* Returns the rights applicable to the user who has sent the current
@@ -357,11 +356,11 @@ public interface MailboxManager extends RequestAware, MailboxListenerSupport {
* @param mailboxPath Path of the mailbox you want to get your rights on.
* @param session The session used to determine the user we should retrieve the rights of.
* @return the rights applicable to the user who has sent the request,
- * returns {@link SimpleMailboxACL#NO_RIGHTS} if
+ * returns {@link MailboxACL#NO_RIGHTS} if
* {@code session.getUser()} is null.
* @throws UnsupportedRightException
*/
- MailboxACL.MailboxACLRights myRights(MailboxPath mailboxPath, MailboxSession session) throws MailboxException;
+ MailboxACL.Rfc4314Rights myRights(MailboxPath mailboxPath, MailboxSession session) throws MailboxException;
/**
* Computes a result suitable for the LISTRIGHTS IMAP command. The result is
@@ -385,7 +384,7 @@ public interface MailboxManager extends RequestAware, MailboxListenerSupport {
* @return result suitable for the LISTRIGHTS IMAP command
* @throws UnsupportedRightException
*/
- MailboxACL.MailboxACLRights[] listRigths(MailboxPath mailboxPath, MailboxACL.MailboxACLEntryKey identifier, MailboxSession session) throws MailboxException;
+ MailboxACL.Rfc4314Rights[] listRigths(MailboxPath mailboxPath, MailboxACL.EntryKey identifier, MailboxSession session) throws MailboxException;
/**
* Update the Mailbox ACL of the designated mailbox. We can either ADD REPLACE or REMOVE entries.
@@ -393,7 +392,7 @@ public interface MailboxManager extends RequestAware, MailboxListenerSupport {
* @param mailboxACLCommand Update to perform.
* @throws UnsupportedRightException
*/
- void setRights(MailboxPath mailboxPath, MailboxACL.MailboxACLCommand mailboxACLCommand, MailboxSession session) throws MailboxException;
+ void setRights(MailboxPath mailboxPath, MailboxACL.ACLCommand mailboxACLCommand, MailboxSession session) throws MailboxException;
/**
http://git-wip-us.apache.org/repos/asf/james-project/blob/4e10f10b/mailbox/api/src/main/java/org/apache/james/mailbox/acl/MailboxACLResolver.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/acl/MailboxACLResolver.java b/mailbox/api/src/main/java/org/apache/james/mailbox/acl/MailboxACLResolver.java
index 55fc15d..f858756 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/acl/MailboxACLResolver.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/acl/MailboxACLResolver.java
@@ -24,9 +24,6 @@ import javax.mail.Flags;
import org.apache.james.mailbox.exception.UnsupportedRightException;
import org.apache.james.mailbox.model.MailboxACL;
-import org.apache.james.mailbox.model.MailboxACL.MailboxACLEntryKey;
-import org.apache.james.mailbox.model.MailboxACL.MailboxACLRight;
-import org.apache.james.mailbox.model.MailboxACL.MailboxACLRights;
/**
* Implements the interpretation of ACLs.
@@ -86,7 +83,7 @@ public interface MailboxACLResolver {
* resource; false otherwise.
* @throws UnsupportedRightException
*/
- boolean hasRight(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACLRight right, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException;
+ boolean hasRight(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACL.Right right, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException;
/**
* Maps the given {@code mailboxACLRights} to READ-WRITE and READ-ONLY
@@ -125,7 +122,7 @@ public interface MailboxACLResolver {
* @return
* @throws UnsupportedRightException
*/
- boolean isReadWrite(MailboxACLRights mailboxACLRights, Flags sharedFlags) throws UnsupportedRightException;
+ boolean isReadWrite(MailboxACL.Rfc4314Rights mailboxACLRights, Flags sharedFlags) throws UnsupportedRightException;
/**
* Computes a result suitable for the LISTRIGHTS IMAP command. The result is
@@ -148,7 +145,7 @@ public interface MailboxACLResolver {
* of rights which can be set for the given identifier and resource.
* @throws UnsupportedRightException
*/
- MailboxACLRights[] listRights(MailboxACLEntryKey key, GroupMembershipResolver groupMembershipResolver, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException;
+ MailboxACL.Rfc4314Rights[] listRights(MailboxACL.EntryKey key, GroupMembershipResolver groupMembershipResolver, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException;
/**
* Computes the rights which apply to the given user and resource. Global
@@ -173,6 +170,6 @@ public interface MailboxACLResolver {
* @return the rights applicable for the given user and resource.
* @throws UnsupportedRightException
*/
- MailboxACLRights resolveRights(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException;
+ MailboxACL.Rfc4314Rights resolveRights(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException;
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/4e10f10b/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java b/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java
index 545d5df..8f51b1a 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/acl/UnionMailboxACLResolver.java
@@ -30,13 +30,11 @@ import javax.mail.Flags.Flag;
import org.apache.james.mailbox.exception.UnsupportedRightException;
import org.apache.james.mailbox.model.MailboxACL;
-import org.apache.james.mailbox.model.MailboxACL.MailboxACLEntryKey;
-import org.apache.james.mailbox.model.MailboxACL.MailboxACLRight;
-import org.apache.james.mailbox.model.MailboxACL.MailboxACLRights;
+import org.apache.james.mailbox.model.MailboxACL.EntryKey;
import org.apache.james.mailbox.model.MailboxACL.NameType;
-import org.apache.james.mailbox.model.SimpleMailboxACL;
-import org.apache.james.mailbox.model.SimpleMailboxACL.Rfc4314Rights;
-import org.apache.james.mailbox.model.SimpleMailboxACL.SimpleMailboxACLEntryKey;
+import org.apache.james.mailbox.model.MailboxACL.Rfc4314Rights;
+import org.apache.james.mailbox.model.MailboxACL.Right;
+import org.apache.james.mailbox.model.MailboxACL.SpecialName;
/**
@@ -56,12 +54,12 @@ import org.apache.james.mailbox.model.SimpleMailboxACL.SimpleMailboxACLEntryKey;
*
*/
public class UnionMailboxACLResolver implements MailboxACLResolver {
- public static final MailboxACL DEFAULT_GLOBAL_GROUP_ACL = SimpleMailboxACL.OWNER_FULL_EXCEPT_ADMINISTRATION_ACL;
+ public static final MailboxACL DEFAULT_GLOBAL_GROUP_ACL = MailboxACL.OWNER_FULL_EXCEPT_ADMINISTRATION_ACL;
/**
* Nothing else than full rights for the owner.
*/
- public static final MailboxACL DEFAULT_GLOBAL_USER_ACL = SimpleMailboxACL.OWNER_FULL_ACL;
+ public static final MailboxACL DEFAULT_GLOBAL_USER_ACL = MailboxACL.OWNER_FULL_ACL;
private static final int POSITIVE_INDEX = 0;
private static final int NEGATIVE_INDEX = 1;
@@ -72,7 +70,7 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
* computing
* {@link #rightsOf(String, org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver, Mailbox)}
* and
- * {@link #hasRight(String, Mailbox, MailboxACLRight, org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver)}
+ * {@link #hasRight(String, Mailbox, Right, org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver)}
* .
*/
private final MailboxACL userGlobalACL;
@@ -112,25 +110,25 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
}
/**
- * Tells whether the given {@code aclKey} {@link MailboxACLEntryKey} is
+ * Tells whether the given {@code aclKey} {@link EntryKey} is
* applicable for the given {@code queryKey}.
*
* There are two use cases for which this method was designed and tested:
*
* (1) Calls from
- * {@link #hasRight(String, GroupMembershipResolver, MailboxACLRight, MailboxACL, String, boolean)}
+ * {@link #hasRight(String, GroupMembershipResolver, Right, MailboxACL, String, boolean)}
* and
* {@link #resolveRights(String, GroupMembershipResolver, MailboxACL, String, boolean)}
* in which the {@code queryKey} is a {@link NameType#user}.
*
* (2) Calls from
- * {@link #listRights(MailboxACLEntryKey, GroupMembershipResolver, String, boolean)}
+ * {@link #listRights(EntryKey, GroupMembershipResolver, String, boolean)}
* where {@code queryKey} can be anything including {@link NameType#user},
* {@link NameType#group} and all {@link NameType#special} identifiers.
*
* Clearly the set of cases which this method has to handle in (1) is a
* proper subset of the cases handled in (2). See the javadoc on
- * {@link #listRights(MailboxACLEntryKey, GroupMembershipResolver, String, boolean)}
+ * {@link #listRights(EntryKey, GroupMembershipResolver, String, boolean)}
* for more details.
*
* @param aclKey
@@ -140,10 +138,10 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
* @param resourceOwnerIsGroup
* @return
*/
- protected static boolean applies(MailboxACLEntryKey aclKey, MailboxACLEntryKey queryKey, GroupMembershipResolver groupMembershipResolver, String resourceOwner, boolean resourceOwnerIsGroup) {
+ protected static boolean applies(EntryKey aclKey, EntryKey queryKey, GroupMembershipResolver groupMembershipResolver, String resourceOwner, boolean resourceOwnerIsGroup) {
final String aclKeyName = aclKey.getName();
final NameType aclKeyNameType = aclKey.getNameType();
- if (MailboxACL.SpecialName.anybody.name().equals(aclKeyName)) {
+ if (SpecialName.anybody.name().equals(aclKeyName)) {
/* this works also for unauthenticated users */
return true;
} else if (queryKey != null) {
@@ -153,14 +151,14 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
/* Authenticated users */
switch (aclKeyNameType) {
case special:
- if (MailboxACL.SpecialName.authenticated.name().equals(aclKeyName)) {
+ if (SpecialName.authenticated.name().equals(aclKeyName)) {
/* non null query user is viewed as authenticated */
return true;
- } else if (MailboxACL.SpecialName.owner.name().equals(aclKeyName)) {
+ } else if (SpecialName.owner.name().equals(aclKeyName)) {
return (!resourceOwnerIsGroup && queryUserOrGroupName.equals(resourceOwner)) || (resourceOwnerIsGroup && groupMembershipResolver.isMember(queryUserOrGroupName, resourceOwner));
} else {
/* should not happen unless the parent if is changed */
- throw new IllegalStateException("Unexpected " + MailboxACL.SpecialName.class.getName() + "." + aclKeyName);
+ throw new IllegalStateException("Unexpected " + SpecialName.class.getName() + "." + aclKeyName);
}
case user:
return aclKeyName.equals(queryUserOrGroupName);
@@ -173,16 +171,16 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
/* query is a group */
switch (aclKeyNameType) {
case special:
- if (MailboxACL.SpecialName.authenticated.name().equals(aclKeyName)) {
+ if (SpecialName.authenticated.name().equals(aclKeyName)) {
/*
* see the javadoc comment on listRights()
*/
return true;
- } else if (MailboxACL.SpecialName.owner.name().equals(aclKeyName)) {
+ } else if (SpecialName.owner.name().equals(aclKeyName)) {
return resourceOwnerIsGroup && queryUserOrGroupName.equals(resourceOwner);
} else {
/* should not happen unless the parent if is changed */
- throw new IllegalStateException("Unexpected " + MailboxACL.SpecialName.class.getName() + "." + aclKeyName);
+ throw new IllegalStateException("Unexpected " + SpecialName.class.getName() + "." + aclKeyName);
}
case user:
/* query groups cannot match ACL users */
@@ -202,7 +200,7 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
* owner
*/
return true;
- } else if (MailboxACL.SpecialName.owner.name().equals(queryUserOrGroupName) && MailboxACL.SpecialName.authenticated.name().equals(aclKeyName)) {
+ } else if (SpecialName.owner.name().equals(queryUserOrGroupName) && SpecialName.authenticated.name().equals(aclKeyName)) {
/*
* query owner matches authenticated because owner will
* be resolved only if the user is authenticated
@@ -240,17 +238,17 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
* @see org.apache.james.mailbox.store.mail.MailboxACLResolver#hasRight(java.
* lang.String, org.apache.james.mailbox.store.mail.MailboxACLResolver.
* GroupMembershipResolver,
- * org.apache.james.mailbox.MailboxACL.MailboxACLRight,
+ * org.apache.james.mailbox.MailboxACL.Right,
* org.apache.james.mailbox.MailboxACL, java.lang.String)
*/
@Override
- public boolean hasRight(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACLRight right, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException {
- final MailboxACLEntryKey queryKey = requestUser == null ? null : new SimpleMailboxACLEntryKey(requestUser, NameType.user, false);
+ public boolean hasRight(String requestUser, GroupMembershipResolver groupMembershipResolver, Right right, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException {
+ final EntryKey queryKey = requestUser == null ? null : new EntryKey(requestUser, NameType.user, false);
boolean result = false;
- Map<MailboxACLEntryKey, MailboxACLRights> entries = resourceOwnerIsGroup ? groupGlobalACL.getEntries() : userGlobalACL.getEntries();
+ Map<EntryKey, Rfc4314Rights> entries = resourceOwnerIsGroup ? groupGlobalACL.getEntries() : userGlobalACL.getEntries();
if (entries != null) {
- for (Entry<MailboxACLEntryKey, MailboxACLRights> entry : entries.entrySet()) {
- final MailboxACLEntryKey key = entry.getKey();
+ for (Entry<EntryKey, Rfc4314Rights> entry : entries.entrySet()) {
+ final EntryKey key = entry.getKey();
if (applies(key, queryKey, groupMembershipResolver, resourceOwner, resourceOwnerIsGroup) && entry.getValue().contains(right)) {
if (key.isNegative()) {
return false;
@@ -264,8 +262,8 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
if (resourceACL != null) {
entries = resourceACL.getEntries();
if (entries != null) {
- for (Entry<MailboxACLEntryKey, MailboxACLRights> entry : entries.entrySet()) {
- final MailboxACLEntryKey key = entry.getKey();
+ for (Entry<EntryKey, Rfc4314Rights> entry : entries.entrySet()) {
+ final EntryKey key = entry.getKey();
if (applies(key, queryKey, groupMembershipResolver, resourceOwner, resourceOwnerIsGroup) && entry.getValue().contains(right)) {
if (key.isNegative()) {
return false;
@@ -281,13 +279,13 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
}
/**
- * @see org.apache.james.mailbox.acl.MailboxACLResolver#isReadWrite(org.apache.james.mailbox.model.MailboxACL.MailboxACLRights,
+ * @see org.apache.james.mailbox.acl.MailboxACLResolver#isReadWrite(org.apache.james.mailbox.model.Rfc4314Rights,
* javax.mail.Flags)
*/
@Override
- public boolean isReadWrite(MailboxACLRights mailboxACLRights, Flags sharedFlags) throws UnsupportedRightException {
+ public boolean isReadWrite(Rfc4314Rights Rfc4314Rights, Flags sharedFlags) throws UnsupportedRightException {
/* the two fast cases first */
- if (mailboxACLRights.contains(SimpleMailboxACL.Right.Insert) || mailboxACLRights.contains(SimpleMailboxACL.Right.PerformExpunge)) {
+ if (Rfc4314Rights.contains(MailboxACL.Right.Insert) || Rfc4314Rights.contains(MailboxACL.Right.PerformExpunge)) {
return true;
}
/*
@@ -303,12 +301,12 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
* flags.
*/
else if (sharedFlags != null) {
- if (sharedFlags.contains(Flag.DELETED) && mailboxACLRights.contains(SimpleMailboxACL.Right.DeleteMessages)) {
+ if (sharedFlags.contains(Flag.DELETED) && Rfc4314Rights.contains(MailboxACL.Right.DeleteMessages)) {
return true;
- } else if (sharedFlags.contains(Flag.SEEN) && mailboxACLRights.contains(SimpleMailboxACL.Right.WriteSeenFlag)) {
+ } else if (sharedFlags.contains(Flag.SEEN) && Rfc4314Rights.contains(MailboxACL.Right.WriteSeenFlag)) {
return true;
} else {
- boolean hasWriteRight = mailboxACLRights.contains(SimpleMailboxACL.Right.Write);
+ boolean hasWriteRight = Rfc4314Rights.contains(MailboxACL.Right.Write);
return hasWriteRight && (sharedFlags.contains(Flag.ANSWERED) || sharedFlags.contains(Flag.DRAFT) || sharedFlags.contains(Flag.FLAGGED) || sharedFlags.contains(Flag.RECENT) || sharedFlags.contains(Flag.USER));
}
}
@@ -363,8 +361,8 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
* @see org.apache.james.mailbox.acl.MailboxACLResolver#listRightsDefault(boolean)
*/
@Override
- public MailboxACLRights[] listRights(MailboxACLEntryKey queryKey, GroupMembershipResolver groupMembershipResolver, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException {
- MailboxACL.MailboxACLRights[] positiveNegativePair = { SimpleMailboxACL.NO_RIGHTS, SimpleMailboxACL.NO_RIGHTS };
+ public Rfc4314Rights[] listRights(EntryKey queryKey, GroupMembershipResolver groupMembershipResolver, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException {
+ Rfc4314Rights[] positiveNegativePair = { MailboxACL.NO_RIGHTS, MailboxACL.NO_RIGHTS };
MailboxACL userACL = resourceOwnerIsGroup ? groupGlobalACL : userGlobalACL;
resolveRights(queryKey, groupMembershipResolver, userACL.getEntries(), resourceOwner, resourceOwnerIsGroup, positiveNegativePair);
@@ -376,15 +374,15 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
}
}
- private static MailboxACLRights[] toListRightsArray(MailboxACLRights implicitRights) throws UnsupportedRightException {
- List<MailboxACLRights> result = new ArrayList<>();
+ private static Rfc4314Rights[] toListRightsArray(Rfc4314Rights implicitRights) throws UnsupportedRightException {
+ List<Rfc4314Rights> result = new ArrayList<>();
result.add(implicitRights);
- for (MailboxACLRight right : SimpleMailboxACL.FULL_RIGHTS) {
+ for (Right right : MailboxACL.FULL_RIGHTS.list()) {
if (!implicitRights.contains(right)) {
result.add(new Rfc4314Rights(right));
}
}
- return result.toArray(new MailboxACLRights[result.size()]);
+ return result.toArray(new Rfc4314Rights[result.size()]);
}
/**
@@ -394,9 +392,9 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
* java.lang.String)
*/
@Override
- public MailboxACL.MailboxACLRights resolveRights(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException {
- MailboxACL.MailboxACLRights[] positiveNegativePair = { SimpleMailboxACL.NO_RIGHTS, SimpleMailboxACL.NO_RIGHTS };
- final MailboxACLEntryKey queryKey = requestUser == null ? null : new SimpleMailboxACLEntryKey(requestUser, NameType.user, false);
+ public Rfc4314Rights resolveRights(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException {
+ Rfc4314Rights[] positiveNegativePair = { MailboxACL.NO_RIGHTS, MailboxACL.NO_RIGHTS };
+ final EntryKey queryKey = requestUser == null ? null : new EntryKey(requestUser, NameType.user, false);
MailboxACL userACL = resourceOwnerIsGroup ? groupGlobalACL : userGlobalACL;
resolveRights(queryKey, groupMembershipResolver, userACL.getEntries(), resourceOwner, resourceOwnerIsGroup, positiveNegativePair);
@@ -418,11 +416,11 @@ public class UnionMailboxACLResolver implements MailboxACLResolver {
* @param positiveNegativePair
* @throws UnsupportedRightException
*/
- private void resolveRights(MailboxACLEntryKey queryKey, GroupMembershipResolver groupMembershipResolver, Map<MailboxACLEntryKey, MailboxACLRights> entries, String resourceOwner, boolean resourceOwnerIsGroup, MailboxACL.MailboxACLRights[] positiveNegativePair)
+ private void resolveRights(EntryKey queryKey, GroupMembershipResolver groupMembershipResolver, Map<EntryKey, Rfc4314Rights> entries, String resourceOwner, boolean resourceOwnerIsGroup, Rfc4314Rights[] positiveNegativePair)
throws UnsupportedRightException {
if (entries != null) {
- for (Entry<MailboxACLEntryKey, MailboxACLRights> entry : entries.entrySet()) {
- final MailboxACLEntryKey key = entry.getKey();
+ for (Entry<EntryKey, Rfc4314Rights> entry : entries.entrySet()) {
+ final EntryKey key = entry.getKey();
if (applies(key, queryKey, groupMembershipResolver, resourceOwner, resourceOwnerIsGroup)) {
if (key.isNegative()) {
positiveNegativePair[NEGATIVE_INDEX] = positiveNegativePair[NEGATIVE_INDEX].union(entry.getValue());
http://git-wip-us.apache.org/repos/asf/james-project/blob/4e10f10b/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java b/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java
index 8ca5dcd..26b711c 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/exception/UnsupportedRightException.java
@@ -20,7 +20,7 @@
package org.apache.james.mailbox.exception;
-import org.apache.james.mailbox.model.MailboxACL.MailboxACLRight;
+import org.apache.james.mailbox.model.MailboxACL.Right;
/**
* Thrown when the current system does not support the given right.
@@ -41,7 +41,7 @@ public class UnsupportedRightException extends MailboxSecurityException {
this.unsupportedRight = right;
}
- public UnsupportedRightException(MailboxACLRight unsupportedRight) {
+ public UnsupportedRightException(Right unsupportedRight) {
this(unsupportedRight.asCharacter());
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/4e10f10b/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 b6e97d2..09dcbbb 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
@@ -19,235 +19,686 @@
package org.apache.james.mailbox.model;
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.tuple.Pair;
import org.apache.james.mailbox.exception.UnsupportedRightException;
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
/**
* Stores an Access Control List (ACL) applicable to a mailbox. Inspired by
* RFC4314 IMAP4 Access Control List (ACL) Extension.
- *
+ *
* Implementations must be immutable. Implementations should override
* {@link #hashCode()} and {@link #equals(Object)}.
- *
+ *
*/
-public interface MailboxACL {
+public class MailboxACL {
/**
* SETACL command mode.
*/
- enum EditMode {
+ public enum EditMode {
ADD, REMOVE, REPLACE
}
+ public enum NameType {
+ group, special, user
+ }
+
/**
- * The key used in {@link MailboxACL#getEntries()}. Implementations should
- * override {@link #hashCode()} and {@link #equals(Object)} in such a way
- * that all of {@link #getName()}, {@link #getNameType()} and
- * {@link #isNegative()} are significant.
- *
+ * Special name literals.
*/
- interface MailboxACLEntryKey {
- /**
- * Returns the name of a user or of a group to which this
- * {@link MailboxACLEntryKey} applies.
- *
- * @return User name, group name or special name.
- */
- String getName();
+ public enum SpecialName {
+ anybody, authenticated, owner
+ }
- /**
- * Tells of what type is the name returned by {@link #getName()}.
- *
- * @return type of the name returned by {@link #getName()}
- */
- NameType getNameType();
+ /**
+ * SETACL third argument prefix
+ */
+ public static final char ADD_RIGHTS_MARKER = '+';
- /**
- * If true the {@link MailboxACLRights} returned by
- * {@link MailboxACLEntry#getRights()} should be interpreted as
- * "negative rights" as described in RFC4314: If the identifier "-fred"
- * is granted the "w" right, that indicates that the "w" right is to be
- * removed from users matching the identifier "fred", even though the
- * user "fred" might have the "w" right as a consequence of some other
- * identifier in the ACL.
- *
- * Note that {@link MailboxACLEntry#getName()} does not start with "-"
- * when {@link MailboxACLEntry#getRights()} returns true.
- *
- * @return
- */
- boolean isNegative();
+ /**
+ * Marks groups when (de)serializing {@link MailboxACLEntryKey}s.
+ *
+ * @see MailboxACLEntryKey#serialize()
+ */
+ public static final char DEFAULT_GROUP_MARKER = '$';
- /**
- * Returns a serialized form of this {@link MailboxACLEntryKey} as a
- * {@link String}. Implementations should choose a consistent way how
- * all of {@link #getName()}, {@link #getNameType()} and
- * {@link #isNegative()} get serialized.
- *
- * RFC4314 sction 2. states: All user name strings accepted by the LOGIN
- * or AUTHENTICATE commands to authenticate to the IMAP server are
- * reserved as identifiers for the corresponding users. Identifiers
- * starting with a dash ("-") are reserved for "negative rights",
- * described below. All other identifier strings are interpreted in an
- * implementation-defined manner.
- *
- * Dovecot and Cyrus mark groups with '$' prefix. See <a
- * href="http://wiki2.dovecot.org/SharedMailboxes/Shared"
- * >http://wiki2.dovecot.org/SharedMailboxes/Shared</a>:
- *
- * <cite>The $group syntax is not a standard, but it is mentioned in RFC
- * 4314 examples and is also understood by at least Cyrus IMAP. Having
- * '-' before the identifier specifies negative rights.</cite>
- *
- * @see MailboxACL#DEFAULT_GROUP_MARKER
- * @see MailboxACL#DEFAULT_NEGATIVE_MARKER
- *
- * @return serialized form as a {@link String}
- */
- String serialize();
- }
+ /**
+ * Marks negative when (de)serializing {@link MailboxACLEntryKey}s.
+ *
+ * @see MailboxACLEntryKey#serialize()
+ */
+ public static final char DEFAULT_NEGATIVE_MARKER = '-';
+
+ /**
+ * SETACL third argument prefix
+ */
+ public static final char REMOVE_RIGHTS_MARKER = '-';
/**
* Single right applicable to a mailbox.
*/
- interface MailboxACLRight {
+ public enum Right {
+ Administer('a'), // (perform SETACL/DELETEACL/GETACL/LISTRIGHTS)
+ PerformExpunge('e'), //perform EXPUNGE and expunge as a part of CLOSE
+ Insert('i'), //insert (perform APPEND, COPY into mailbox)
+ /*
+ * create mailboxes (CREATE new sub-mailboxes in any
+ * implementation-defined hierarchy, parent mailbox for the new mailbox
+ * name in RENAME)
+ * */
+ CreateMailbox('k'),
+ Lookup('l'), //lookup (mailbox is visible to LIST/LSUB commands, SUBSCRIBE mailbox)
+ Post('p'), //post (send mail to submission address for mailbox, not enforced by IMAP4 itself)
+ Read('r'), //read (SELECT the mailbox, perform STATUS)
+ /**
+ * keep seen/unseen information across sessions (set or clear \SEEN
+ * flag via STORE, also set \SEEN during APPEND/COPY/ FETCH BODY[...])
+ */
+ WriteSeenFlag('s'),
+ DeleteMessages('t'), //delete messages (set or clear \DELETED flag via STORE, set \DELETED flag during APPEND/COPY)
+ /**
+ * write (set or clear flags other than \SEEN and \DELETED via
+ * STORE, also set them during APPEND/COPY)
+ */
+ Write('w'),
+ DeleteMailbox('x'); //delete mailbox (DELETE mailbox, old mailbox name in RENAME)
+
+ private final char rightCharacter;
+
+ Right(char rightCharacter) {
+ this.rightCharacter = rightCharacter;
+ }
+
/**
* Returns the char representation of this right.
- *
+ *
* @return char representation of this right
*/
- char asCharacter();
+ public char asCharacter() {
+ return rightCharacter;
+ }
+
+ public static final EnumSet<Right> allRights = EnumSet.allOf(Right.class);
+
+ public static Right forChar(char c) throws UnsupportedRightException {
+ return Right.allRights
+ .stream()
+ .filter(r -> r.asCharacter() == c)
+ .findFirst()
+ .orElseThrow(() -> new UnsupportedRightException(c));
+ }
}
/**
- * Iterable set of {@link MailboxACLRight}s.
- *
+ * Holds the collection of {@link MailboxACL.Right}s.
+ *
* Implementations may decide to support only a specific range of rights,
* e.g. the Standard Rights of RFC 4314 section 2.1.
- *
+ *
* Implementations must not allow adding or removing of elements once this
* MailboxACLRights are initialized.
*/
- interface MailboxACLRights extends Iterable<MailboxACLRight> {
+ public static class Rfc4314Rights {
+ /**
+ * See RFC 4314 section 2.1.1. Obsolete Rights.
+ */
+ public enum CompatibilityMode {
+ ck_detx, ckx_det, NO_COMPATIBILITY
+ }
+
+ private static final char c_ObsoleteCreate = 'c';
+ private static final char d_ObsoleteDelete = 'd';
+
+ /**
+ * See RFC 4314 section 2.1.1. Obsolete Rights.
+ */
+ private final CompatibilityMode compatibilityMode = CompatibilityMode.ckx_det;
+
+ private final EnumSet<Right> value;
+
+ private Rfc4314Rights(EnumSet<Right> rights) {
+ this.value = EnumSet.copyOf(rights);
+ }
+
+ private Rfc4314Rights() {
+ this(EnumSet.noneOf(Right.class));
+ }
+
+ public Rfc4314Rights(Right... rights) {
+ this(EnumSet.copyOf(Arrays.asList(rights)));
+ }
+
+ public Rfc4314Rights(Right right) throws UnsupportedRightException {
+ this.value = EnumSet.of(Right.forChar(right.asCharacter()));
+ }
+
+ /* Used for json serialization (probably a bad idea) */
+ public Rfc4314Rights(int serializedRights) {
+ List<Right> rights = Right.allRights.stream()
+ .filter(right -> ((serializedRights >> right.ordinal()) & 1) != 0)
+ .collect(Collectors.toList());
+ if (rights.isEmpty()) {
+ this.value = EnumSet.noneOf(Right.class);
+ } else {
+ this.value = EnumSet.copyOf(rights);
+ }
+ }
+
+ public Rfc4314Rights(String serializedRfc4314Rights) throws UnsupportedRightException {
+ List<Right> rights = serializedRfc4314Rights.chars()
+ .mapToObj(i -> (char) i)
+ .flatMap(Throwing.function(this::convert).sneakyThrow())
+ .collect(Collectors.toList());
+ if (rights.isEmpty()) {
+ this.value = EnumSet.noneOf(Right.class);
+ } else {
+ this.value = EnumSet.copyOf(rights);
+ }
+ }
+
+ private Stream<Right> convert(char flag) throws UnsupportedRightException {
+ switch (flag) {
+ case c_ObsoleteCreate:
+ return convertObsoleteCreate(flag);
+ case d_ObsoleteDelete:
+ return convertObsoleteDelete(flag);
+ default:
+ return Stream.of(Right.forChar(flag));
+ }
+ }
+
+ private Stream<Right> convertObsoleteDelete(char flag) throws UnsupportedRightException {
+ switch (compatibilityMode) {
+ case ck_detx:
+ return Stream.of(Right.PerformExpunge, Right.DeleteMessages, Right.DeleteMailbox);
+ case ckx_det:
+ return Stream.of(Right.PerformExpunge, Right.DeleteMessages);
+ case NO_COMPATIBILITY:
+ throw new UnsupportedRightException(flag);
+ default:
+ throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name());
+ }
+ }
+
+ private Stream<Right> convertObsoleteCreate(char flag) throws UnsupportedRightException {
+ switch (compatibilityMode) {
+ case ck_detx:
+ return Stream.of(Right.CreateMailbox);
+ case ckx_det:
+ return Stream.of(Right.CreateMailbox, Right.DeleteMailbox);
+ case NO_COMPATIBILITY:
+ throw new UnsupportedRightException(flag);
+ default:
+ throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name());
+ }
+ }
/**
* Tells whether this contains the given right.
- *
- * @param right
- * @return
- * @throws UnsupportedRightException
- * iff the given right is not supported.
+ *
+ * @throws UnsupportedRightException if the given right is not supported.
*/
- boolean contains(MailboxACLRight right) throws UnsupportedRightException;
+ public boolean contains(char flag) throws UnsupportedRightException {
+ return contains(Right.forChar(flag));
+ }
+
+ public boolean contains(Right right) throws UnsupportedRightException {
+ return value.contains(Right.forChar(right.asCharacter()));
+ }
+
+ /* Used for json serialization (probably a bad idea) */
+ public int serializeAsInteger() {
+ return value.stream().mapToInt(x -> 1 << x.ordinal()).sum();
+ }
+
+ public boolean equals(Object o) {
+ if (o instanceof Rfc4314Rights) {
+ Rfc4314Rights that = (Rfc4314Rights) o;
+ return this.value.equals(that.value);
+ }
+ return false;
+ }
/**
* Performs the set theoretic operation of relative complement of
* toRemove MailboxACLRights in this MailboxACLRights.
- *
+ *
* A schematic example: "lrw".except("w") returns "lr".
- *
+ *
* Implementations must return a new unmodifiable instance of
- * {@link MailboxACLRights}. However, implementations may decide to
+ * {@link MailboxACL.MailboxACLRights}. However, implementations may decide to
* return this or toRemove parameter value in case the result would be
* equal to the respective one of those.
- *
- * @param toRemove
- * @return
+ *
* @throws UnsupportedRightException
*/
- MailboxACLRights except(MailboxACLRights toRemove) throws UnsupportedRightException;
+ public Rfc4314Rights except(Rfc4314Rights toRemove) throws UnsupportedRightException {
+ EnumSet<Right> copy = EnumSet.copyOf(value);
+ copy.removeAll(convertRightsToList(toRemove));
+ return new Rfc4314Rights(copy);
+ }
/**
* Tells if this set of rights is empty.
- *
+ *
* @return true if there are no rights in this set; false otherwise.
*/
- boolean isEmpty();
+ public boolean isEmpty() {
+ return value.isEmpty();
+ }
/**
* Tells whether the implementation supports the given right.
- *
+ *
* @param right
* @return true if this supports the given right.
*/
- boolean isSupported(MailboxACLRight right);
+ public boolean isSupported(Right right) {
+ try {
+ contains(right.asCharacter());
+ return true;
+ } catch (UnsupportedRightException e) {
+ return false;
+ }
+ }
+
+ public Iterator<Right> iterator() {
+ ImmutableList<Right> rights = ImmutableList.copyOf(value);
+ return rights.iterator();
+ }
+
+ public List<Right> list() {
+ return ImmutableList.copyOf(value);
+ }
/**
- * Returns a serialized form of this {@link MailboxACLRights} as
+ * Returns a serialized form of this {@link MailboxACL.Right} as
* {@link String}.
- *
+ *
* @return a {@link String}
*/
- String serialize();
+ public String serialize() {
+ return value.stream()
+ .map(Right::asCharacter)
+ .map(String::valueOf)
+ .collect(Collectors.joining());
+ }
+
+ public String toString() {
+ return serialize();
+ }
/**
- * Performs the set theoretic operation of union of this
- * MailboxACLRights and toAdd MailboxACLRights.
- *
+ * Performs the theoretic operation of union of this
+ * Rfc4314Rights and toAdd Rfc4314Rights.
+ *
* A schematic example: "lr".union("rw") returns "lrw".
- *
+ *
* Implementations must return a new unmodifiable instance of
- * {@link MailboxACLRights}. However, implementations may decide to
- * return this or toAdd parameter value in case the result would be
- * equal to the respective one of those.
- *
+ * {@link MailboxACL.Rfc4314Rights}.
+ *
* @param toAdd
* @return union of this and toAdd
* @throws UnsupportedRightException
- *
+ *
*/
- MailboxACLRights union(MailboxACLRights toAdd) throws UnsupportedRightException;
+ public Rfc4314Rights union(Rfc4314Rights toAdd) throws UnsupportedRightException {
+ Preconditions.checkNotNull(toAdd);
+ EnumSet<Right> rightUnion = EnumSet.noneOf(Right.class);
+ rightUnion.addAll(value);
+ rightUnion.addAll(convertRightsToList(toAdd));
+ return new Rfc4314Rights(rightUnion);
+ }
+
+ private List<Right> convertRightsToList(Rfc4314Rights toAdd) {
+ return Optional.ofNullable(toAdd).orElse(Rfc4314Rights.empty())
+ .list()
+ .stream()
+ .map(Throwing.function(right -> Right.forChar(right.asCharacter())))
+ .collect(Guavate.toImmutableList());
+ }
+
+ private static Rfc4314Rights empty() {
+ return new Rfc4314Rights();
+ }
+
+ }
+ /**
+ * A utility implementation of
+ * {@code Map.Entry<EntryKey, Rfc4314Rights>}.
+ */
+ public static class Entry extends AbstractMap.SimpleEntry<EntryKey, Rfc4314Rights> {
+ public Entry(EntryKey key, Rfc4314Rights value) {
+ super(key, value);
+ }
+
+ public Entry(String key, String value) throws UnsupportedRightException {
+ this(EntryKey.deserialize(key), new Rfc4314Rights(value));
+ }
}
/**
- * Allows distinguishing between users, groups and special names (see
- * {@link SpecialName}).
+ * The key used in {@link MailboxACL#getEntries()}. Implementations should
+ * override {@link #hashCode()} and {@link #equals(Object)} in such a way
+ * that all of {@link #getName()}, {@link #getNameType()} and
+ * {@link #isNegative()} are significant.
+ *
*/
+ public static class EntryKey {
+ public static EntryKey createGroup(String name) {
+ return new EntryKey(name, NameType.group, false);
+ }
+
+ public static EntryKey createGroup(String name, boolean negative) {
+ return new EntryKey(name, NameType.group, negative);
+ }
+
+ public static EntryKey createUser(String name) {
+ return new EntryKey(name, NameType.user, false);
+ }
- interface MailboxACLCommand {
- MailboxACLEntryKey getEntryKey();
+ public static EntryKey createUser(String name, boolean negative) {
+ return new EntryKey(name, NameType.user, negative);
+ }
- EditMode getEditMode();
+ private final String name;
+ private final NameType nameType;
+ private final boolean negative;
+
+ /**
+ * Creates a new instance of SimpleMailboxACLEntryKey from the given
+ * serialized {@link String}. It supposes that negative rights are
+ * marked with {@link MailboxACL#DEFAULT_NEGATIVE_MARKER} and that
+ * groups are marked with {@link MailboxACL#DEFAULT_GROUP_MARKER}.
+ *
+ * @param serialized
+ */
+ public static EntryKey deserialize(String serialized) {
+ Preconditions.checkNotNull(serialized, "Cannot parse null");
+ Preconditions.checkArgument(!serialized.isEmpty(), "Cannot parse an empty string");
+
+ boolean negative = serialized.charAt(0) == DEFAULT_NEGATIVE_MARKER;
+ int nameStart = negative ? 1 : 0;
+ boolean isGroup = serialized.charAt(nameStart) == DEFAULT_GROUP_MARKER;
+ Optional<NameType> explicitNameType = isGroup ? Optional.of(NameType.group) : Optional.empty();
+ String name = isGroup ? serialized.substring(nameStart + 1) : serialized.substring(nameStart);
+
+ if (name.isEmpty()) {
+ throw new IllegalStateException("Cannot parse a string with empty name");
+ }
+ NameType nameType = explicitNameType.orElseGet(() -> computeImplicitNameType(name));
+
+ return new EntryKey(name, nameType, negative);
+ }
+
+ private static NameType computeImplicitNameType(String name) {
+ boolean isSpecialName = Arrays.stream(SpecialName.values())
+ .anyMatch(specialName -> specialName.name().equals(name));
+ if (isSpecialName) {
+ return NameType.special;
+ }
+ return NameType.user;
+ }
+
+ public EntryKey(String name, NameType nameType, boolean negative) {
+ Preconditions.checkNotNull(name, "Provide a name for this " + getClass().getName());
+ Preconditions.checkNotNull(nameType, "Provide a nameType for this " + getClass().getName());
+
+ this.name = name;
+ this.nameType = nameType;
+ this.negative = negative;
+ }
+
+ public boolean equals(Object o) {
+ if (o instanceof EntryKey) {
+ EntryKey other = (EntryKey) o;
+ return Objects.equals(this.name, other.getName())
+ && Objects.equals(this.nameType, other.getNameType())
+ && Objects.equals(this.negative, other.isNegative());
+ }
+ return false;
+ }
- MailboxACLRights getRights();
+ /**
+ * Returns the name of a user or of a group to which this
+ * {@link MailboxACL.MailboxACLEntryKey} applies.
+ *
+ * @return User name, group name or special name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Tells of what type is the name returned by {@link #getName()}.
+ *
+ * @return type of the name returned by {@link #getName()}
+ */
+ public NameType getNameType() {
+ return nameType;
+ }
+
+ public final int hashCode() {
+ return Objects.hash(negative, nameType, name);
+ }
+
+ /**
+ * If true the {@link MailboxACL.MailboxACLRights} returned by
+ * {@link MailboxACLEntry#getRights()} should be interpreted as
+ * "negative rights" as described in RFC4314: If the identifier "-fred"
+ * is granted the "w" right, that indicates that the "w" right is to be
+ * removed from users matching the identifier "fred", even though the
+ * user "fred" might have the "w" right as a consequence of some other
+ * identifier in the ACL.
+ *
+ * Note that {@link MailboxACLEntry#getName()} does not start with "-"
+ * when {@link MailboxACLEntry#getRights()} returns true.
+ *
+ * @return
+ */
+ public boolean isNegative() {
+ return negative;
+ }
+
+ /**
+ * Returns a serialized form of this {@link MailboxACL.MailboxACLEntryKey} as a
+ * {@link String}. Implementations should choose a consistent way how
+ * all of {@link #getName()}, {@link #getNameType()} and
+ * {@link #isNegative()} get serialized.
+ *
+ * RFC4314 sction 2. states: All user name strings accepted by the LOGIN
+ * or AUTHENTICATE commands to authenticate to the IMAP server are
+ * reserved as identifiers for the corresponding users. Identifiers
+ * starting with a dash ("-") are reserved for "negative rights",
+ * described below. All other identifier strings are interpreted in an
+ * implementation-defined manner.
+ *
+ * Dovecot and Cyrus mark groups with '$' prefix. See <a
+ * href="http://wiki2.dovecot.org/SharedMailboxes/Shared"
+ * >http://wiki2.dovecot.org/SharedMailboxes/Shared</a>:
+ *
+ * <cite>The $group syntax is not a standard, but it is mentioned in RFC
+ * 4314 examples and is also understood by at least Cyrus IMAP. Having
+ * '-' before the identifier specifies negative rights.</cite>
+ *
+ * @see MailboxACL#DEFAULT_GROUP_MARKER
+ * @see MailboxACL#DEFAULT_NEGATIVE_MARKER
+ *
+ * @return serialized form as a {@link String}
+ */
+ public String serialize() {
+ String negativePart = negative ? String.valueOf(DEFAULT_NEGATIVE_MARKER) : "";
+ String nameTypePart = nameType == NameType.group ? String.valueOf(DEFAULT_GROUP_MARKER) : "";
+
+ return negativePart + nameTypePart + name;
+ }
+
+ public String toString() {
+ return serialize();
+ }
}
- enum NameType {
- group, special, user
+
+ public static class ACLCommand {
+ private final EntryKey key;
+ private final EditMode editMode;
+ private final Rfc4314Rights rights;
+
+ public ACLCommand(EntryKey key, EditMode editMode, Rfc4314Rights rights) {
+ this.key = key;
+ this.editMode = editMode;
+ this.rights = rights;
+ }
+
+ public EntryKey getEntryKey() {
+ return key;
+ }
+
+ public EditMode getEditMode() {
+ return editMode;
+ }
+
+ public Rfc4314Rights getRights() {
+ return rights;
+ }
+
+ public final boolean equals(Object o) {
+ if (o instanceof ACLCommand) {
+ ACLCommand that = (ACLCommand) o;
+
+ return Objects.equals(this.key, that.key)
+ && Objects.equals(this.editMode, that.editMode)
+ && Objects.equals(this.rights, that.rights);
+ }
+ return false;
+ }
+
+ public final int hashCode() {
+ return Objects.hash(key, editMode, rights);
+ }
}
- /**
- * Special name literals.
- */
- enum SpecialName {
- anybody, authenticated, owner
+ public static final EntryKey ANYBODY_KEY;
+ public static final EntryKey ANYBODY_NEGATIVE_KEY;
+ public static final EntryKey AUTHENTICATED_KEY;
+ public static final EntryKey AUTHENTICATED_NEGATIVE_KEY;
+ public static final MailboxACL EMPTY;
+
+ public static final Rfc4314Rights FULL_RIGHTS;
+
+ public static final Rfc4314Rights NO_RIGHTS;
+ public static final MailboxACL OWNER_FULL_ACL;
+ public static final MailboxACL OWNER_FULL_EXCEPT_ADMINISTRATION_ACL;
+
+ public static final EntryKey OWNER_KEY;
+ public static final EntryKey OWNER_NEGATIVE_KEY;
+
+ static {
+ try {
+ ANYBODY_KEY = new EntryKey(SpecialName.anybody.name(), NameType.special, false);
+ ANYBODY_NEGATIVE_KEY = new EntryKey(SpecialName.anybody.name(), NameType.special, true);
+ AUTHENTICATED_KEY = new EntryKey(SpecialName.authenticated.name(), NameType.special, false);
+ AUTHENTICATED_NEGATIVE_KEY = new EntryKey(SpecialName.authenticated.name(), NameType.special, true);
+ EMPTY = new MailboxACL();
+ FULL_RIGHTS = new Rfc4314Rights(Right.allRights);
+ NO_RIGHTS = new Rfc4314Rights();
+ OWNER_KEY = new EntryKey(SpecialName.owner.name(), NameType.special, false);
+ OWNER_NEGATIVE_KEY = new EntryKey(SpecialName.owner.name(), NameType.special, true);
+ OWNER_FULL_ACL = new MailboxACL(new Entry[] { new Entry(MailboxACL.OWNER_KEY, MailboxACL.FULL_RIGHTS) });
+ OWNER_FULL_EXCEPT_ADMINISTRATION_ACL = new MailboxACL(new Entry[] { new Entry(MailboxACL.OWNER_KEY, MailboxACL.FULL_RIGHTS.except(new Rfc4314Rights(Right.Administer))) });
+ } catch (UnsupportedRightException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Map<EntryKey, Rfc4314Rights> toMap(Properties props) throws UnsupportedRightException {
+ ImmutableMap.Builder<EntryKey, Rfc4314Rights> builder = ImmutableMap.builder();
+ for (Map.Entry prop : props.entrySet()) {
+ builder.put(EntryKey.deserialize((String) prop.getKey()), new Rfc4314Rights((String) prop.getValue()));
+ }
+ return builder.build();
}
+
+ private final Map<EntryKey, Rfc4314Rights> entries;
/**
- * SETACL third argument prefix
+ * Creates a new instance of SimpleMailboxACL containing no entries.
+ *
*/
- char ADD_RIGHTS_MARKER = '+';
+ public MailboxACL() {
+ this(ImmutableMap.of());
+ }
/**
- * Marks groups when (de)serializing {@link MailboxACLEntryKey}s.
+ * Creates a new instance of SimpleMailboxACL from the given array of
+ * entries.
*
- * @see MailboxACLEntryKey#serialize()
+ * @param entries
*/
- char DEFAULT_GROUP_MARKER = '$';
+ public MailboxACL(Map.Entry<EntryKey, Rfc4314Rights>... entries) {
+ this(ImmutableMap.copyOf(
+ Optional.ofNullable(entries)
+ .map(array -> Arrays.stream(array)
+ .collect(Guavate.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)))
+ .orElse(ImmutableMap.of())));
+ }
/**
- * Marks negative when (de)serializing {@link MailboxACLEntryKey}s.
- *
- * @see MailboxACLEntryKey#serialize()
+ * Creates a new instance of SimpleMailboxACL from the given {@link Map} of
+ * entries.
+ *
+ * @param entries
*/
- char DEFAULT_NEGATIVE_MARKER = '-';
+ public MailboxACL(Map<EntryKey, Rfc4314Rights> entries) {
+ Preconditions.checkNotNull(entries);
+
+ this.entries = ImmutableMap.copyOf(entries);
+ }
/**
- * SETACL third argument prefix
+ * Creates a new instance of SimpleMailboxACL from {@link Properties}. The
+ * keys and values from the <code>props</code> parameter are parsed by the
+ * {@link String} constructors of {@link EntryKey} and
+ * {@link Rfc4314Rights} respectively.
+ *
+ * @param props
+ * @throws UnsupportedRightException
*/
- char REMOVE_RIGHTS_MARKER = '-';
+ public MailboxACL(Properties props) throws UnsupportedRightException {
+ this(toMap(props));
+ }
+
+ public boolean equals(Object o) {
+ if (o instanceof MailboxACL) {
+ MailboxACL acl = (MailboxACL) o;
+ return Objects.equals(this.getEntries(), acl.getEntries());
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return Objects.hash(entries);
+ }
/**
* Apply the given ACL update on current ACL and return the result as a new ACL.
@@ -256,96 +707,144 @@ public interface MailboxACL {
* @return Copy of current ACL updated
* @throws UnsupportedRightException
*/
- MailboxACL apply(MailboxACLCommand aclUpdate) throws UnsupportedRightException;
+ public MailboxACL apply(ACLCommand aclUpdate) throws UnsupportedRightException {
+ switch (aclUpdate.getEditMode()) {
+ case ADD:
+ return union(aclUpdate.getEntryKey(), aclUpdate.getRights());
+ case REMOVE:
+ return except(aclUpdate.getEntryKey(), aclUpdate.getRights());
+ case REPLACE:
+ return replace(aclUpdate.getEntryKey(), aclUpdate.getRights());
+ }
+ throw new RuntimeException("Unknown edit mode");
+ }
/**
* Performs the set theoretic operation of relative complement of toRemove
* {@link MailboxACL} in this {@link MailboxACL}.
- *
+ *
* A schematic example: "user1:lr;user2:lrwt".except("user1:w;user2:t")
* returns "user1:lr;user2:lrw".
- *
+ *
* Implementations must return a new unmodifiable instance of
* {@link MailboxACL}. However, implementations may decide to return this or
* toRemove parameter value in case the result would be equal to the
* respective one of those.
- *
+ *
* Implementations must ensure that the result does not contain entries with
* empty rigths. E.g. "user1:lr;user2:lrwt".except("user1:lr") should return
* "user2:lrwt" rather than "user1:;user2:lrwt"
- *
+ *
* @param toRemove
* @return
* @throws UnsupportedRightException
*/
- MailboxACL except(MailboxACL toRemove) throws UnsupportedRightException;
+ public MailboxACL except(MailboxACL other) throws UnsupportedRightException {
+ return new MailboxACL(entries.entrySet()
+ .stream()
+ .map(entry -> Pair.of(entry.getKey(),
+ Optional.ofNullable(other.getEntries().get(entry.getKey()))
+ .map(Throwing.function(exceptValue -> entry.getValue().except(exceptValue)))
+ .orElse(entry.getValue())))
+ .filter(pair -> !pair.getValue().isEmpty())
+ .collect(Guavate.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
+ }
- /**
- * TODO except.
- *
- * @param key
- * @param toRemove
- * @return
- * @throws UnsupportedRightException
- */
- MailboxACL except(MailboxACLEntryKey key, MailboxACLRights toRemove) throws UnsupportedRightException;
+ public MailboxACL except(EntryKey key, Rfc4314Rights mailboxACLRights) throws UnsupportedRightException {
+ return except(new MailboxACL(new Entry(key, mailboxACLRights)));
+ }
/**
* {@link Map} of entries.
- *
+ *
* @return the entries.
*/
- Map<MailboxACLEntryKey, MailboxACLRights> getEntries();
+ public Map<EntryKey, Rfc4314Rights> getEntries() {
+ return entries;
+ }
/**
* Replaces the entry corresponding to the given {@code key} with
* {@code toAdd}link MailboxACLRights}.
- *
+ *
* Implementations must return a new unmodifiable instance of
* {@link MailboxACL}. However, implementations may decide to return this in
* case the result would be equal to it.
- *
+ *
* Implementations must ensure that the result does not contain entries with
* empty rigths. E.g. "user1:lr;user2:lrwt".replace("user1",
* MailboxACLRights.EMPTY) should return "user2:lrwt" rather than
* "user1:;user2:lrwt". The same result should be returned by
* "user1:lr;user2:lrwt".replace("user1", null).
- *
+ *
* @param key
* @param toAdd
* @return
* @throws UnsupportedRightException
*/
- MailboxACL replace(MailboxACLEntryKey key, MailboxACLRights toAdd) throws UnsupportedRightException;
+ public MailboxACL replace(EntryKey key, Rfc4314Rights replacement) throws UnsupportedRightException {
+ if (entries.containsKey(key)) {
+ return new MailboxACL(
+ entries.entrySet()
+ .stream()
+ .map(entry -> Pair.of(entry.getKey(),
+ entry.getKey().equals(key) ? replacement : entry.getValue()))
+ .filter(pair -> pair.getValue() != null && !pair.getValue().isEmpty())
+ .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)));
+ } else {
+ return new MailboxACL(
+ ImmutableMap.<EntryKey, Rfc4314Rights>builder()
+ .putAll(entries)
+ .put(key, replacement)
+ .build());
+ }
+ }
+
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("entries", entries)
+ .toString();
+ }
/**
* Performs the set theoretic operation of union of this {@link MailboxACL}
* and toAdd {@link MailboxACL}.
- *
+ *
* A schematic example:
* "user1:lr;user2:lrwt".union("user1:at;-$group1:lrwt") returns
* "user1:alrt;user2:lrwt;-$group1:lrwt".
- *
+ *
* Implementations must return a new unmodifiable instance of
* {@link MailboxACL}. However, implementations may decide to return this or
* toAdd parameter value in case the result would be equal to the respective
* one of those.
- *
- *
+ *
+ *
* @param toAdd
* @return
* @throws UnsupportedRightException
*/
- MailboxACL union(MailboxACL toAdd) throws UnsupportedRightException;
+ public MailboxACL union(MailboxACL other) throws UnsupportedRightException {
+ return new MailboxACL(
+ Stream.concat(
+ this.entries.entrySet().stream(),
+ other.getEntries().entrySet().stream())
+ .collect(Guavate.toImmutableListMultimap(Map.Entry::getKey, Map.Entry::getValue))
+ .asMap()
+ .entrySet()
+ .stream()
+ .map(entry -> Pair.of(entry.getKey(),
+ entry.getValue()
+ .stream()
+ .reduce(
+ new Rfc4314Rights(),
+ Throwing.binaryOperator(Rfc4314Rights::union))))
+ .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)));
+ }
+
- /**
- * TODO union.
- *
- * @param key
- * @param toAdd
- * @return
- * @throws UnsupportedRightException
- */
- MailboxACL union(MailboxACLEntryKey key, MailboxACLRights toAdd) throws UnsupportedRightException;
+ public MailboxACL union(EntryKey key, Rfc4314Rights mailboxACLRights) throws UnsupportedRightException {
+ return union(new MailboxACL(new Entry(key, mailboxACLRights)));
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org