Index: imap/api/src/main/java/org/apache/james/imap/api/ImapConstants.java =================================================================== --- imap/api/src/main/java/org/apache/james/imap/api/ImapConstants.java (revision 1232377) +++ imap/api/src/main/java/org/apache/james/imap/api/ImapConstants.java (working copy) @@ -103,6 +103,8 @@ public static final String SUPPORTS_QRESYNC = "QRESYNC"; + public static final String SUPPORTS_ACL = "ACL"; + public static final String INBOX_NAME = "INBOX"; public static final String MIME_TYPE_TEXT = "TEXT"; @@ -212,7 +214,17 @@ public static final String APPEND_COMMAND_NAME = "APPEND"; public static final String ENABLE_COMMAND_NAME = "ENABLE"; + + public static final String GETACL_COMMAND_NAME = "GETACL"; + public static final String SETACL_COMMAND_NAME = "SETACL"; + + public static final String DELETEACL_COMMAND_NAME = "DELETEACL"; + + public static final String LISTRIGHTS_COMMAND_NAME = "LISTRIGHTS"; + + public static final String MYRIGHTS_COMMAND_NAME = "MYRIGHTS"; + public static final String LIST_RESPONSE_NAME = "LIST"; public static final String XLIST_RESPONSE_NAME = "XLIST"; @@ -221,6 +233,8 @@ public static final String SEARCH_RESPONSE_NAME = "SEARCH"; + public static final String ACL_RESPONSE_NAME = "ACL"; + public static final String NAME_ATTRIBUTE_NOINFERIORS = "\\Noinferiors"; public static final String NAME_ATTRIBUTE_NOSELECT = "\\Noselect"; Index: imap/message/src/main/java/org/apache/james/imap/decode/parser/GetACLCommandParser.java =================================================================== --- imap/message/src/main/java/org/apache/james/imap/decode/parser/GetACLCommandParser.java (revision 0) +++ imap/message/src/main/java/org/apache/james/imap/decode/parser/GetACLCommandParser.java (revision 0) @@ -0,0 +1,49 @@ +/**************************************************************** + * 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.imap.decode.parser; + +import org.apache.james.imap.api.ImapCommand; +import org.apache.james.imap.api.ImapConstants; +import org.apache.james.imap.api.ImapMessage; +import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.decode.DecodingException; +import org.apache.james.imap.decode.ImapRequestLineReader; +import org.apache.james.imap.decode.base.AbstractImapCommandParser; +import org.apache.james.imap.message.request.GetACLRequest; + +/** + * GETACL Parser + * + * @author Peter Palaga + */ +public class GetACLCommandParser extends AbstractImapCommandParser { + + public GetACLCommandParser() { + super(ImapCommand.authenticatedStateCommand(ImapConstants.GETACL_COMMAND_NAME)); + } + + @Override + protected ImapMessage decode(ImapCommand command, ImapRequestLineReader request, String tag, ImapSession session) throws DecodingException { + final String mailboxName = request.mailbox(); + request.eol(); + return new GetACLRequest(tag, command, mailboxName); + } + +} Index: imap/message/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java =================================================================== --- imap/message/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java (revision 1232377) +++ imap/message/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java (working copy) @@ -72,8 +72,8 @@ // RFC2342 NAMESPACE _imapCommands.put(ImapConstants.NAMESPACE_COMMAND_NAME, NamespaceCommandParser.class); - // RFC2086 GETACL, SETACL, DELETEACL, LISTRIGHTS, MYRIGHTS - // _imapCommands.put( "GETACL", GetAclCommand.class ); + // RFC4314 GETACL, SETACL, DELETEACL, LISTRIGHTS, MYRIGHTS + _imapCommands.put(ImapConstants.GETACL_COMMAND_NAME, GetACLCommandParser.class ); // _imapCommands.put( "SETACL", SetAclCommand.class ); // _imapCommands.put( "DELETEACL", DeleteAclCommand.class ); // _imapCommands.put( "LISTRIGHTS", ListRightsCommand.class ); Index: imap/message/src/main/java/org/apache/james/imap/encode/ACLResponseEncoder.java =================================================================== --- imap/message/src/main/java/org/apache/james/imap/encode/ACLResponseEncoder.java (revision 0) +++ imap/message/src/main/java/org/apache/james/imap/encode/ACLResponseEncoder.java (revision 0) @@ -0,0 +1,84 @@ +/**************************************************************** + * 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.imap.encode; + +import java.io.IOException; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.james.imap.api.ImapConstants; +import org.apache.james.imap.api.ImapMessage; +import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.encode.base.AbstractChainedImapEncoder; +import org.apache.james.imap.message.response.ACLResponse; +import org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey; +import org.apache.james.mailbox.MailboxACL.MailboxACLRights; + +/** + * ACL Response Encoder. + * + * @author Peter Palaga + */ +public class ACLResponseEncoder extends AbstractChainedImapEncoder { + + public ACLResponseEncoder(ImapEncoder next) { + super(next); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.imap.encode.base.AbstractChainedImapEncoder#doEncode + * (org.apache.james.imap.api.ImapMessage, + * org.apache.james.imap.encode.ImapResponseComposer, + * org.apache.james.imap.api.process.ImapSession) + */ + protected void doEncode(ImapMessage acceptableMessage, ImapResponseComposer composer, ImapSession session) throws IOException { + final ACLResponse aclResponse = (ACLResponse) acceptableMessage; + final Map entries = aclResponse.getAcl().getEntries(); + composer.untagged(); + composer.commandName(ImapConstants.ACL_RESPONSE_NAME); + + String mailboxName = aclResponse.getMailboxName(); + composer.quote(mailboxName == null ? "" : mailboxName); + + if (entries != null) { + for (Entry entry : entries.entrySet()) { + String identifier = entry.getKey().serialize(); + composer.quote(identifier); + String rights = entry.getValue().serialize(); + composer.quote(rights == null ? "" : rights); + } + } + composer.end(); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.imap.encode.base.AbstractChainedImapEncoder#isAcceptable + * (org.apache.james.imap.api.ImapMessage) + */ + public boolean isAcceptable(ImapMessage message) { + return message instanceof ACLResponse; + } +} Index: imap/message/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java =================================================================== --- imap/message/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java (revision 1232377) +++ imap/message/src/main/java/org/apache/james/imap/encode/main/DefaultImapEncoderFactory.java (working copy) @@ -20,6 +20,7 @@ package org.apache.james.imap.encode.main; import org.apache.james.imap.api.display.Localizer; +import org.apache.james.imap.encode.ACLResponseEncoder; import org.apache.james.imap.encode.AuthenticateResponseEncoder; import org.apache.james.imap.encode.CapabilityResponseEncoder; import org.apache.james.imap.encode.ContinuationResponseEncoder; @@ -41,6 +42,7 @@ import org.apache.james.imap.encode.VanishedResponseEncoder; import org.apache.james.imap.encode.XListResponseEncoder; import org.apache.james.imap.encode.base.EndImapEncoder; +import org.apache.james.imap.message.response.ACLResponse; /** * TODO: perhaps a POJO would be better @@ -59,7 +61,9 @@ */ public static final ImapEncoder createDefaultEncoder(final Localizer localizer, final boolean neverAddBodyStructureExtensions) { final EndImapEncoder endImapEncoder = new EndImapEncoder(); - final NamespaceResponseEncoder namespaceEncoder = new NamespaceResponseEncoder(endImapEncoder); + + final ACLResponseEncoder aclResponseEncoder = new ACLResponseEncoder(endImapEncoder); + final NamespaceResponseEncoder namespaceEncoder = new NamespaceResponseEncoder(aclResponseEncoder); final StatusResponseEncoder statusResponseEncoder = new StatusResponseEncoder(namespaceEncoder, localizer); final RecentResponseEncoder recentResponseEncoder = new RecentResponseEncoder(statusResponseEncoder); final FetchResponseEncoder fetchResponseEncoder = new FetchResponseEncoder(recentResponseEncoder, neverAddBodyStructureExtensions); Index: imap/message/src/main/java/org/apache/james/imap/message/request/GetACLRequest.java =================================================================== --- imap/message/src/main/java/org/apache/james/imap/message/request/GetACLRequest.java (revision 0) +++ imap/message/src/main/java/org/apache/james/imap/message/request/GetACLRequest.java (revision 0) @@ -0,0 +1,41 @@ +/**************************************************************** + * 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.imap.message.request; + +import org.apache.james.imap.api.ImapCommand; + +/** + * GETACL Request. + * + * @author Peter Palaga + */ +public class GetACLRequest extends AbstractImapRequest { + private final String mailboxName; + + public GetACLRequest(String tag, ImapCommand command, String mailboxName) { + super(tag, command); + this.mailboxName = mailboxName; + } + + public String getMailboxName() { + return mailboxName; + } + +} Index: imap/message/src/main/java/org/apache/james/imap/message/response/ACLResponse.java =================================================================== --- imap/message/src/main/java/org/apache/james/imap/message/response/ACLResponse.java (revision 0) +++ imap/message/src/main/java/org/apache/james/imap/message/response/ACLResponse.java (revision 0) @@ -0,0 +1,92 @@ +/**************************************************************** + * 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.imap.message.response; + +import java.util.Map.Entry; + +import org.apache.james.imap.api.ImapConstants; +import org.apache.james.imap.api.display.CharsetUtil; +import org.apache.james.imap.api.message.response.ImapResponseMessage; +import org.apache.james.mailbox.MailboxACL; +import org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey; +import org.apache.james.mailbox.MailboxACL.MailboxACLRights; + +/** + * ACL Response. + * + * @author Peter Palaga + */ +public final class ACLResponse implements ImapResponseMessage { + private final MailboxACL acl; + + private final String mailboxName; + + public ACLResponse(String mailboxName, MailboxACL acl) { + super(); + //FIXME encodeModifiedUTF7 invocations should probably be moved to org.apache.james.imap.encode.ImapResponseComposer analogically to org.apache.james.imap.decode.ImapRequestLineReader.mailbox() + this.mailboxName = CharsetUtil.encodeModifiedUTF7(mailboxName); + this.acl = acl; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ACLResponse) { + ACLResponse other = (ACLResponse) o; + return this.acl == other.acl || (this.acl != null && this.acl.equals(other.acl)) + && this.mailboxName == other.mailboxName || (this.mailboxName != null && this.mailboxName.equals(other.mailboxName)) + ; + } + return false; + } + + public MailboxACL getAcl() { + return acl; + } + + public String getMailboxName() { + return mailboxName; + } + + @Override + public int hashCode() { + final int PRIME = 31; + return PRIME * acl.hashCode() + mailboxName.hashCode(); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder() + .append(ImapConstants.ACL_RESPONSE_NAME) + .append(' ') + .append(mailboxName); + + for (Entry en : acl.getEntries().entrySet()) { + result + .append(' ') + .append(en.getKey().toString()) + .append(' ') + .append(en.getValue().toString()) + ; + } + + return result.toString(); + }; + +} Index: imap/processor/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java =================================================================== --- imap/processor/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java (revision 1232377) +++ imap/processor/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java (working copy) @@ -84,7 +84,11 @@ final UnselectProcessor unselectProcessor = new UnselectProcessor(startTLSProcessor, mailboxManager, statusResponseFactory); final CompressProcessor compressProcessor = new CompressProcessor(unselectProcessor, statusResponseFactory); - final EnableProcessor enableProcessor = new EnableProcessor(compressProcessor, mailboxManager, statusResponseFactory); + + final GetACLProcessor getACLProcessor = new GetACLProcessor(compressProcessor, mailboxManager, statusResponseFactory); + + final EnableProcessor enableProcessor = new EnableProcessor(getACLProcessor, mailboxManager, statusResponseFactory); + // add for QRESYNC enableProcessor.addProcessor(selectProcessor); @@ -108,6 +112,8 @@ // Add to announce QRESYNC capabilityProcessor.addProcessor(selectProcessor); + + capabilityProcessor.addProcessor(getACLProcessor); return enableProcessor; Index: imap/processor/src/main/java/org/apache/james/imap/processor/GetACLProcessor.java =================================================================== --- imap/processor/src/main/java/org/apache/james/imap/processor/GetACLProcessor.java (revision 0) +++ imap/processor/src/main/java/org/apache/james/imap/processor/GetACLProcessor.java (revision 0) @@ -0,0 +1,114 @@ +/**************************************************************** + * 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.imap.processor; + +import java.util.Collections; +import java.util.List; + +import org.apache.james.imap.api.ImapCommand; +import org.apache.james.imap.api.ImapConstants; +import org.apache.james.imap.api.ImapSessionUtils; +import org.apache.james.imap.api.display.HumanReadableText; +import org.apache.james.imap.api.message.response.StatusResponseFactory; +import org.apache.james.imap.api.process.ImapProcessor; +import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.message.request.GetACLRequest; +import org.apache.james.imap.message.response.ACLResponse; +import org.apache.james.mailbox.InsufficientRightsException; +import org.apache.james.mailbox.MailboxException; +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxNotFoundException; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.MessageManager.MetaData; +import org.apache.james.mailbox.MessageManager.MetaData.FetchGroup; +import org.apache.james.mailbox.SimpleMailboxACL.Rfc4314Rights; + +/** + * GETACL Processor. + * + * @author Peter Palaga + */ +public class GetACLProcessor extends AbstractMailboxProcessor implements CapabilityImplementingProcessor { + + private static final List CAPABILITIES = Collections.singletonList(ImapConstants.SUPPORTS_ACL); + + public GetACLProcessor(ImapProcessor next, MailboxManager mailboxManager, StatusResponseFactory factory) { + super(GetACLRequest.class, next, mailboxManager, factory); + } + + @Override + protected void doProcess(GetACLRequest message, ImapSession session, String tag, ImapCommand command, Responder responder) { + + final MailboxManager mailboxManager = getMailboxManager(); + final MailboxSession mailboxSession = ImapSessionUtils.getMailboxSession(session); + try { + String mailboxName = message.getMailboxName(); + + MessageManager messageManager = mailboxManager.getMailbox(buildFullPath(session, mailboxName), mailboxSession); + + /* + * RFC 4314 section 6. + * An implementation MUST make sure the ACL commands themselves do + * not give information about mailboxes with appropriately + * restricted ACLs. For example, when a user agent executes a GETACL + * command on a mailbox that the user has no permission to LIST, the + * server would respond to that request with the same error that + * would be used if the mailbox did not exist, thus revealing no + * existence information, much less the mailbox’s ACL. + */ + if (!messageManager.hasRight(Rfc4314Rights.l_Lookup_RIGHT, mailboxSession)) { + throw new MailboxNotFoundException(mailboxName); + } + + /* RFC 4314 section 4. */ + if (!messageManager.hasRight(Rfc4314Rights.a_Administer_RIGHT, mailboxSession)) { + throw new InsufficientRightsException(); + } + + MetaData metaData = messageManager.getMetaData(false, mailboxSession, FetchGroup.NO_COUNT); + ACLResponse aclResponse = new ACLResponse(mailboxName, metaData.getACL()); + responder.respond(aclResponse); + okComplete(command, tag, responder); + // FIXME should we send unsolicited responses here? + // unsolicitedResponses(session, responder, false); + } catch (MailboxNotFoundException e) { + no(command, tag, responder, HumanReadableText.MAILBOX_NOT_FOUND); + } catch (InsufficientRightsException e) { + // FIXME: be more specific in the human readable text. + no(command, tag, responder, HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING); + } catch (MailboxException e) { + // FIXME: be more specific in the human readable text. + no(command, tag, responder, HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING); + } + + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.imap.processor.CapabilityImplementingProcessor# + * getImplementedCapabilities(org.apache.james.imap.api.process.ImapSession) + */ + public List getImplementedCapabilities(ImapSession session) { + return CAPABILITIES; + } + +} Index: imap/processor/src/test/java/org/apache/james/imap/processor/GetACLProcessorTest.java =================================================================== --- imap/processor/src/test/java/org/apache/james/imap/processor/GetACLProcessorTest.java (revision 0) +++ imap/processor/src/test/java/org/apache/james/imap/processor/GetACLProcessorTest.java (revision 0) @@ -0,0 +1,218 @@ +/**************************************************************** + * 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.imap.processor; + +import org.apache.james.imap.api.ImapCommand; +import org.apache.james.imap.api.ImapConstants; +import org.apache.james.imap.api.ImapSessionState; +import org.apache.james.imap.api.ImapSessionUtils; +import org.apache.james.imap.api.message.response.StatusResponse; +import org.apache.james.imap.api.process.ImapProcessor; +import org.apache.james.imap.api.process.ImapProcessor.Responder; +import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.message.request.GetACLRequest; +import org.apache.james.imap.message.response.ACLResponse; +import org.apache.james.imap.message.response.UnpooledStatusResponseFactory; +import org.apache.james.mailbox.MailboxACL; +import org.apache.james.mailbox.MailboxException; +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxNotFoundException; +import org.apache.james.mailbox.MailboxPath; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MailboxSession.User; +import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.MessageManager.MetaData; +import org.apache.james.mailbox.MessageManager.MetaData.FetchGroup; +import org.apache.james.mailbox.SimpleMailboxACL; +import org.apache.james.mailbox.SimpleMailboxACL.Rfc4314Rights; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.jmock.integration.junit4.JMock; +import org.jmock.integration.junit4.JUnit4Mockery; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * GetACLProcessor Test. + * + * @author Peter Palaga + */ +@RunWith(JMock.class) +public class GetACLProcessorTest { + + private static final String MAILBOX_NAME = ImapConstants.INBOX_NAME; + private static final String USER_1 = "user1"; + + ImapSession imapSessionStub; + MailboxManager mailboxManagerStub; + MailboxSession mailboxSessionStub; + MessageManager messageManagerStub; + MetaData metaDataStub; + Mockery mockery = new JUnit4Mockery(); + GetACLRequest namespaceRequest; + UnpooledStatusResponseFactory statusResponseFactory; + GetACLProcessor subject; + User user1Stub; + + private Expectations prepareRightsExpectations() throws MailboxException { + return new Expectations() { + { + + allowing(imapSessionStub).getAttribute(ImapSessionUtils.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY); + will(returnValue(mailboxSessionStub)); + + allowing(imapSessionStub).getState(); + will(returnValue(ImapSessionState.AUTHENTICATED)); + + allowing(mailboxSessionStub).getUser(); + will(returnValue(user1Stub)); + + allowing(user1Stub).getUserName(); + will(returnValue(USER_1)); + + allowing(mailboxManagerStub).startProcessingRequest(with(same(mailboxSessionStub))); + allowing(mailboxManagerStub).endProcessingRequest(with(same(mailboxSessionStub))); + + allowing(messageManagerStub).getMetaData(with(any(Boolean.class)), with(same(mailboxSessionStub)), with(any(FetchGroup.class))); + will(returnValue(metaDataStub)); + + } + }; + } + + @Before + public void setUp() throws Exception { + statusResponseFactory = new UnpooledStatusResponseFactory(); + mailboxManagerStub = mockery.mock(MailboxManager.class); + subject = new GetACLProcessor(mockery.mock(ImapProcessor.class), mailboxManagerStub, statusResponseFactory); + imapSessionStub = mockery.mock(ImapSession.class); + mailboxSessionStub = mockery.mock(MailboxSession.class); + user1Stub = mockery.mock(User.class); + messageManagerStub = mockery.mock(MessageManager.class); + metaDataStub = mockery.mock(MetaData.class); + + namespaceRequest = new GetACLRequest("TAG", ImapCommand.anyStateCommand("Name"), MAILBOX_NAME); + + } + + @Test + public void testNoListRight() throws Exception { + + Expectations expectations = prepareRightsExpectations(); + expectations.allowing(messageManagerStub).hasRight(expectations.with(Expectations.equal(Rfc4314Rights.l_Lookup_RIGHT)), expectations.with(Expectations.same(mailboxSessionStub))); + expectations.will(Expectations.returnValue(false)); + + expectations.allowing(mailboxManagerStub).getMailbox(expectations.with(Expectations.any(MailboxPath.class)), expectations.with(Expectations.any(MailboxSession.class))); + expectations.will(Expectations.returnValue(messageManagerStub)); + + mockery.checking(expectations); + + final Responder responderMock = mockery.mock(Responder.class); + mockery.checking(new Expectations() { + { + oneOf(responderMock).respond(with(new StatusResponseTypeMatcher(StatusResponse.Type.NO))); + } + }); + + subject.doProcess(namespaceRequest, responderMock, imapSessionStub); + + } + + @Test + public void testNoAdminRight() throws Exception { + + Expectations expectations = prepareRightsExpectations(); + expectations.allowing(messageManagerStub).hasRight(expectations.with(Expectations.equal(Rfc4314Rights.l_Lookup_RIGHT)), expectations.with(Expectations.same(mailboxSessionStub))); + expectations.will(Expectations.returnValue(true)); + + expectations.allowing(messageManagerStub).hasRight(expectations.with(Expectations.equal(Rfc4314Rights.a_Administer_RIGHT)), expectations.with(Expectations.same(mailboxSessionStub))); + expectations.will(Expectations.returnValue(false)); + + expectations.allowing(mailboxManagerStub).getMailbox(expectations.with(Expectations.any(MailboxPath.class)), expectations.with(Expectations.any(MailboxSession.class))); + expectations.will(Expectations.returnValue(messageManagerStub)); + + mockery.checking(expectations); + + final Responder responderMock = mockery.mock(Responder.class); + mockery.checking(new Expectations() { + { + oneOf(responderMock).respond(with(new StatusResponseTypeMatcher(StatusResponse.Type.NO))); + } + }); + + subject.doProcess(namespaceRequest, responderMock, imapSessionStub); + + } + + @Test + public void testInexistentMailboxName() throws Exception { + Expectations expectations = prepareRightsExpectations(); + + expectations.allowing(mailboxManagerStub).getMailbox(expectations.with(Expectations.any(MailboxPath.class)), expectations.with(Expectations.any(MailboxSession.class))); + expectations.will(Expectations.throwException(new MailboxNotFoundException(MAILBOX_NAME))); + + mockery.checking(expectations); + + final Responder responderMock = mockery.mock(Responder.class); + mockery.checking(new Expectations() { + { + oneOf(responderMock).respond(with(new StatusResponseTypeMatcher(StatusResponse.Type.NO))); + } + }); + + subject.doProcess(namespaceRequest, responderMock, imapSessionStub); + } + + @Test + public void testSufficientRights() throws Exception { + + final MailboxACL acl = SimpleMailboxACL.OWNER_FULL_ACL; + + Expectations expectations = prepareRightsExpectations(); + + expectations.allowing(mailboxManagerStub).getMailbox(expectations.with(Expectations.any(MailboxPath.class)), expectations.with(Expectations.any(MailboxSession.class))); + expectations.will(Expectations.returnValue(messageManagerStub)); + + expectations.allowing(messageManagerStub).hasRight(expectations.with(Expectations.equal(Rfc4314Rights.l_Lookup_RIGHT)), expectations.with(Expectations.same(mailboxSessionStub))); + expectations.will(Expectations.returnValue(true)); + + expectations.allowing(messageManagerStub).hasRight(expectations.with(Expectations.equal(Rfc4314Rights.a_Administer_RIGHT)), expectations.with(Expectations.same(mailboxSessionStub))); + expectations.will(Expectations.returnValue(true)); + + + expectations.allowing(metaDataStub).getACL(); + expectations.will(Expectations.returnValue(acl)); + + mockery.checking(expectations); + + final ACLResponse response = new ACLResponse(MAILBOX_NAME, acl); + final Responder responderMock = mockery.mock(Responder.class); + mockery.checking(new Expectations() { + { + oneOf(responderMock).respond(with(equal(response))); + oneOf(responderMock).respond(with(new StatusResponseTypeMatcher(StatusResponse.Type.OK))); + } + }); + + subject.doProcess(namespaceRequest, responderMock, imapSessionStub); + } + +} Index: imap/processor/src/test/java/org/apache/james/imap/processor/StatusResponseTypeMatcher.java =================================================================== --- imap/processor/src/test/java/org/apache/james/imap/processor/StatusResponseTypeMatcher.java (revision 0) +++ imap/processor/src/test/java/org/apache/james/imap/processor/StatusResponseTypeMatcher.java (revision 0) @@ -0,0 +1,57 @@ +/* + * 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.imap.processor; + +import org.apache.james.imap.api.message.response.StatusResponse; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; + +/** + * A matcher for {@link StatusResponse} objects, whereby only their + * serverResponseType field is significant. is significant. + * + * @author Peter Palaga + */ +public class StatusResponseTypeMatcher extends BaseMatcher { + private final org.apache.james.imap.api.message.response.StatusResponse.Type serverResponseType; + + public StatusResponseTypeMatcher(org.apache.james.imap.api.message.response.StatusResponse.Type responseCode) { + super(); + this.serverResponseType = responseCode; + } + + @Override + public boolean matches(Object o) { + if (o instanceof StatusResponse) { + StatusResponse sr = (StatusResponse) o; + return this.serverResponseType.equals(sr.getServerResponseType()); + } + return false; + } + + @Override + public void describeTo(Description d) { + d.appendText(StatusResponse.class.getName()); + d.appendText(" with serverResponseType.equals(" + serverResponseType.name() + ")"); + + } + +} \ No newline at end of file Index: imap/processor/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java =================================================================== --- imap/processor/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java (revision 1232377) +++ imap/processor/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java (working copy) @@ -43,6 +43,7 @@ import org.apache.james.mailbox.BadCredentialsException; import org.apache.james.mailbox.Content; import org.apache.james.mailbox.Headers; +import org.apache.james.mailbox.MailboxACL.MailboxACLRight; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxListener; import org.apache.james.mailbox.MailboxManager; @@ -295,6 +296,12 @@ } + @Override + public boolean hasRight(MailboxACLRight right, MailboxSession session) throws MailboxException { + //FIXME: somebody should approve that always true is the proper result here + return true; + } + }; } Index: mailbox-integration-tester/src/test/java/org/apache/james/mailbox/hbase/host/HBaseHostSystem.java =================================================================== --- mailbox-integration-tester/src/test/java/org/apache/james/mailbox/hbase/host/HBaseHostSystem.java (revision 1232377) +++ mailbox-integration-tester/src/test/java/org/apache/james/mailbox/hbase/host/HBaseHostSystem.java (working copy) @@ -30,8 +30,12 @@ import org.apache.james.imap.processor.main.DefaultImapProcessorFactory; import org.apache.james.imap.tester.ImapHostSystem; import org.apache.james.imap.tester.base.HostSystem; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; import org.apache.james.mailbox.SubscriptionManager; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.hbase.HBaseMailboxManager; import org.apache.james.mailbox.hbase.HBaseMailboxSessionMapperFactory; import org.apache.james.mailbox.hbase.mail.HBaseModSeqProvider; @@ -83,7 +87,10 @@ final HBaseMailboxSessionMapperFactory mapperFactory = new HBaseMailboxSessionMapperFactory( conf, uidProvider, modSeqProvider); - mailboxManager = new HBaseMailboxManager(mapperFactory, userManager); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + mailboxManager = new HBaseMailboxManager(mapperFactory, userManager, aclResolver, groupMembershipResolver); mailboxManager.init(); SubscriptionManager subscriptionManager = new StoreSubscriptionManager(mapperFactory); Index: mailbox-integration-tester/src/test/java/org/apache/james/mailbox/inmemory/host/InMemoryHostSystem.java =================================================================== --- mailbox-integration-tester/src/test/java/org/apache/james/mailbox/inmemory/host/InMemoryHostSystem.java (revision 1232377) +++ mailbox-integration-tester/src/test/java/org/apache/james/mailbox/inmemory/host/InMemoryHostSystem.java (working copy) @@ -25,7 +25,11 @@ import org.apache.james.imap.processor.main.DefaultImapProcessorFactory; import org.apache.james.imap.tester.ImapHostSystem; import org.apache.james.imap.tester.base.HostSystem; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; import org.apache.james.mailbox.store.MockAuthenticator; import org.apache.james.mailbox.store.StoreMailboxManager; @@ -59,7 +63,10 @@ private void initFields() throws MailboxException { userManager = new MockAuthenticator(); factory = new InMemoryMailboxSessionMapperFactory(); - mailboxManager = new StoreMailboxManager(factory, userManager); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + mailboxManager = new StoreMailboxManager(factory, userManager, aclResolver, groupMembershipResolver); mailboxManager.init(); final ImapProcessor defaultImapProcessorFactory = DefaultImapProcessorFactory.createDefaultProcessor(mailboxManager, new StoreSubscriptionManager(factory)); Index: mailbox-integration-tester/src/test/java/org/apache/james/mailbox/jcr/host/JCRHostSystem.java =================================================================== --- mailbox-integration-tester/src/test/java/org/apache/james/mailbox/jcr/host/JCRHostSystem.java (revision 1232377) +++ mailbox-integration-tester/src/test/java/org/apache/james/mailbox/jcr/host/JCRHostSystem.java (working copy) @@ -29,7 +29,11 @@ import org.apache.james.imap.processor.main.DefaultImapProcessorFactory; import org.apache.james.imap.tester.ImapHostSystem; import org.apache.james.imap.tester.base.HostSystem; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.jcr.GlobalMailboxSessionJCRRepository; import org.apache.james.mailbox.jcr.JCRMailboxManager; import org.apache.james.mailbox.jcr.JCRMailboxSessionMapperFactory; @@ -77,7 +81,10 @@ JCRModSeqProvider modSeqProvider = new JCRModSeqProvider(locker, sessionRepos); JCRMailboxSessionMapperFactory mf = new JCRMailboxSessionMapperFactory(sessionRepos, uidProvider, modSeqProvider); - mailboxManager = new JCRMailboxManager(mf, userManager, locker); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + mailboxManager = new JCRMailboxManager(mf, userManager, locker, aclResolver, groupMembershipResolver); mailboxManager.init(); final ImapProcessor defaultImapProcessorFactory = DefaultImapProcessorFactory.createDefaultProcessor(mailboxManager, new JCRSubscriptionManager(mf)); Index: mailbox-integration-tester/src/test/java/org/apache/james/mailbox/jpa/host/JPAHostSystem.java =================================================================== --- mailbox-integration-tester/src/test/java/org/apache/james/mailbox/jpa/host/JPAHostSystem.java (revision 1232377) +++ mailbox-integration-tester/src/test/java/org/apache/james/mailbox/jpa/host/JPAHostSystem.java (working copy) @@ -31,8 +31,12 @@ import org.apache.james.imap.processor.main.DefaultImapProcessorFactory; import org.apache.james.imap.tester.ImapHostSystem; import org.apache.james.imap.tester.base.HostSystem; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; import org.apache.james.mailbox.SubscriptionManager; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.jpa.JPAMailboxSessionMapperFactory; import org.apache.james.mailbox.jpa.JPASubscriptionManager; import org.apache.james.mailbox.jpa.mail.JPAModSeqProvider; @@ -109,7 +113,10 @@ JPAModSeqProvider modSeqProvider = new JPAModSeqProvider(locker, entityManagerFactory); JPAMailboxSessionMapperFactory mf = new JPAMailboxSessionMapperFactory(entityManagerFactory, uidProvider, modSeqProvider); - mailboxManager = new OpenJPAMailboxManager(mf, userManager, locker, false); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + mailboxManager = new OpenJPAMailboxManager(mf, userManager, locker, false, aclResolver, groupMembershipResolver); mailboxManager.init(); SubscriptionManager subscriptionManager = new JPASubscriptionManager(mf); Index: mailbox-integration-tester/src/test/java/org/apache/james/mailbox/maildir/host/MaildirHostSystem.java =================================================================== --- mailbox-integration-tester/src/test/java/org/apache/james/mailbox/maildir/host/MaildirHostSystem.java (revision 1232377) +++ mailbox-integration-tester/src/test/java/org/apache/james/mailbox/maildir/host/MaildirHostSystem.java (working copy) @@ -27,7 +27,11 @@ import org.apache.james.imap.processor.main.DefaultImapProcessorFactory; import org.apache.james.imap.tester.ImapHostSystem; import org.apache.james.imap.tester.base.HostSystem; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.maildir.MaildirMailboxSessionMapperFactory; import org.apache.james.mailbox.maildir.MaildirStore; import org.apache.james.mailbox.store.JVMMailboxPathLocker; @@ -54,7 +58,11 @@ MaildirStore store = new MaildirStore(MAILDIR_HOME + "/%user", locker); mailboxSessionMapperFactory = new MaildirMailboxSessionMapperFactory(store); StoreSubscriptionManager sm = new StoreSubscriptionManager(mailboxSessionMapperFactory); - mailboxManager = new StoreMailboxManager(mailboxSessionMapperFactory, userManager, locker); + + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + mailboxManager = new StoreMailboxManager(mailboxSessionMapperFactory, userManager, locker, aclResolver, groupMembershipResolver); mailboxManager.init(); final ImapProcessor defaultImapProcessorFactory = DefaultImapProcessorFactory.createDefaultProcessor(mailboxManager, sm); Index: mailbox/api/src/main/java/org/apache/james/mailbox/InsufficientRightsException.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/InsufficientRightsException.java (revision 0) +++ mailbox/api/src/main/java/org/apache/james/mailbox/InsufficientRightsException.java (revision 0) @@ -0,0 +1,48 @@ +/* + * 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.mailbox; + +/** + * Thrown when the rights granted to the given authenticated user do not suffice + * for the given action. + * + * @author Peter Palaga + */ +public class InsufficientRightsException extends MailboxSecurityException { + + /** + * + */ + private static final long serialVersionUID = 7310501567640913596L; + + public InsufficientRightsException() { + super(); + } + + public InsufficientRightsException(String message) { + super(message); + } + + public InsufficientRightsException(String msg, Exception cause) { + super(msg, cause); + } + +} Index: mailbox/api/src/main/java/org/apache/james/mailbox/MailboxACL.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/MailboxACL.java (revision 0) +++ mailbox/api/src/main/java/org/apache/james/mailbox/MailboxACL.java (revision 0) @@ -0,0 +1,263 @@ +/**************************************************************** + * 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.mailbox; + +import java.util.Map; + +/** + * 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)}. + * + * @author Peter Palaga + */ +public interface MailboxACL { + + /** + * 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 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(); + + /** + * Tells of what type is the name returned by {@link #getName()}. + * + * @return type of the name returned by {@link #getName()} + */ + NameType getNameType(); + + /** + * 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(); + + /** + * 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 http://wiki2.dovecot.org/SharedMailboxes/Shared: + * + * 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. + * + * @see MailboxACL#DEFAULT_GROUP_MARKER + * @see MailboxACL#DEFAULT_NEGATIVE_MARKER + * + * @return serialized form as a {@link String} + */ + String serialize(); + } + + /** + * Single right applicable to a mailbox. + */ + public interface MailboxACLRight { + /** + * Returns the char representation of this right. + * + * @return char representation of this right + */ + char getValue(); + } + + /** + * Iterable set of {@link MailboxACLRight}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. + */ + public interface MailboxACLRights extends Iterable { + + /** + * Tells whether the implementation supports the given right. + * + * @param right + * @return true if this supports the given right. + */ + boolean isSupported(MailboxACLRight right); + + /** + * Tells whether this conatins the given right. + * + * @param right + * @return + * @throws UnsupportedRightException + * iff the given right is not supported. + */ + boolean contains(MailboxACLRight right) throws UnsupportedRightException; + + /** + * 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 + * return this or toRemove parameter value in case the result would be + * equal to the respective one of those. + * + * @param toRemove + * @return + * @throws UnsupportedRightException + */ + public abstract MailboxACLRights except(MailboxACLRights toRemove) throws UnsupportedRightException; + + /** + * Returns a serialized form of this {@link MailboxACLRights} as + * {@link String}. + * + * @return a {@link String} + */ + String serialize(); + + /** + * Performs the set theoretic operation of union of this + * MailboxACLRights and toAdd MailboxACLRights. + * + * 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. + * + * @param toAdd + * @return union of this and toAdd + * @throws UnsupportedRightException + * + */ + public abstract MailboxACLRights union(MailboxACLRights toAdd) throws UnsupportedRightException; + + } + + /** + * Allows distinguishing between users, groups and special names (see + * {@link SpecialName}). + */ + enum NameType { + group, special, user + }; + + /** + * Special name literals. + */ + enum SpecialName { + anybody, authenticated, owner + }; + + /** + * Marks groups when (de)serializing {@link MailboxACLEntryKey}s. + * + * @see MailboxACLEntryKey#serialize() + */ + public static final char DEFAULT_GROUP_MARKER = '$'; + + /** + * Marks negative when (de)serializing {@link MailboxACLEntryKey}s. + * + * @see MailboxACLEntryKey#serialize() + */ + public static final char DEFAULT_NEGATIVE_MARKER = '-'; + + /** + * 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. + * + * @param toRemove + * @return + * @throws UnsupportedRightException + */ + MailboxACL except(MailboxACL toRemove) throws UnsupportedRightException; + + /** + * {@link Map} of entries. + * + * @return the entries. + */ + Map getEntries(); + + /** + * 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; + +} Index: mailbox/api/src/main/java/org/apache/james/mailbox/MailboxACLResolver.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/MailboxACLResolver.java (revision 0) +++ mailbox/api/src/main/java/org/apache/james/mailbox/MailboxACLResolver.java (revision 0) @@ -0,0 +1,126 @@ +/* + * 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.mailbox; + +import org.apache.james.mailbox.MailboxACL.MailboxACLRight; +import org.apache.james.mailbox.MailboxACL.MailboxACLRights; + +/** + * Implements the interpretation of ACLs. + * + * From RFC4314: It is possible for multiple identifiers in an access + * control list to apply to a given user. For example, an ACL may include rights + * to be granted to the identifier matching the user, one or more + * implementation-defined identifiers matching groups that include the user, + * and/or the identifier "anyone". How these rights are combined to determine + * the user’s access is implementation defined. An implementation may choose, + * for example, to use the union of the rights granted to the applicable + * identifiers. An implementation may instead choose, for example, to use only + * those rights granted to the most specific identifier present in the ACL. A + * client can determine the set of rights granted to the logged-in user for a + * given mailbox name by using the MYRIGHTS command. + * + * @author Peter Palaga + */ +public interface MailboxACLResolver { + + /** + * FIXME: where does GroupMembershipResolver belong? + */ + public interface GroupMembershipResolver { + /** + * Tests if the given user is a member of the given group. + * + * @param user + * @param group + * @return + */ + boolean isMember(String user, String group); + } + + /** + * Applies global ACL to the given resourceACL. From RFC 4314: + * An implementation [...] MAY force rights to always or never be granted to + * particular identifiers. + * + * @param resourceACL + * @param resourceOwnerIsGroup + * @return + * @throws UnsupportedRightException + */ + public abstract MailboxACL applyGlobalACL(MailboxACL resourceACL, boolean resourceOwnerIsGroup) throws UnsupportedRightException; + + /** + * Tells whether the given user has the given right granted on the basis of + * the given resourceACL. Global ACL (if there is any) should be applied + * within this method. + * + * @param requestUser + * the user for whom the given right is tested, possibly + * null when there is no authenticated user in the + * given context. + * @param groupMembershipResolver + * this resolver is used when checking whether any group rights + * contained in resourceACL are applicable for the requestUser. + * @param right + * the right which will be proven to apply for the given + * requestUser. + * @param resourceACL + * the ACL defining the access right for the resource in + * question. + * @param resourceOwner + * this user name is used as a replacement for the "owner" place + * holder in the resourceACL. + * @param resourceOwnerIsGroup + * true if the resourceOwner is a group of users, false + * otherwise. + * @return true if the given user has the given right for the given + * resource; false otherwise. + * @throws UnsupportedRightException + */ + boolean hasRight(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACLRight right, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException; + + /** + * Computes the rights which apply to the given user and resource. Global ACL (if there is any) should be applied + * within this method. + * + * @param requestUser + * the user for whom the rights are computed, possibly + * null when there is no authenticated user in the + * given context. + * @param groupMembershipResolver + * this resolver is used when checking whether any group rights + * contained in resourceACL are applicable for the requestUser. + * @param resourceACL + * the ACL defining the access right for the resource in + * question. + * @param resourceOwner + * this user name is used as a replacement for the "owner" place + * holder in the resourceACL. + * @param resourceOwnerIsGroup + * true if the resourceOwner is a group of users, false + * otherwise. + * @return the rights applicable for the given user and resource. + * @throws UnsupportedRightException + */ + public abstract MailboxACLRights listRights(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException; + +} Index: mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java (revision 1232377) +++ mailbox/api/src/main/java/org/apache/james/mailbox/MailboxListener.java (working copy) @@ -120,7 +120,25 @@ public abstract MailboxPath getNewPath(); } + /** + * A mailbox event related to updated ACL + */ + public abstract class MailboxACLUpdated extends MessageEvent { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public MailboxACLUpdated(MailboxSession session, MailboxPath path) { + super(session, path); + } + + public abstract MailboxACL getUpdatedACL(); + } + + /** * A mailbox event related to a message. */ public abstract class MessageEvent extends Event { @@ -200,5 +218,5 @@ public abstract MessageMetaData getMetaData(long uid); } - + } Index: mailbox/api/src/main/java/org/apache/james/mailbox/MailboxSecurityException.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/MailboxSecurityException.java (revision 0) +++ mailbox/api/src/main/java/org/apache/james/mailbox/MailboxSecurityException.java (revision 0) @@ -0,0 +1,47 @@ +/* + * 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.mailbox; + +/** + * Parent for security related exceptions. + * + * @author Peter Palaga + */ +public class MailboxSecurityException extends MailboxException { + + /** + * + */ + private static final long serialVersionUID = 4186633460326902649L; + + public MailboxSecurityException() { + super(); + } + + public MailboxSecurityException(String message) { + super(message); + } + + public MailboxSecurityException(String msg, Exception cause) { + super(msg, cause); + } + +} Index: mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java (revision 1232377) +++ mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java (working copy) @@ -27,6 +27,7 @@ import javax.mail.Flags; +import org.apache.james.mailbox.MailboxACL.MailboxACLRight; import org.apache.james.mailbox.MessageResult.FetchGroup; /** @@ -42,7 +43,8 @@ * @param mailboxSession * @return count * @throws MailboxException - * @deprecated use {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)} + * @deprecated use + * {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)} */ @Deprecated long getMessageCount(MailboxSession mailboxSession) throws MailboxException; @@ -52,18 +54,20 @@ * * @param session * @return writable - * @deprecated use {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)} + * @deprecated use + * {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)} */ @Deprecated boolean isWriteable(MailboxSession session); - - + /** - * Return true if {@link MessageResult#getModSeq()} is stored in a permanent way. + * Return true if {@link MessageResult#getModSeq()} is stored in a permanent + * way. * * @param session * @return modSeqPermanent - * @deprecated use {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)} + * @deprecated use + * {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)} */ boolean isModSeqPermanent(MailboxSession session); @@ -115,7 +119,8 @@ Map setFlags(Flags flags, boolean value, boolean replace, MessageRange set, MailboxSession mailboxSession) throws MailboxException; /** - * Appends a message to this mailbox. This method must return a higher UID as the last call in every case which also needs to be unique for the + * Appends a message to this mailbox. This method must return a higher UID + * as the last call in every case which also needs to be unique for the * lifetime of the mailbox. * * @@ -135,10 +140,12 @@ long appendMessage(InputStream msgIn, Date internalDate, MailboxSession mailboxSession, boolean isRecent, Flags flags) throws MailboxException; /** - * Gets messages in the given range. The messages may get fetched under the-hood in batches so the caller should check if {@link MessageResultIterator#getException()} - * returns null after {@link MessageResultIterator#hasNext()} returns false. - * + * Gets messages in the given range. The messages may get fetched under + * the-hood in batches so the caller should check if + * {@link MessageResultIterator#getException()} returns null + * after {@link MessageResultIterator#hasNext()} returns false. * + * * @param set * @param fetchGroup * data to fetch @@ -149,6 +156,18 @@ */ MessageResultIterator getMessages(MessageRange set, FetchGroup fetchGroup, MailboxSession mailboxSession) throws MailboxException; + /** + * Tells whether the given {@link MailboxSession}'s user has the given + * {@link MailboxACLRight} for this {@link MessageManager}'s mailbox. + * + * @param right + * @param session + * @return true if the given {@link MailboxSession}'s user has the given + * {@link MailboxACLRight} for this {@link MessageManager}'s + * mailbox; false otherwise. + * @throws MailboxException + */ + public boolean hasRight(MailboxACLRight right, MailboxSession session) throws MailboxException; /** * Gets current meta data for the mailbox.
@@ -173,33 +192,29 @@ public interface MetaData { /** - * Describes the optional data types which will get set in the {@link MetaData}. + * Describes the optional data types which will get set in the + * {@link MetaData}. * - * These are always set: - * - HIGHESTMODSEQ - * - PERMANENTFLAGS - * - UIDNEXT - * - UIDVALIDITY - * - MODSEQPERMANET - * - WRITABLE + * These are always set: - HIGHESTMODSEQ - PERMANENTFLAGS - UIDNEXT - + * UIDVALIDITY - MODSEQPERMANET - WRITABLE */ public enum FetchGroup { - + /** * Only include the message and recent count */ - NO_UNSEEN, - + NO_UNSEEN, + /** * Only include the unseen message and recent count */ - UNSEEN_COUNT, - + UNSEEN_COUNT, + /** * Only include the first unseen and the recent count */ - FIRST_UNSEEN, - + FIRST_UNSEEN, + /** * Only return the "always set" metadata as documented above */ @@ -207,7 +222,8 @@ }; /** - * Gets the UIDs of recent messages if requested or an empty {@link List} otherwise. + * Gets the UIDs of recent messages if requested or an empty + * {@link List} otherwise. * * @return the uids flagged RECENT in this mailbox, */ @@ -242,10 +258,11 @@ * @return the uid that will be assigned to the next appended message */ long getUidNext(); - + /** - * Return the highest mod-sequence for the mailbox. If this value has changed - * till the last check you can be sure that some changes where happen on the mailbox + * Return the highest mod-sequence for the mailbox. If this value has + * changed till the last check you can be sure that some changes where + * happen on the mailbox * * @return higestModSeq */ @@ -255,9 +272,9 @@ * Gets the number of messages that this mailbox contains. This is an * optional property.
* - * @return number of messages contained or -1 when this - * optional data has not be requested - * + * @return number of messages contained or -1 when this optional data + * has not be requested + * */ long getMessageCount(); @@ -287,13 +304,21 @@ * @return true if read-write, false if read only */ boolean isWriteable(); - + /** - * Return true if the mailbox does store the mod-sequences in a permanent way + * Return true if the mailbox does store the mod-sequences in a + * permanent way * * @return permanent */ boolean isModSeqPermanent(); + /** + * Returns the ACL concerning this mailbox. + * + * @return acl + */ + MailboxACL getACL(); + } } Index: mailbox/api/src/main/java/org/apache/james/mailbox/SimpleGroupMembershipResolver.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/SimpleGroupMembershipResolver.java (revision 0) +++ mailbox/api/src/main/java/org/apache/james/mailbox/SimpleGroupMembershipResolver.java (revision 0) @@ -0,0 +1,83 @@ +/* + * 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.mailbox; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; + +/** + * In memory {@link GroupMembershipResolver} implementation. There is no + * persistence. You will get only what you add. + * + * @author Peter Palaga + */ +public class SimpleGroupMembershipResolver implements GroupMembershipResolver { + + private static class Membership { + private final String group; + private final int hash; + private final String user; + + public Membership(String user, String group) { + super(); + this.group = group; + this.user = user; + + final int PRIME = 31; + this.hash = PRIME * this.group.hashCode() + this.user.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Membership) { + Membership other = (Membership) o; + return this.group == other.group || (this.group != null && this.group.equals(other.group)) && this.user == other.user || (this.user != null && this.user.equals(other.user)); + + } + return false; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public String toString() { + return group + ": " + user; + } + + } + + private Set memberships = new HashSet(32); + + public void addMembership(String group, String user) { + memberships.add(new Membership(user, group)); + } + + @Override + public boolean isMember(String user, String group) { + return memberships.contains(new Membership(user, group)); + } + +} Index: mailbox/api/src/main/java/org/apache/james/mailbox/SimpleMailboxACL.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/SimpleMailboxACL.java (revision 0) +++ mailbox/api/src/main/java/org/apache/james/mailbox/SimpleMailboxACL.java (revision 0) @@ -0,0 +1,994 @@ +/**************************************************************** + * 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.mailbox; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +/** + * Default implementation of {@link MailboxACL}. + * + * @author Peter Palaga + */ +public class SimpleMailboxACL implements MailboxACL { + + /** + * Supports only the Standard Rights of RFC 4314 section 2.1. The rights are + * stored as single bits in 32 bit int {@link #value} field. + */ + public static class Rfc4314Rights implements MailboxACLRights { + /** + * See RFC 4314 section 2.1.1. Obsolete Rights. + */ + public enum CompatibilityMode { + ck_detx, ckx_det, NO_COMPATIBILITY + } + + private class Rfc4314RightsIterator implements Iterator { + + int position = 0; + + public Rfc4314RightsIterator() { + super(); + nextPostion(); + } + + @Override + public boolean hasNext() { + return (value | (1 << position)) != 0; + } + + @Override + public MailboxACLRight next() { + if (!hasNext()) { + throw new IndexOutOfBoundsException("No next element at position " + position + " from " + FIELD_COUNT + " in " + Rfc4314RightsIterator.class.getName()); + } + return indexRightLookup[position]; + } + + /** + */ + private void nextPostion() { + while (position < FIELD_COUNT && (value | (1 << position)) != 0) { + position++; + } + } + + @Override + public void remove() { + throw new java.lang.UnsupportedOperationException("Cannot remove rights through this " + Rfc4314RightsIterator.class.getName()); + } + + } + + /** + * a - administer (perform SETACL/DELETEACL/GETACL/LISTRIGHTS) + * + */ + public static final char a_Administer = 'a'; + + static final int a_Administer_MASK = 1; + public static final MailboxACLRight a_Administer_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(a_Administer); + public static final char c_ObsoleteCreate = 'c'; + public static final char d_ObsoleteDelete = 'd'; + /** + * e - perform EXPUNGE and expunge as a part of CLOSE + * + */ + public static final char e_PerformExpunge = 'e'; + static final int e_PerformExpunge_MASK = 1 << 1; + public static final MailboxACLRight e_PerformExpunge_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(e_PerformExpunge); + public static final int EMPTY_MASK = 0; + public static final int FIELD_COUNT = 11; + /** + * i - insert (perform APPEND, COPY into mailbox) + * + */ + public static final char i_Insert = 'i'; + static final int i_Insert_MASK = 1 << 2; + + public static final MailboxACLRight i_Insert_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(i_Insert); + private static final char[] indexFlagLookup; + private static final MailboxACLRight[] indexRightLookup; + /** + * k - create mailboxes (CREATE new sub-mailboxes in any + * implementation-defined hierarchy, parent mailbox for the new mailbox + * name in RENAME) + * + */ + public static final char k_CreateMailbox = 'k'; + static final int k_CreateMailbox_MASK = 1 << 3; + public static final MailboxACLRight k_CreateMailbox_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(k_CreateMailbox); + /** + * l - lookup (mailbox is visible to LIST/LSUB commands, SUBSCRIBE + * mailbox) + * + */ + public static final char l_Lookup = 'l'; + static final int l_Lookup_MASK = 1 << 4; + public static final MailboxACLRight l_Lookup_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(l_Lookup); + /** + * p - post (send mail to submission address for mailbox, not enforced + * by IMAP4 itself) + * + */ + public static final char p_Post = 'p'; + static final int p_Post_MASK = 1 << 5; + public static final MailboxACLRight p_Post_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(p_Post); + + /** + * r - read (SELECT the mailbox, perform STATUS) + * + */ + public static final char r_Read = 'r'; + static final int r_Read_MASK = 1 << 6; + + public static final MailboxACLRight r_Read_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(r_Read); + /** + * s - keep seen/unseen information across sessions (set or clear \SEEN + * flag via STORE, also set \SEEN during APPEND/COPY/ FETCH BODY[...]) + * + */ + public static final char s_WriteSeenFlag = 's'; + + static final int s_WriteSeenFlag_MASK = 1 << 7; + + public static final MailboxACLRight s_WriteSeenFlag_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(s_WriteSeenFlag); + + public static final char t_DeleteMessages = 't'; + + /** + * t - delete messages (set or clear \DELETED flag via STORE, set + * \DELETED flag during APPEND/COPY) + * + */ + static final int t_DeleteMessages_MASK = 1 << 8; + public static final MailboxACLRight t_DeleteMessages_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(t_DeleteMessages); + /** + * w - write (set or clear flags other than \SEEN and \DELETED via + * STORE, also set them during APPEND/COPY) + * + */ + public static final char w_Write = 'w'; + static final int w_Write_MASK = 1 << 9; + public static final MailboxACLRight w_Write_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(w_Write); + /** + * x - delete mailbox (DELETE mailbox, old mailbox name in RENAME) + * + */ + public static final char x_DeleteMailbox = 'x'; + static final int x_DeleteMailbox_MASK = 1 << 10; + public static final MailboxACLRight x_DeleteMailbox_RIGHT = new SimpleMailboxACL.SimpleMailboxACLRight(x_DeleteMailbox); + static { + indexFlagLookup = new char[] { a_Administer, e_PerformExpunge, i_Insert, k_CreateMailbox, l_Lookup, p_Post, r_Read, s_WriteSeenFlag, t_DeleteMessages, w_Write, x_DeleteMailbox }; + indexRightLookup = new MailboxACLRight[] { a_Administer_RIGHT, e_PerformExpunge_RIGHT, i_Insert_RIGHT, k_CreateMailbox_RIGHT, l_Lookup_RIGHT, p_Post_RIGHT, r_Read_RIGHT, s_WriteSeenFlag_RIGHT, t_DeleteMessages_RIGHT, w_Write_RIGHT, x_DeleteMailbox_RIGHT }; + } + + private static int flagMaskLookup(char flag) throws UnsupportedRightException { + switch (flag) { + case a_Administer: + return a_Administer_MASK; + case e_PerformExpunge: + return e_PerformExpunge_MASK; + case i_Insert: + return i_Insert_MASK; + case k_CreateMailbox: + return k_CreateMailbox_MASK; + case l_Lookup: + return l_Lookup_MASK; + case p_Post: + return p_Post_MASK; + case r_Read: + return r_Read_MASK; + case s_WriteSeenFlag: + return s_WriteSeenFlag_MASK; + case t_DeleteMessages: + return t_DeleteMessages_MASK; + case w_Write: + return w_Write_MASK; + case x_DeleteMailbox: + return x_DeleteMailbox_MASK; + default: + throw new UnsupportedRightException(flag); + } + } + + /** + * See RFC 4314 section 2.1.1. Obsolete Rights. + */ + private CompatibilityMode compatibilityMode = CompatibilityMode.ckx_det; + + /** + * 32 bit int to store the rights. + */ + private final int value; + + private Rfc4314Rights() { + this.value = EMPTY_MASK; + } + + public Rfc4314Rights(boolean canAdminister, boolean canCreateMailbox, boolean canDeleteMailbox, boolean canDeleteMessages, boolean canInsert, boolean canLookup, boolean canPerformExpunge, boolean canPost, boolean canRead, boolean canWrite, boolean canWriteSeenFlag) { + super(); + int v = 0; + + if (canAdminister) { + v |= a_Administer_MASK; + } + if (canCreateMailbox) { + v |= k_CreateMailbox_MASK; + } + if (canDeleteMailbox) { + v |= x_DeleteMailbox_MASK; + } + if (canDeleteMessages) { + v |= t_DeleteMessages_MASK; + } + if (canInsert) { + v |= i_Insert_MASK; + } + if (canLookup) { + v |= l_Lookup_MASK; + } + if (canPerformExpunge) { + v |= e_PerformExpunge_MASK; + } + if (canPost) { + v |= p_Post_MASK; + } + if (canRead) { + v |= r_Read_MASK; + } + if (canWrite) { + v |= w_Write_MASK; + } + if (canWriteSeenFlag) { + v |= s_WriteSeenFlag_MASK; + } + + this.value = v; + + } + + private Rfc4314Rights(int value) throws UnsupportedRightException { + if ((value >> FIELD_COUNT) != 0) { + throw new UnsupportedRightException(); + } + this.value = value; + } + + public Rfc4314Rights(String serializedRfc4314Rights) throws UnsupportedRightException { + int v = 0; + + for (int i = 0; i < serializedRfc4314Rights.length(); i++) { + char flag = serializedRfc4314Rights.charAt(i); + switch (flag) { + case c_ObsoleteCreate: + switch (compatibilityMode) { + case ck_detx: + v |= k_CreateMailbox_MASK; + break; + case ckx_det: + v |= k_CreateMailbox_MASK; + v |= x_DeleteMailbox_MASK; + break; + case NO_COMPATIBILITY: + throw new UnsupportedRightException(flag); + default: + throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name()); + } + break; + case d_ObsoleteDelete: + switch (compatibilityMode) { + case ck_detx: + v |= e_PerformExpunge_MASK; + v |= t_DeleteMessages_MASK; + v |= x_DeleteMailbox_MASK; + break; + case ckx_det: + v |= e_PerformExpunge_MASK; + v |= t_DeleteMessages_MASK; + break; + case NO_COMPATIBILITY: + throw new UnsupportedRightException(flag); + default: + throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name()); + } + break; + default: + v |= flagMaskLookup(flag); + } + } + this.value = v; + + } + + public boolean contains(char flag) throws UnsupportedRightException { + + switch (flag) { + case c_ObsoleteCreate: + switch (compatibilityMode) { + case ck_detx: + return (value & k_CreateMailbox_MASK) != 0; + case ckx_det: + return (value & (k_CreateMailbox_MASK | x_DeleteMailbox_MASK)) != 0; + case NO_COMPATIBILITY: + throw new UnsupportedRightException(flag); + default: + throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name()); + } + case d_ObsoleteDelete: + switch (compatibilityMode) { + case ck_detx: + return (value & (e_PerformExpunge_MASK | t_DeleteMessages_MASK | x_DeleteMailbox_MASK)) != 0; + case ckx_det: + return (value & (e_PerformExpunge_MASK | t_DeleteMessages_MASK)) != 0; + case NO_COMPATIBILITY: + throw new UnsupportedRightException(flag); + default: + throw new IllegalStateException("Unexpected enum member: " + CompatibilityMode.class.getName() + "." + compatibilityMode.name()); + } + default: + return (value | flagMaskLookup(flag)) != 0; + } + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.mailbox.MailboxACL.MailboxACLRights#contains(org + * .apache.james.mailbox.MailboxACL.MailboxACLRight) + */ + @Override + public boolean contains(MailboxACLRight right) throws UnsupportedRightException { + return contains(right.getValue()); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Rfc4314Rights) { + return this.value == ((Rfc4314Rights) o).value; + } else if (o instanceof MailboxACLRights) { + try { + return this.value == new Rfc4314Rights(((MailboxACLRights) o).serialize()).value; + } catch (UnsupportedRightException e) { + throw new RuntimeException(e); + } + } else { + return false; + } + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.mailbox.MailboxACL.MailboxACLRights#except(org.apache + * .james.mailbox.MailboxACL.MailboxACLRights) + */ + @Override + public MailboxACLRights except(MailboxACLRights toRemove) throws UnsupportedRightException { + if (this.value == EMPTY_MASK) { + /* nothing to remove */ + return this; + } else if (toRemove instanceof Rfc4314Rights) { + Rfc4314Rights other = (Rfc4314Rights) toRemove; + if (other.value == EMPTY_MASK) { + /* toRemove is an identity element */ + return this; + } else { + return new Rfc4314Rights(this.value & (~((other).value))); + } + } else { + return new Rfc4314Rights(this.value & (~(new Rfc4314Rights(toRemove.serialize()).value))); + } + } + + public int getValue() { + return value; + } + + /** + * Returns {@link #value}. + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return value; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.mailbox.MailboxACL.MailboxACLRights#isSupported( + * org.apache.james.mailbox.MailboxACL.MailboxACLRight) + */ + @Override + public boolean isSupported(MailboxACLRight right) { + try { + contains(right.getValue()); + return true; + } catch (UnsupportedRightException e) { + return false; + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() { + return new Rfc4314RightsIterator(); + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.mailbox.MailboxACL.MailboxACLRights#serialize() + */ + @Override + public String serialize() { + StringBuilder result = new StringBuilder(FIELD_COUNT); + for (int i = 0; i < FIELD_COUNT; i++) { + if ((value & (1 << i)) != 0) { + result.append(indexFlagLookup[i]); + } + } + return result.toString(); + } + + /** + * Returns {@link #serialize()} + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return serialize(); + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.mailbox.MailboxACL.MailboxACLRights#union(org.apache + * .james.mailbox.MailboxACL.MailboxACLRights) + */ + @Override + public MailboxACLRights union(MailboxACLRights toAdd) throws UnsupportedRightException { + if (this.value == EMPTY_MASK) { + /* this is an identity element */ + return toAdd; + } else if (toAdd instanceof Rfc4314Rights) { + Rfc4314Rights other = (Rfc4314Rights) toAdd; + if (other.value == EMPTY_MASK) { + /* toAdd is an identity element */ + return this; + } else { + return new Rfc4314Rights(this.value | other.value); + } + } else { + return new Rfc4314Rights(this.value | new Rfc4314Rights(toAdd.serialize()).value); + } + } + + } + + /** + * A utility implementation of + * {@code Map.Entry}. + */ + public static class SimpleMailboxACLEntry implements Map.Entry { + private final MailboxACLEntryKey key; + + private final MailboxACLRights value; + + public SimpleMailboxACLEntry(MailboxACLEntryKey key, MailboxACLRights value) { + super(); + this.key = key; + this.value = value; + } + + /* + * (non-Javadoc) + * + * @see java.util.Map.Entry#getKey() + */ + @Override + public MailboxACLEntryKey getKey() { + return key; + } + + /* + * (non-Javadoc) + * + * @see java.util.Map.Entry#getValue() + */ + @Override + public MailboxACLRights getValue() { + return value; + } + + /** + * Unsupported. + * + * @see java.util.Map.Entry#setValue(java.lang.Object) + */ + @Override + public MailboxACLRights setValue(MailboxACLRights value) { + throw new java.lang.UnsupportedOperationException("Fields of " + MailboxACLRights.class.getName() + " are read only."); + } + + } + + /** + * Default implementation of {@link MailboxACLEntryKey}. + */ + public static class SimpleMailboxACLEntryKey implements MailboxACLEntryKey { + public static SimpleMailboxACLEntryKey createGroup(String name) { + return new SimpleMailboxACLEntryKey(name, NameType.group, false); + } + + public static SimpleMailboxACLEntryKey createGroup(String name, boolean negative) { + return new SimpleMailboxACLEntryKey(name, NameType.group, negative); + } + + public static SimpleMailboxACLEntryKey createSpecial(SpecialName name) { + return new SimpleMailboxACLEntryKey(name.name(), NameType.special, false); + } + + public static SimpleMailboxACLEntryKey createSpecial(SpecialName name, boolean negative) { + return new SimpleMailboxACLEntryKey(name.name(), NameType.special, negative); + } + + public static SimpleMailboxACLEntryKey createUser(String name) { + return new SimpleMailboxACLEntryKey(name, NameType.user, false); + } + + public static SimpleMailboxACLEntryKey createUser(String name, boolean negative) { + return new SimpleMailboxACLEntryKey(name, NameType.user, negative); + } + + private final int hash; + 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 SimpleMailboxACLEntryKey(String serialized) { + + if (serialized == null) { + throw new IllegalStateException("Cannot parse null to a " + getClass().getName()); + } + if (serialized.length() == 0) { + throw new IllegalStateException("Cannot parse an empty string to a " + getClass().getName()); + } + int start = 0; + if (serialized.charAt(0) == DEFAULT_NEGATIVE_MARKER) { + negative = true; + start++; + } else { + negative = true; + } + if (serialized.charAt(0) == DEFAULT_GROUP_MARKER) { + nameType = NameType.group; + start++; + name = serialized.substring(start); + if (name.length() == 0) { + throw new IllegalStateException("Cannot parse a string with empty name to a " + getClass().getName()); + } + } else { + name = serialized.substring(start); + if (name.length() == 0) { + throw new IllegalStateException("Cannot parse a string with empty name to a " + getClass().getName()); + } + NameType nt = NameType.user; + for (SpecialName specialName : SpecialName.values()) { + if (specialName.name().equals(name)) { + nt = NameType.special; + break; + } + } + this.nameType = nt; + } + + this.hash = hash(); + + } + + public SimpleMailboxACLEntryKey(String name, NameType nameType, boolean negative) { + super(); + if (name == null) { + throw new NullPointerException("Provide a name for this " + getClass().getName()); + } + if (nameType == null) { + throw new NullPointerException("Provide a nameType for this " + getClass().getName()); + } + this.name = name; + this.nameType = nameType; + this.negative = negative; + this.hash = hash(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof MailboxACLEntryKey) { + MailboxACLEntryKey other = (MailboxACLEntryKey) o; + return this.name.equals(other.getName()) && this.nameType.equals(other.getNameType()) && this.negative == other.isNegative(); + } else { + return false; + } + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey#getName() + */ + public String getName() { + return name; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey#getNameType() + */ + public NameType getNameType() { + return nameType; + } + + private int hash() { + final int PRIME = 31; + int hash = negative ? 1 : 0; + hash = PRIME * hash + nameType.hashCode(); + hash = PRIME * hash + name.hashCode(); + return hash; + } + + @Override + public int hashCode() { + return hash; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey#isNegative() + */ + public boolean isNegative() { + return negative; + } + + /** + * Serializes this {@link SimpleMailboxACLEntryKey} using + * {@link MailboxACL#DEFAULT_NEGATIVE_MARKER} and + * {@link MailboxACL#DEFAULT_GROUP_MARKER}. + * + * @see org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey#serialize() + */ + @Override + public String serialize() { + if (!negative) { + switch (nameType) { + case special: + case user: + return name; + case group: + return new StringBuilder(name.length() + 1).append(DEFAULT_GROUP_MARKER).append(name).toString(); + default: + throw new IllegalStateException(); + } + } else { + StringBuilder result = new StringBuilder(name.length() + 2).append(DEFAULT_NEGATIVE_MARKER); + switch (nameType) { + case special: + case user: + break; + case group: + result.append(DEFAULT_GROUP_MARKER); + break; + default: + throw new IllegalStateException(); + } + return result.append(name).toString(); + } + } + + @Override + public String toString() { + return serialize(); + } + + } + + /** + * Default implementation of {@link MailboxACLRight}. + */ + public static final class SimpleMailboxACLRight implements MailboxACLRight { + private final char value; + + public SimpleMailboxACLRight(char value) { + super(); + this.value = value; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (o instanceof MailboxACLRight) { + return ((MailboxACLRight) o).getValue() == this.value; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.apache.james.mailbox.MailboxACL.MailboxACLRight#getValue() + */ + @Override + public char getValue() { + return value; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return (int) value; + } + + /** + * Returns String.valueOf(value). + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return String.valueOf(value); + } + + } + + public static final MailboxACLEntryKey ANYBODY_KEY; + public static final MailboxACLEntryKey AUTHENTICATED_KEY; + public static final MailboxACL EMPTY; + + public static final MailboxACLRights FULL_RIGHTS; + + public static final MailboxACLRights NO_RIGHTS; + public static final MailboxACL OWNER_FULL_ACL; + public static final MailboxACL OWNER_FULL_EXCEPT_ADMINISTRATION_ACL; + + public static final MailboxACLEntryKey OWNER_KEY; + + static { + try { + ANYBODY_KEY = new SimpleMailboxACLEntryKey(SpecialName.anybody.name(), NameType.special, false); + AUTHENTICATED_KEY = new SimpleMailboxACLEntryKey(SpecialName.authenticated.name(), NameType.special, false); + EMPTY = new SimpleMailboxACL(); + FULL_RIGHTS = new Rfc4314Rights(true, true, true, true, true, true, true, true, true, true, true); + NO_RIGHTS = new Rfc4314Rights(); + OWNER_KEY = new SimpleMailboxACLEntryKey(SpecialName.owner.name(), NameType.special, false); + OWNER_FULL_ACL = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACL.OWNER_KEY, SimpleMailboxACL.FULL_RIGHTS) }); + OWNER_FULL_EXCEPT_ADMINISTRATION_ACL = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACL.OWNER_KEY, SimpleMailboxACL.FULL_RIGHTS.except(new Rfc4314Rights(Rfc4314Rights.a_Administer_MASK))) }); + } catch (UnsupportedRightException e) { + throw new RuntimeException(e); + } + } + private final Map entries; + + /** + * Creates a new instance of SimpleMailboxACL containing no entries. + * + */ + public SimpleMailboxACL() { + this.entries = Collections.emptyMap(); + } + + /** + * Creates a new instance of SimpleMailboxACL from the given array of + * entries. + * + * @param entries + */ + public SimpleMailboxACL(Map.Entry[] entries) { + if (entries != null) { + Map m = new HashMap(entries.length + entries.length / 2 + 1); + for (Entry en : entries) { + m.put(en.getKey(), en.getValue()); + } + this.entries = Collections.unmodifiableMap(m); + } else { + this.entries = Collections.emptyMap(); + } + } + + /** + * Creates a new instance of SimpleMailboxACL from the given {@link Map} of + * entries. + * + * @param entries + */ + public SimpleMailboxACL(Map entries) { + if (entries != null && entries.size() > 0) { + Map m = new HashMap(entries.size() + entries.size() / 2 + 1); + for (Entry en : entries.entrySet()) { + m.put(en.getKey(), en.getValue()); + } + this.entries = Collections.unmodifiableMap(m); + } else { + this.entries = Collections.emptyMap(); + } + } + + /** + * Creates a new instance of SimpleMailboxACL. + * unmodifiableEntries parameter is supposed to be umodifiable + * already. + * + * @param unmodifiableEntries + * @param dummy + * just to be different from {@link #SimpleMailboxACL(Map)}. + */ + private SimpleMailboxACL(Map unmodifiableEntries, boolean dummy) { + this.entries = unmodifiableEntries; + } + + /** + * Creates a new instance of SimpleMailboxACL from {@link Properties}. The + * keys and values from the props parameter are parsed by the + * {@link String} constructors of {@link SimpleMailboxACLEntryKey} and + * {@link Rfc4314Rights} respectively. + * + * @param props + * @throws UnsupportedRightException + */ + public SimpleMailboxACL(Properties props) throws UnsupportedRightException { + super(); + + Map m = new HashMap(props.size() + props.size() / 2 + 1); + + if (props != null) { + for (Map.Entry prop : props.entrySet()) { + m.put(new SimpleMailboxACLEntryKey((String) prop.getKey()), new Rfc4314Rights((String) prop.getValue())); + } + } + + entries = Collections.unmodifiableMap(m); + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (o instanceof MailboxACL) { + MailboxACL acl = (MailboxACL) o; + Map ens = acl.getEntries(); + return entries == ens || (entries != null && entries.equals(ens)); + } + return false; + } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.MailboxACL#except(org.apache.james.mailbox.MailboxACL) + */ + @Override + public MailboxACL except(MailboxACL other) { + if (entries.size() == 0) { + return this; + } else { + Map otherEntries = other.getEntries(); + int cnt = entries.size(); + Map resultEntries = new HashMap(cnt + cnt / 2 + 1); + for (Entry otherEntry : otherEntries.entrySet()) { + MailboxACLEntryKey key = otherEntry.getKey(); + entries.remove(key); + } + return new SimpleMailboxACL(Collections.unmodifiableMap(resultEntries), true); + } + } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.MailboxACL#getEntries() + */ + @Override + public Map getEntries() { + return entries; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return entries == null ? 0 : entries.hashCode(); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return entries == null ? "" : entries.toString(); + } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.MailboxACL#union(org.apache.james.mailbox.MailboxACL) + */ + @Override + public MailboxACL union(MailboxACL other) throws UnsupportedRightException { + Map otherEntries = other.getEntries(); + if (otherEntries.size() == 0) { + return this; + } else if (entries.size() == 0) { + return other; + } else { + int cnt = otherEntries.size() + entries.size(); + Map resultEntries = new HashMap(cnt + cnt / 2 + 1); + for (Entry otherEntry : otherEntries.entrySet()) { + MailboxACLEntryKey key = otherEntry.getKey(); + MailboxACLRights thisRights = entries.get(key); + if (thisRights == null) { + /* nothing to union */ + resultEntries.put(key, otherEntry.getValue()); + } else { + /* union */ + resultEntries.put(key, otherEntry.getValue().union(thisRights)); + } + } + /* let us check what we have missed in the previous loop */ + for (Entry thisEntry : entries.entrySet()) { + MailboxACLEntryKey key = thisEntry.getKey(); + if (!resultEntries.containsKey(key)) { + resultEntries.put(key, thisEntry.getValue()); + } + } + return new SimpleMailboxACL(Collections.unmodifiableMap(resultEntries), true); + } + } + +} Index: mailbox/api/src/main/java/org/apache/james/mailbox/UnionMailboxACLResolver.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/UnionMailboxACLResolver.java (revision 0) +++ mailbox/api/src/main/java/org/apache/james/mailbox/UnionMailboxACLResolver.java (revision 0) @@ -0,0 +1,248 @@ +/* + * 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.mailbox; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey; +import org.apache.james.mailbox.MailboxACL.MailboxACLRight; +import org.apache.james.mailbox.MailboxACL.MailboxACLRights; +import org.apache.james.mailbox.MailboxACL.NameType; + +/** + * An implementation which works with the union of the rights granted to the + * applicable identifiers. Inspired by RFC 4314 Section 2. + * + * In + * {@link UnionMailboxACLResolver#listRights(String, org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver, MailboxACL, String, boolean)} + * all applicable negative and non-negative rights are union-ed separately and + * the result is computed afterwards with + * nonNegativeUnion.except(negativeUnion). + * + * Allows for setting dictinct global ACL for users' mailboxes on one hand and + * group (a.k.a shared) mailboxes on the other hand. E.g. the zero parameter + * constructor uses full rights for user mailboxes and + * full-except-administration rights for group mailboxes. + * + * @author Peter Palaga + */ +public class UnionMailboxACLResolver implements MailboxACLResolver { + public static final MailboxACL DEFAULT_GLOBAL_GROUP_ACL = SimpleMailboxACL.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; + + private final MailboxACL groupGlobalACL; + /** + * Stores global ACL which is merged with ACL of every mailbox when + * computing + * {@link #rightsOf(String, org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver, Mailbox)} + * and + * {@link #hasRight(String, Mailbox, MailboxACLRight, org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver)} + * . + */ + private final MailboxACL userGlobalACL; + + /** + * Creates a new instance of UnionMailboxACLResolver with + * {@link #DEFAULT_GLOBAL_USER_ACL} as {@link #userGlobalACL} and + * {@link #DEFAULT_GLOBAL_USER_ACL} as {@link #groupGlobalACL}. + */ + public UnionMailboxACLResolver() { + super(); + this.userGlobalACL = DEFAULT_GLOBAL_USER_ACL; + this.groupGlobalACL = DEFAULT_GLOBAL_GROUP_ACL; + } + + /** + * Creates a new instance of UnionMailboxACLResolver with the given + * globalACL. + * + * @param groupGlobalACL + * + * @param globalACL + * see {@link #userGlobalACL}, cannot be null. + * @throws NullPointerException + * when globalACL is null. + */ + public UnionMailboxACLResolver(MailboxACL userGlobalACL, MailboxACL groupGlobalACL) { + super(); + if (userGlobalACL == null) { + throw new NullPointerException("Missing userGlobalACL."); + } + if (groupGlobalACL == null) { + throw new NullPointerException("Missing groupGlobalACL."); + } + this.userGlobalACL = userGlobalACL; + this.groupGlobalACL = groupGlobalACL; + } + + /** + * Tells whether the given {@link MailboxACLEntryKey} is applicable for the + * given user. If the given key is a group key, the given + * {@link GroupMembershipResolver} is used to find out if the given user is + * a member of the key's group. If the given key is an "owner" key, it is + * effectively handled as if it was a {@code resourceOwner} key. To avoid + * clash between user and group names, {@code resourceOwnerIsGroup} must + * state explicitly if the given {@code resourceOwner} is a group. + * + * @param key + * @param user + * @param groupMembershipResolver + * @param resourceOwner + * @param resourceOwnerIsGroup + * @return + */ + protected boolean applies(MailboxACLEntryKey key, String user, GroupMembershipResolver groupMembershipResolver, String resourceOwner, boolean resourceOwnerIsGroup) { + final String keyName = key.getName(); + final NameType keyNameType = key.getNameType(); + if (MailboxACL.SpecialName.anybody.name().equals(keyName)) { + /* this works also for unauthenticated users */ + return true; + } else if (user != null) { + /* Authenticated users */ + if (MailboxACL.SpecialName.authenticated.name().equals(keyName)) { + return true; + } else if (MailboxACL.SpecialName.owner.name().equals(keyName)) { + return (!resourceOwnerIsGroup && user.equals(resourceOwner)) || (resourceOwnerIsGroup && groupMembershipResolver.isMember(user, resourceOwner)); + } else if (MailboxACL.NameType.user.equals(keyNameType)) { + return keyName.equals(user); + } else if (MailboxACL.NameType.group.equals(keyNameType)) { + return groupMembershipResolver.isMember(user, keyName); + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.mailbox.MailboxACLResolver#applyGlobalACL(org.apache + * .james.mailbox.MailboxACL, boolean) + */ + @Override + public MailboxACL applyGlobalACL(MailboxACL resourceACL, boolean resourceOwnerIsGroup) throws UnsupportedRightException { + return resourceOwnerIsGroup ? resourceACL.union(groupGlobalACL) : resourceACL.union(userGlobalACL); + } + + /* + * (non-Javadoc) + * + * @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, java.lang.String) + */ + @Override + public boolean hasRight(String requestUser, GroupMembershipResolver groupMembershipResolver, MailboxACLRight right, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException { + boolean result = false; + Map entries = resourceOwnerIsGroup ? groupGlobalACL.getEntries() : userGlobalACL.getEntries(); + if (entries != null) { + for (Iterator> it = entries.entrySet().iterator(); it.hasNext();) { + final Entry entry = it.next(); + final MailboxACLEntryKey key = entry.getKey(); + if (applies(key, requestUser, groupMembershipResolver, resourceOwner, resourceOwnerIsGroup) && entry.getValue().contains(right)) { + if (key.isNegative()) { + return false; + } else { + result = true; + } + } + } + } + + if (resourceACL != null) { + entries = resourceACL.getEntries(); + if (entries != null) { + for (Iterator> it = entries.entrySet().iterator(); it.hasNext();) { + final Entry entry = it.next(); + final MailboxACLEntryKey key = entry.getKey(); + if (applies(key, requestUser, groupMembershipResolver, resourceOwner, resourceOwnerIsGroup) && entry.getValue().contains(right)) { + if (key.isNegative()) { + return false; + } else { + result = true; + } + } + } + } + } + + return result; + } + + /* + * (non-Javadoc) + * + * @see + * org.apache.james.mailbox.store.mail.MailboxACLResolver#rightsOf(java. + * lang.String, org.apache.james.mailbox.store.mail.MailboxACLResolver. + * GroupMembershipResolver, org.apache.james.mailbox.MailboxACL, + * java.lang.String) + */ + @Override + public MailboxACL.MailboxACLRights listRights(String user, GroupMembershipResolver groupMembershipResolver, MailboxACL resourceACL, String resourceOwner, boolean resourceOwnerIsGroup) throws UnsupportedRightException { + MailboxACL.MailboxACLRights[] positiveNegativePair = { SimpleMailboxACL.NO_RIGHTS, SimpleMailboxACL.NO_RIGHTS }; + + listRights(user, groupMembershipResolver, userGlobalACL.getEntries(), resourceOwner, resourceOwnerIsGroup, positiveNegativePair); + + if (resourceACL != null) { + listRights(user, groupMembershipResolver, resourceACL.getEntries(), resourceOwner, resourceOwnerIsGroup, positiveNegativePair); + } + + return positiveNegativePair[0].except(positiveNegativePair[1]); + } + + /** + * What needs to be done for both global ACL and the given mailboxe's ACL. + * + * @param requestUser + * @param groupMembershipResolver + * @param entries + * @param resourceOwner + * @param resourceOwnerIsGroup + * @param positiveNegativePair + * @throws UnsupportedRightException + */ + private void listRights(String requestUser, GroupMembershipResolver groupMembershipResolver, final Map entries, String resourceOwner, boolean resourceOwnerIsGroup, MailboxACL.MailboxACLRights[] positiveNegativePair) throws UnsupportedRightException { + if (entries != null) { + for (Iterator> it = entries.entrySet().iterator(); it.hasNext();) { + final Entry entry = it.next(); + final MailboxACLEntryKey key = entry.getKey(); + if (applies(key, requestUser, groupMembershipResolver, resourceOwner, resourceOwnerIsGroup)) { + if (key.isNegative()) { + positiveNegativePair[1] = positiveNegativePair[1].union(entry.getValue()); + } else { + positiveNegativePair[0] = positiveNegativePair[0].union(entry.getValue()); + } + } + } + } + } + +} Index: mailbox/api/src/main/java/org/apache/james/mailbox/UnsupportedRightException.java =================================================================== --- mailbox/api/src/main/java/org/apache/james/mailbox/UnsupportedRightException.java (revision 0) +++ mailbox/api/src/main/java/org/apache/james/mailbox/UnsupportedRightException.java (revision 0) @@ -0,0 +1,46 @@ +/* + * 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.mailbox; + +import org.apache.james.mailbox.MailboxACL.MailboxACLRight; + +/** + * Thrown when the current system does not support the given right. + * + * @author Peter Palaga + */ +public class UnsupportedRightException extends MailboxSecurityException { + + private static final long serialVersionUID = 2959248897018370078L; + + public UnsupportedRightException() { + super(); + } + + public UnsupportedRightException(MailboxACLRight unsupportedRight) { + this(unsupportedRight.getValue()); + } + + public UnsupportedRightException(char rigthFlag) { + super("Unsupported right flag '"+ rigthFlag +"'."); + } + +} Index: mailbox/api/src/test/java/org/apache/james/mailbox/Rfc4314RightsTest.java =================================================================== --- mailbox/api/src/test/java/org/apache/james/mailbox/Rfc4314RightsTest.java (revision 0) +++ mailbox/api/src/test/java/org/apache/james/mailbox/Rfc4314RightsTest.java (revision 0) @@ -0,0 +1,104 @@ +/* + * 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.mailbox; + +import static org.junit.Assert.assertEquals; + +import org.apache.james.mailbox.MailboxACL.MailboxACLRights; +import org.apache.james.mailbox.SimpleMailboxACL.Rfc4314Rights; +import org.apache.james.mailbox.SimpleMailboxACL; +import org.apache.james.mailbox.UnsupportedRightException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * @author Peter Palaga + */ +public class Rfc4314RightsTest { + + private Rfc4314Rights aeik; + private Rfc4314Rights lprs; + private Rfc4314Rights twx; + private MailboxACLRights full; + private MailboxACLRights none; + + @Before + public void setUp() throws Exception { + aeik = new SimpleMailboxACL.Rfc4314Rights("aeik"); + lprs = new SimpleMailboxACL.Rfc4314Rights("lprs"); + twx = new SimpleMailboxACL.Rfc4314Rights("twx"); + full = SimpleMailboxACL.FULL_RIGHTS; + none = SimpleMailboxACL.NO_RIGHTS; + } + @Test + public void test() throws UnsupportedRightException { + assertEquals(aeik.getValue(), Rfc4314Rights.a_Administer_MASK | Rfc4314Rights.e_PerformExpunge_MASK | Rfc4314Rights.i_Insert_MASK | Rfc4314Rights.k_CreateMailbox_MASK); + assertEquals(lprs.getValue(), Rfc4314Rights.l_Lookup_MASK | Rfc4314Rights.p_Post_MASK | Rfc4314Rights.s_WriteSeenFlag_MASK | Rfc4314Rights.r_Read_MASK); + assertEquals(twx.getValue(), Rfc4314Rights.t_DeleteMessages_MASK | Rfc4314Rights.w_Write_MASK | Rfc4314Rights.x_DeleteMailbox_MASK); + } + + @Test + public void testSerialize() throws UnsupportedRightException { + assertEquals("aeik", aeik.serialize()); + assertEquals("lprs", lprs.serialize()); + assertEquals("twx", twx.serialize()); + assertEquals("aeiklprstwx", full.serialize()); + assertEquals("", none.serialize()); + } + + @Test + public void testUnionFull() throws UnsupportedRightException { + assertEquals(full, aeik.union(lprs).union(twx)); + } + + @Test + public void testUnionZero() throws UnsupportedRightException { + assertEquals(lprs, lprs.union(none)); + } + + @Test + public void testExceptZero() throws UnsupportedRightException { + assertEquals(aeik, aeik.except(none)); + } + + @Test + public void testExceptNonExistent() throws UnsupportedRightException { + assertEquals(aeik, aeik.except(lprs)); + } + + @Test + public void testExceptFull() throws UnsupportedRightException { + assertEquals(none, full.except(aeik).except(lprs).except(twx)); + } + + @Test + public void testExceptUnsupportedFlag() { + try { + String unsupportedFlag = "z"; + new SimpleMailboxACL.Rfc4314Rights(unsupportedFlag ); + Assert.fail(UnsupportedRightException.class.getName() +" expected for unsupported right flag '"+ unsupportedFlag +"'."); + } catch (UnsupportedRightException e) { + /* OK */ + } + } + +} Index: mailbox/api/src/test/java/org/apache/james/mailbox/UnionMailboxACLResolverTest.java =================================================================== --- mailbox/api/src/test/java/org/apache/james/mailbox/UnionMailboxACLResolverTest.java (revision 0) +++ mailbox/api/src/test/java/org/apache/james/mailbox/UnionMailboxACLResolverTest.java (revision 0) @@ -0,0 +1,575 @@ +/* + * 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.mailbox; + +import org.apache.james.mailbox.MailboxACL.NameType; +import org.apache.james.mailbox.MailboxACL.SpecialName; +import org.apache.james.mailbox.SimpleMailboxACL.Rfc4314Rights; +import org.apache.james.mailbox.SimpleMailboxACL.SimpleMailboxACLEntryKey; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * @author Peter Palaga + */ +public class UnionMailboxACLResolverTest { + + private static final String GROUP_1 = "group1"; + private static final String GROUP_2 = "group2"; + + private static final String USER_1 = "user1"; + private static final String USER_2 = "user2"; + + private MailboxACL anybodyRead; + private MailboxACL anybodyReadNegative; + private UnionMailboxACLResolver anyoneReadListGlobal; + private MailboxACL authenticatedRead; + private UnionMailboxACLResolver authenticatedReadListWriteGlobal; + private MailboxACL authenticatedReadNegative; + private MailboxACL group1Read; + private MailboxACL group1ReadNegative; + private SimpleGroupMembershipResolver groupMembershipResolver; + private UnionMailboxACLResolver negativeGroup2FullGlobal; + private UnionMailboxACLResolver noGlobals; + private UnionMailboxACLResolver ownerFullGlobal; + private MailboxACL ownerRead; + private MailboxACL ownerReadNegative; + private MailboxACL user1Read; + private MailboxACL user1ReadNegative; + + @Before + public void setUp() throws Exception { + MailboxACL acl = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACL.AUTHENTICATED_KEY, SimpleMailboxACL.FULL_RIGHTS) }); + authenticatedReadListWriteGlobal = new UnionMailboxACLResolver(acl, acl); + acl = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACL.ANYBODY_KEY, new Rfc4314Rights("rl")) }); + anyoneReadListGlobal = new UnionMailboxACLResolver(acl, acl); + acl = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACL.OWNER_KEY, SimpleMailboxACL.FULL_RIGHTS) }); + ownerFullGlobal = new UnionMailboxACLResolver(acl, acl); + noGlobals = new UnionMailboxACLResolver(SimpleMailboxACL.EMPTY, SimpleMailboxACL.EMPTY); + acl = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(new SimpleMailboxACLEntryKey(GROUP_2, NameType.group, true), SimpleMailboxACL.FULL_RIGHTS) }); + negativeGroup2FullGlobal = new UnionMailboxACLResolver(acl, new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(new SimpleMailboxACLEntryKey(GROUP_2, NameType.group, true), SimpleMailboxACL.FULL_RIGHTS) })); + + groupMembershipResolver = new SimpleGroupMembershipResolver(); + groupMembershipResolver.addMembership(GROUP_1, USER_1); + groupMembershipResolver.addMembership(GROUP_2, USER_2); + + user1Read = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createUser(USER_1), new Rfc4314Rights("r")) }); + user1ReadNegative = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createUser(USER_1, true), new Rfc4314Rights("r")) }); + + group1Read = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createGroup(GROUP_1), new Rfc4314Rights("r")) }); + group1ReadNegative = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createGroup(GROUP_1, true), new Rfc4314Rights("r")) }); + + anybodyRead = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createSpecial(SpecialName.anybody), new Rfc4314Rights("r")) }); + anybodyReadNegative = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createSpecial(SpecialName.anybody, true), new Rfc4314Rights("r")) }); + + authenticatedRead = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createSpecial(SpecialName.authenticated), new Rfc4314Rights("r")) }); + authenticatedReadNegative = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createSpecial(SpecialName.authenticated, true), new Rfc4314Rights("r")) }); + + ownerRead = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createSpecial(SpecialName.owner), new Rfc4314Rights("r")) }); + ownerReadNegative = new SimpleMailboxACL(new SimpleMailboxACL.SimpleMailboxACLEntry[] { new SimpleMailboxACL.SimpleMailboxACLEntry(SimpleMailboxACLEntryKey.createSpecial(SpecialName.owner, true), new Rfc4314Rights("r")) }); + + } + + @Test + public void testAppliesNullUser() throws UnsupportedRightException { + + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_1), null, groupMembershipResolver, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_2), null, groupMembershipResolver, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_1), null, groupMembershipResolver, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_2), null, groupMembershipResolver, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.anybody), null, groupMembershipResolver, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.authenticated), null, groupMembershipResolver, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.owner), null, groupMembershipResolver, USER_1, false)); + } + + @Test + public void testAppliesUser() throws UnsupportedRightException { + /* requester unequal to owner */ + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_1), USER_1, groupMembershipResolver, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_2), USER_1, groupMembershipResolver, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_1), USER_1, groupMembershipResolver, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_2), USER_1, groupMembershipResolver, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.anybody), USER_1, groupMembershipResolver, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.authenticated), USER_1, groupMembershipResolver, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.owner), USER_1, groupMembershipResolver, USER_1, false)); + + /* requester unequal to owner user */ + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_1), USER_1, groupMembershipResolver, USER_2, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_2), USER_1, groupMembershipResolver, USER_2, false)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_1), USER_1, groupMembershipResolver, USER_2, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_2), USER_1, groupMembershipResolver, USER_2, false)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.anybody), USER_1, groupMembershipResolver, USER_2, false)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.authenticated), USER_1, groupMembershipResolver, USER_2, false)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.owner), USER_1, groupMembershipResolver, USER_2, false)); + + /* requester member of owner group */ + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_1), USER_1, groupMembershipResolver, GROUP_1, true)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_2), USER_1, groupMembershipResolver, GROUP_1, true)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_1), USER_1, groupMembershipResolver, GROUP_1, true)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_2), USER_1, groupMembershipResolver, GROUP_1, true)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.anybody), USER_1, groupMembershipResolver, GROUP_1, true)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.authenticated), USER_1, groupMembershipResolver, GROUP_1, true)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.owner), USER_1, groupMembershipResolver, GROUP_1, true)); + + /* requester not member of owner group */ + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_1), USER_1, groupMembershipResolver, GROUP_2, true)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createUser(USER_2), USER_1, groupMembershipResolver, GROUP_2, true)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_1), USER_1, groupMembershipResolver, GROUP_2, true)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createGroup(GROUP_2), USER_1, groupMembershipResolver, GROUP_2, true)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.anybody), USER_1, groupMembershipResolver, GROUP_2, true)); + Assert.assertTrue(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.authenticated), USER_1, groupMembershipResolver, GROUP_2, true)); + Assert.assertFalse(anyoneReadListGlobal.applies(SimpleMailboxACLEntryKey.createSpecial(SpecialName.owner), USER_1, groupMembershipResolver, GROUP_2, true)); + + } + + @Test + public void testHasRightNullUser() throws UnsupportedRightException { + + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + Assert.assertTrue(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + } + + @Test + public void testHasRightNullUserGlobals() throws UnsupportedRightException { + Assert.assertTrue(anyoneReadListGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_2, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, SimpleMailboxACL.EMPTY, USER_2, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, SimpleMailboxACL.EMPTY, USER_2, false)); + Assert.assertFalse(noGlobals.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, SimpleMailboxACL.EMPTY, USER_2, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(null, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, SimpleMailboxACL.EMPTY, USER_2, false)); + } + + + @Test + public void testHasRightUserSelfOwner() throws UnsupportedRightException { + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_1, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_1, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_1, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_1, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_1, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_1, false)); + + } + + + @Test + public void testHasRightUserNotOwner() throws UnsupportedRightException { + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_2, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_2, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_2, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_2, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_2, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_2, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_2, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_2, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, USER_2, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, USER_2, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_2, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_2, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_2, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_2, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_2, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_2, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_2, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_2, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, USER_2, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, USER_2, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_2, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_2, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_2, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_2, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_2, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_2, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_2, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_2, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, USER_2, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, USER_2, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_2, false)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_2, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_2, false)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_2, false)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_2, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_2, false)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_2, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_2, false)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, USER_2, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, USER_2, false)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_2, false)); + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_2, false)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_2, false)); + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_2, false)); + + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_2, false)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_2, false)); + + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_2, false)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_2, false)); + + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, USER_2, false)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, USER_2, false)); + + } + @Test + public void testHasRightUserMemberOfOwnerGroup() throws UnsupportedRightException { + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_1, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_1, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_1, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_1, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_1, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_1, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_1, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_1, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_1, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_1, true)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_1, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_1, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_1, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_1, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_1, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_1, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_1, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_1, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_1, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_1, true)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_1, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_1, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_1, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_1, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_1, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_1, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_1, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_1, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_1, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_1, true)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_1, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_1, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_1, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_1, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_1, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_1, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_1, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_1, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_1, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_1, true)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_1, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_1, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_1, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_1, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_1, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_1, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_1, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_1, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_1, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_1, true)); + + } + + + @Test + public void testHasRightUserNotMemberOfOwnerGroup() throws UnsupportedRightException { + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_2, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_2, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_2, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_2, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_2, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_2, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_2, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_2, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1Read, GROUP_2, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, user1ReadNegative, GROUP_2, true)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_2, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_2, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_2, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_2, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_2, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_2, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_2, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_2, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1Read, GROUP_2, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, group1ReadNegative, GROUP_2, true)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_2, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_2, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_2, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_2, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_2, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_2, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_2, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_2, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyRead, GROUP_2, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, anybodyReadNegative, GROUP_2, true)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_2, true)); + Assert.assertFalse(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_2, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_2, true)); + Assert.assertFalse(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_2, true)); + + Assert.assertTrue(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_2, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_2, true)); + + Assert.assertTrue(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_2, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_2, true)); + + Assert.assertTrue(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedRead, GROUP_2, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, authenticatedReadNegative, GROUP_2, true)); + + + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_2, true)); + Assert.assertTrue(anyoneReadListGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_2, true)); + + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_2, true)); + Assert.assertTrue(authenticatedReadListWriteGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_2, true)); + + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_2, true)); + Assert.assertFalse(ownerFullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_2, true)); + + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_2, true)); + Assert.assertFalse(noGlobals.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_2, true)); + + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerRead, GROUP_2, true)); + Assert.assertFalse(negativeGroup2FullGlobal.hasRight(USER_1, groupMembershipResolver, Rfc4314Rights.r_Read_RIGHT, ownerReadNegative, GROUP_2, true)); + + } + +} Index: mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMailboxManager.java =================================================================== --- mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMailboxManager.java (revision 1232377) +++ mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMailboxManager.java (working copy) @@ -22,6 +22,8 @@ import org.apache.james.mailbox.hbase.mail.model.HBaseMailbox; import org.apache.james.mailbox.MailboxPathLocker; import org.apache.james.mailbox.hbase.mail.HBaseMailboxMapper; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPath; import org.apache.james.mailbox.MailboxSession; @@ -38,12 +40,12 @@ */ public class HBaseMailboxManager extends StoreMailboxManager { - public HBaseMailboxManager(HBaseMailboxSessionMapperFactory mapperFactory, Authenticator authenticator, MailboxPathLocker locker) { - super(mapperFactory, authenticator, locker); + public HBaseMailboxManager(HBaseMailboxSessionMapperFactory mapperFactory, Authenticator authenticator, MailboxPathLocker locker, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) { + super(mapperFactory, authenticator, locker, aclResolver, groupMembershipResolver); } - public HBaseMailboxManager(HBaseMailboxSessionMapperFactory mapperFactory, Authenticator authenticator) { - super(mapperFactory, authenticator, new JVMMailboxPathLocker()); + public HBaseMailboxManager(HBaseMailboxSessionMapperFactory mapperFactory, Authenticator authenticator, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) { + super(mapperFactory, authenticator, new JVMMailboxPathLocker(), aclResolver, groupMembershipResolver); } @Override @@ -79,7 +81,7 @@ @Override protected StoreMessageManager createMessageManager(Mailbox mailboxRow, MailboxSession session) throws MailboxException { - StoreMessageManager result = new HBaseMessageManager(getMapperFactory(), getMessageSearchIndex(), getEventDispatcher(), getLocker(), mailboxRow); + StoreMessageManager result = new HBaseMessageManager(getMapperFactory(), getMessageSearchIndex(), getEventDispatcher(), getLocker(), mailboxRow, getAclResolver(), getGroupMembershipResolver()); return result; } } Index: mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMessageManager.java =================================================================== --- mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMessageManager.java (revision 1232377) +++ mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMessageManager.java (working copy) @@ -20,6 +20,9 @@ import java.util.UUID; import javax.mail.Flags; + +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPathLocker; import org.apache.james.mailbox.MailboxSession; @@ -36,8 +39,8 @@ public class HBaseMessageManager extends StoreMessageManager { public HBaseMessageManager(MailboxSessionMapperFactory mapperFactory, MessageSearchIndex index, - MailboxEventDispatcher dispatcher, MailboxPathLocker locker, Mailbox mailbox) throws MailboxException { - super(mapperFactory, index, dispatcher, locker, mailbox); + MailboxEventDispatcher dispatcher, MailboxPathLocker locker, Mailbox mailbox, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) throws MailboxException { + super(mapperFactory, index, dispatcher, locker, mailbox, aclResolver, groupMembershipResolver); } Index: mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/model/HBaseMailbox.java =================================================================== --- mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/model/HBaseMailbox.java (revision 1232377) +++ mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/model/HBaseMailbox.java (working copy) @@ -19,7 +19,10 @@ package org.apache.james.mailbox.hbase.mail.model; import java.util.UUID; + +import org.apache.james.mailbox.MailboxACL; import org.apache.james.mailbox.MailboxPath; +import org.apache.james.mailbox.SimpleMailboxACL; import org.apache.james.mailbox.store.mail.model.Mailbox; /** @@ -193,4 +196,22 @@ public void setMessageCount(long messageCount) { this.messageCount = messageCount; } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#getACL() + */ + @Override + public MailboxACL getACL() { + // TODO ACL support + return SimpleMailboxACL.OWNER_FULL_ACL; + } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#setACL(org.apache.james.mailbox.MailboxACL) + */ + @Override + public void setACL(MailboxACL acl) { + // TODO ACL support + } + } Index: mailbox/hbase/src/test/java/org/apache/james/mailbox/hbase/HBaseMailboxManagerTest.java =================================================================== --- mailbox/hbase/src/test/java/org/apache/james/mailbox/hbase/HBaseMailboxManagerTest.java (revision 1232377) +++ mailbox/hbase/src/test/java/org/apache/james/mailbox/hbase/HBaseMailboxManagerTest.java (working copy) @@ -22,8 +22,12 @@ import org.apache.hadoop.conf.Configuration; import org.apache.james.mailbox.AbstractMailboxManagerTest; import org.apache.james.mailbox.BadCredentialsException; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; import org.apache.james.mailbox.hbase.mail.HBaseModSeqProvider; import org.apache.james.mailbox.hbase.mail.HBaseUidProvider; import org.junit.After; @@ -72,7 +76,10 @@ HBaseModSeqProvider modSeqProvider = new HBaseModSeqProvider(conf); HBaseMailboxSessionMapperFactory mf = new HBaseMailboxSessionMapperFactory(conf, uidProvider, modSeqProvider); - HBaseMailboxManager mailboxManagerLocal = new HBaseMailboxManager(mf, null); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + HBaseMailboxManager mailboxManagerLocal = new HBaseMailboxManager(mf, null, aclResolver, groupMembershipResolver); mailboxManagerLocal.init(); setMailboxManager(mailboxManagerLocal); Index: mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMailboxManager.java =================================================================== --- mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMailboxManager.java (revision 1232377) +++ mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMailboxManager.java (working copy) @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.james.mailbox.jcr; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPath; import org.apache.james.mailbox.MailboxPathLocker; @@ -39,18 +41,18 @@ private final Logger logger = LoggerFactory.getLogger(JCRMailboxManager.class); - public JCRMailboxManager(JCRMailboxSessionMapperFactory mapperFactory, final Authenticator authenticator) { - this(mapperFactory, authenticator, new JVMMailboxPathLocker()); + public JCRMailboxManager(JCRMailboxSessionMapperFactory mapperFactory, final Authenticator authenticator, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) { + this(mapperFactory, authenticator, new JVMMailboxPathLocker(), aclResolver, groupMembershipResolver); } - public JCRMailboxManager(JCRMailboxSessionMapperFactory mapperFactory, final Authenticator authenticator, final MailboxPathLocker locker) { - super(mapperFactory, authenticator, locker); + public JCRMailboxManager(JCRMailboxSessionMapperFactory mapperFactory, final Authenticator authenticator, final MailboxPathLocker locker, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) { + super(mapperFactory, authenticator, locker, aclResolver, groupMembershipResolver); } @Override protected StoreMessageManager createMessageManager(Mailbox mailboxEntity, MailboxSession session) throws MailboxException{ - return new JCRMessageManager(getMapperFactory(), getMessageSearchIndex(), getEventDispatcher(), getLocker(), (JCRMailbox) mailboxEntity, logger, getDelimiter()); + return new JCRMessageManager(getMapperFactory(), getMessageSearchIndex(), getEventDispatcher(), getLocker(), (JCRMailbox) mailboxEntity, getAclResolver(), getGroupMembershipResolver(), logger, getDelimiter()); } @Override Index: mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMessageManager.java =================================================================== --- mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMessageManager.java (revision 1232377) +++ mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMessageManager.java (working copy) @@ -23,6 +23,8 @@ import javax.mail.Flags; import javax.mail.internet.SharedInputStream; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPathLocker; import org.apache.james.mailbox.MailboxSession; @@ -45,8 +47,8 @@ private final Logger log; public JCRMessageManager(MailboxSessionMapperFactory mapperFactory, MessageSearchIndex index, - final MailboxEventDispatcher dispatcher, final MailboxPathLocker locker, final JCRMailbox mailbox, final Logger log, final char delimiter) throws MailboxException { - super(mapperFactory, index, dispatcher, locker, mailbox); + final MailboxEventDispatcher dispatcher, final MailboxPathLocker locker, final JCRMailbox mailbox, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver, final Logger log, final char delimiter) throws MailboxException { + super(mapperFactory, index, dispatcher, locker, mailbox, aclResolver, groupMembershipResolver); this.log = log; } Index: mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/mail/model/JCRMailbox.java =================================================================== --- mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/mail/model/JCRMailbox.java (revision 1232377) +++ mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/mail/model/JCRMailbox.java (working copy) @@ -23,7 +23,9 @@ import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.util.Text; +import org.apache.james.mailbox.MailboxACL; import org.apache.james.mailbox.MailboxPath; +import org.apache.james.mailbox.SimpleMailboxACL; import org.apache.james.mailbox.jcr.JCRImapConstants; import org.apache.james.mailbox.jcr.Persistent; import org.apache.james.mailbox.store.mail.model.Mailbox; @@ -303,4 +305,22 @@ } return highestKnownModSeq; } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#getACL() + */ + @Override + public MailboxACL getACL() { + // TODO ACL support + return SimpleMailboxACL.OWNER_FULL_ACL; + } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#setACL(org.apache.james.mailbox.MailboxACL) + */ + @Override + public void setACL(MailboxACL acl) { + // TODO ACL support + } + } Index: mailbox/jcr/src/test/java/org/apache/james/mailbox/jcr/JCRMailboxManagerTest.java =================================================================== --- mailbox/jcr/src/test/java/org/apache/james/mailbox/jcr/JCRMailboxManagerTest.java (revision 1232377) +++ mailbox/jcr/src/test/java/org/apache/james/mailbox/jcr/JCRMailboxManagerTest.java (working copy) @@ -28,9 +28,13 @@ import org.apache.jackrabbit.core.config.ConfigurationException; import org.apache.jackrabbit.core.config.RepositoryConfig; import org.apache.james.mailbox.BadCredentialsException; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.AbstractMailboxManagerTest; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.jcr.mail.JCRModSeqProvider; import org.apache.james.mailbox.jcr.mail.JCRUidProvider; import org.apache.james.mailbox.store.JVMMailboxPathLocker; @@ -101,7 +105,11 @@ JCRUidProvider uidProvider = new JCRUidProvider(locker, sessionRepos); JCRModSeqProvider modSeqProvider= new JCRModSeqProvider(locker, sessionRepos); JCRMailboxSessionMapperFactory mf = new JCRMailboxSessionMapperFactory(sessionRepos, uidProvider, modSeqProvider); - JCRMailboxManager manager = new JCRMailboxManager(mf, null, locker); + + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + JCRMailboxManager manager = new JCRMailboxManager(mf, null, locker, aclResolver, groupMembershipResolver); manager.init(); setMailboxManager(manager); } Index: mailbox/jcr/src/test/java/org/apache/james/mailbox/jcr/JCRStressTest.java =================================================================== --- mailbox/jcr/src/test/java/org/apache/james/mailbox/jcr/JCRStressTest.java (revision 1232377) +++ mailbox/jcr/src/test/java/org/apache/james/mailbox/jcr/JCRStressTest.java (working copy) @@ -25,9 +25,13 @@ import org.apache.jackrabbit.core.RepositoryImpl; import org.apache.jackrabbit.core.config.RepositoryConfig; import org.apache.james.mailbox.AbstractStressTest; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.jcr.mail.JCRModSeqProvider; import org.apache.james.mailbox.jcr.mail.JCRUidProvider; import org.apache.james.mailbox.store.JVMMailboxPathLocker; @@ -60,7 +64,10 @@ JCRUidProvider uidProvider = new JCRUidProvider(locker, sessionRepos); JCRModSeqProvider modSeqProvider= new JCRModSeqProvider(locker, sessionRepos); JCRMailboxSessionMapperFactory mf = new JCRMailboxSessionMapperFactory(sessionRepos, uidProvider, modSeqProvider); - mailboxManager = new JCRMailboxManager(mf, null, locker); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + mailboxManager = new JCRMailboxManager(mf, null, locker, aclResolver, groupMembershipResolver); mailboxManager.init(); } Index: mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxManager.java =================================================================== --- mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxManager.java (revision 1232377) +++ mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxManager.java (working copy) @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.james.mailbox.jpa; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPath; import org.apache.james.mailbox.MailboxPathLocker; @@ -35,8 +37,8 @@ public abstract class JPAMailboxManager extends StoreMailboxManager { public JPAMailboxManager(JPAMailboxSessionMapperFactory mailboxSessionMapperFactory, - final Authenticator authenticator, final MailboxPathLocker locker) { - super(mailboxSessionMapperFactory, authenticator, locker); + final Authenticator authenticator, final MailboxPathLocker locker, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) { + super(mailboxSessionMapperFactory, authenticator, locker, aclResolver, groupMembershipResolver); } @Override Index: mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMessageManager.java =================================================================== --- mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMessageManager.java (revision 1232377) +++ mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMessageManager.java (working copy) @@ -23,6 +23,8 @@ import javax.mail.Flags; import javax.mail.internet.SharedInputStream; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPathLocker; import org.apache.james.mailbox.MailboxSession; @@ -41,8 +43,8 @@ */ public class JPAMessageManager extends StoreMessageManager { - public JPAMessageManager(MailboxSessionMapperFactory mapperFactory, final MessageSearchIndex index, final MailboxEventDispatcher dispatcher, final MailboxPathLocker locker, final Mailbox mailbox) throws MailboxException { - super(mapperFactory, index, dispatcher, locker, mailbox); + public JPAMessageManager(MailboxSessionMapperFactory mapperFactory, final MessageSearchIndex index, final MailboxEventDispatcher dispatcher, final MailboxPathLocker locker, final Mailbox mailbox, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) throws MailboxException { + super(mapperFactory, index, dispatcher, locker, mailbox, aclResolver, groupMembershipResolver); } @Override Index: mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java =================================================================== --- mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java (revision 1232377) +++ mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java (working copy) @@ -27,7 +27,9 @@ import javax.persistence.NamedQuery; import javax.persistence.Table; +import org.apache.james.mailbox.MailboxACL; import org.apache.james.mailbox.MailboxPath; +import org.apache.james.mailbox.SimpleMailboxACL; import org.apache.james.mailbox.store.mail.model.Mailbox; @Entity(name="Mailbox") @@ -213,4 +215,21 @@ return ++highestModSeq; } + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#getACL() + */ + @Override + public MailboxACL getACL() { + // TODO ACL support + return SimpleMailboxACL.OWNER_FULL_ACL; + } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#setACL(org.apache.james.mailbox.MailboxACL) + */ + @Override + public void setACL(MailboxACL acl) { + // TODO ACL support + } + } Index: mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/openjpa/OpenJPAMailboxManager.java =================================================================== --- mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/openjpa/OpenJPAMailboxManager.java (revision 1232377) +++ mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/openjpa/OpenJPAMailboxManager.java (working copy) @@ -20,6 +20,8 @@ package org.apache.james.mailbox.jpa.openjpa; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPathLocker; import org.apache.james.mailbox.MailboxSession; @@ -40,8 +42,8 @@ private AdvancedFeature feature; - public OpenJPAMailboxManager(JPAMailboxSessionMapperFactory mapperFactory, Authenticator authenticator, MailboxPathLocker locker, boolean useStreaming) { - super(mapperFactory, authenticator, locker); + public OpenJPAMailboxManager(JPAMailboxSessionMapperFactory mapperFactory, Authenticator authenticator, MailboxPathLocker locker, boolean useStreaming, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) { + super(mapperFactory, authenticator, locker, aclResolver, groupMembershipResolver); if (useStreaming) { feature = AdvancedFeature.Streaming; } else { @@ -49,8 +51,8 @@ } } - public OpenJPAMailboxManager(JPAMailboxSessionMapperFactory mapperFactory, Authenticator authenticator, MailboxPathLocker locker, String encryptPass) { - super(mapperFactory, authenticator, locker); + public OpenJPAMailboxManager(JPAMailboxSessionMapperFactory mapperFactory, Authenticator authenticator, MailboxPathLocker locker, String encryptPass, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) { + super(mapperFactory, authenticator, locker, aclResolver, groupMembershipResolver); if (encryptPass != null) { EncryptDecryptHelper.init(encryptPass); feature = AdvancedFeature.Encryption; @@ -59,13 +61,13 @@ } } - public OpenJPAMailboxManager(JPAMailboxSessionMapperFactory mapperFactory, Authenticator authenticator) { - this(mapperFactory, authenticator, new JVMMailboxPathLocker(), false); + public OpenJPAMailboxManager(JPAMailboxSessionMapperFactory mapperFactory, Authenticator authenticator, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) { + this(mapperFactory, authenticator, new JVMMailboxPathLocker(), false, aclResolver, groupMembershipResolver); } @Override protected StoreMessageManager createMessageManager(Mailbox mailboxRow, MailboxSession session) throws MailboxException { - StoreMessageManager result = new OpenJPAMessageManager(getMapperFactory(), getMessageSearchIndex(), getEventDispatcher(), getLocker(), mailboxRow, feature); + StoreMessageManager result = new OpenJPAMessageManager(getMapperFactory(), getMessageSearchIndex(), getEventDispatcher(), getLocker(), mailboxRow, feature, getAclResolver(), getGroupMembershipResolver()); return result; } } Index: mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/openjpa/OpenJPAMessageManager.java =================================================================== --- mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/openjpa/OpenJPAMessageManager.java (revision 1232377) +++ mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/openjpa/OpenJPAMessageManager.java (working copy) @@ -24,6 +24,8 @@ import javax.mail.Flags; import javax.mail.internet.SharedInputStream; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPathLocker; import org.apache.james.mailbox.jpa.JPAMessageManager; @@ -51,13 +53,13 @@ } public OpenJPAMessageManager(MailboxSessionMapperFactory mapperFactory, MessageSearchIndex index, - MailboxEventDispatcher dispatcher, MailboxPathLocker locker, Mailbox mailbox) throws MailboxException { - this(mapperFactory, index, dispatcher, locker, mailbox, AdvancedFeature.None); + MailboxEventDispatcher dispatcher, MailboxPathLocker locker, Mailbox mailbox, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) throws MailboxException { + this(mapperFactory, index, dispatcher, locker, mailbox, AdvancedFeature.None, aclResolver, groupMembershipResolver); } public OpenJPAMessageManager(MailboxSessionMapperFactory mapperFactory, MessageSearchIndex index, - MailboxEventDispatcher dispatcher, MailboxPathLocker locker, Mailbox mailbox, final AdvancedFeature f) throws MailboxException { - super(mapperFactory, index, dispatcher, locker, mailbox); + MailboxEventDispatcher dispatcher, MailboxPathLocker locker, Mailbox mailbox, final AdvancedFeature f, MailboxACLResolver aclResolver, GroupMembershipResolver groupMembershipResolver) throws MailboxException { + super(mapperFactory, index, dispatcher, locker, mailbox, aclResolver, groupMembershipResolver); this.feature = f; } Index: mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAMailboxManagerTest.java =================================================================== --- mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAMailboxManagerTest.java (revision 1232377) +++ mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAMailboxManagerTest.java (working copy) @@ -23,9 +23,13 @@ import javax.persistence.EntityManagerFactory; import org.apache.james.mailbox.BadCredentialsException; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.AbstractMailboxManagerTest; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.jpa.mail.JPAModSeqProvider; import org.apache.james.mailbox.jpa.mail.JPAUidProvider; import org.apache.james.mailbox.jpa.mail.model.JPAMailbox; @@ -99,7 +103,10 @@ JVMMailboxPathLocker locker = new JVMMailboxPathLocker(); JPAMailboxSessionMapperFactory mf = new JPAMailboxSessionMapperFactory(entityManagerFactory, new JPAUidProvider(locker, entityManagerFactory), new JPAModSeqProvider(locker, entityManagerFactory)); - JPAMailboxManager mailboxManager = new OpenJPAMailboxManager(mf, null); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + JPAMailboxManager mailboxManager = new OpenJPAMailboxManager(mf, null, aclResolver, groupMembershipResolver); mailboxManager.init(); setMailboxManager(mailboxManager); Index: mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAStressTest.java =================================================================== --- mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAStressTest.java (revision 1232377) +++ mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/JPAStressTest.java (working copy) @@ -24,9 +24,13 @@ import javax.persistence.EntityManagerFactory; import org.apache.james.mailbox.AbstractStressTest; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.jpa.mail.JPAModSeqProvider; import org.apache.james.mailbox.jpa.mail.JPAUidProvider; import org.apache.james.mailbox.jpa.mail.model.JPAMailbox; @@ -75,7 +79,10 @@ JVMMailboxPathLocker locker = new JVMMailboxPathLocker(); JPAMailboxSessionMapperFactory mf = new JPAMailboxSessionMapperFactory(entityManagerFactory, new JPAUidProvider(locker, entityManagerFactory), new JPAModSeqProvider(locker, entityManagerFactory)); - mailboxManager = new OpenJPAMailboxManager(mf, null); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + mailboxManager = new OpenJPAMailboxManager(mf, null, aclResolver, groupMembershipResolver); mailboxManager.init(); // Set the lock timeout via SQL because of a bug in openJPA Index: mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java =================================================================== --- mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java (revision 1232377) +++ mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java (working copy) @@ -34,7 +34,9 @@ import javax.mail.Flags; import javax.mail.Flags.Flag; +import org.apache.james.mailbox.MailboxACL; import org.apache.james.mailbox.SearchQuery; +import org.apache.james.mailbox.SimpleMailboxACL; import org.apache.james.mailbox.SearchQuery.AddressType; import org.apache.james.mailbox.SearchQuery.DateResolution; import org.apache.james.mailbox.SearchQuery.Sort.SortClause; @@ -742,5 +744,22 @@ return 0; } + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#getACL() + */ + @Override + public MailboxACL getACL() { + return SimpleMailboxACL.OWNER_FULL_ACL; + } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#setACL(org.apache.james.mailbox.MailboxACL) + */ + @Override + public void setACL(MailboxACL acl) { + throw new UnsupportedOperationException("Not supported"); + } + + } } Index: mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java =================================================================== --- mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java (revision 1232377) +++ mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java (working copy) @@ -21,11 +21,14 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; @@ -33,14 +36,19 @@ import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import java.util.Properties; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ArrayUtils; +import org.apache.james.mailbox.MailboxACL; +import org.apache.james.mailbox.MailboxACL.MailboxACLEntryKey; +import org.apache.james.mailbox.MailboxACL.MailboxACLRights; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPath; import org.apache.james.mailbox.MailboxPathLocker; +import org.apache.james.mailbox.SimpleMailboxACL; import org.apache.james.mailbox.MailboxPathLocker.LockAwareExecution; import org.apache.james.mailbox.MailboxSession; @@ -48,6 +56,7 @@ public static final String VALIDITY_FILE = "james-uidvalidity"; public static final String UIDLIST_FILE = "james-uidlist"; + public static final String ACL_FILE = "james-acl"; public static final String CUR = "cur"; public static final String NEW = "new"; public static final String TMP = "tmp"; @@ -57,10 +66,12 @@ private File newFolder; private File tmpFolder; private File uidFile; + private File aclFile; private long lastUid = -1; private int messageCount = 0; private long uidValidity = -1; + private MailboxACL acl; private final MailboxPathLocker locker; @@ -77,6 +88,7 @@ this.newFolder = new File(rootFolder, NEW); this.tmpFolder = new File(rootFolder, TMP); this.uidFile = new File(rootFolder, UIDLIST_FILE); + this.aclFile = new File(rootFolder, ACL_FILE); this.locker = locker; this.path = path; } @@ -201,7 +213,7 @@ } - + /** * Returns the uidValidity of this mailbox * @return The uidValidity @@ -885,4 +897,93 @@ return getRootFile().getAbsolutePath(); } + public MailboxACL getACL(final MailboxSession session) throws MailboxException { + if (acl == null) { + acl = readACL(session); + } + return acl; + } + + /** + * Read the ACL of the given mailbox from the file system. + * + * @param session + * @throws MailboxException if there are problems with the aclFile file + */ + private MailboxACL readACL(MailboxSession session) throws MailboxException { + // FIXME Do we need this locking? + return locker.executeWithLock(session, path, new LockAwareExecution() { + + @Override + public MailboxACL execute() throws MailboxException { + File f = aclFile; + InputStream in = null; + Properties props = new Properties(); + if (f.exists()) { + try { + in = new FileInputStream(f); + props.load(in); + } catch (FileNotFoundException e) { + throw new MailboxException("Unable to read last ACL from "+ f.getAbsolutePath(), e); + } catch (IOException e) { + throw new MailboxException("Unable to read last ACL from "+ f.getAbsolutePath(), e); + } + finally { + IOUtils.closeQuietly(in); + } + } + + return new SimpleMailboxACL(props); + + } + }, true); + + } + + public void setACL(final MailboxSession session, final MailboxACL acl) throws MailboxException { + MailboxACL old = this.acl; + if (old != acl && (old == null || !old.equals(acl))) { + /* change only if different */ + saveACL(acl, session); + this.acl = acl; + } + + } + + private void saveACL(final MailboxACL acl, final MailboxSession session) throws MailboxException { + // FIXME Do we need this locking? + locker.executeWithLock(session, path, new LockAwareExecution() { + + @Override + public Void execute() throws MailboxException { + File f = aclFile; + OutputStream out = null; + Properties props = new Properties(); + Map entries = acl.getEntries(); + if (entries != null) { + for (Entry en : entries.entrySet()) { + props.put(en.getKey().serialize(), en.getValue().serialize()); + } + } + if (f.exists()) { + try { + out = new FileOutputStream(f); + props.store(out, "written by "+ getClass().getName()); + } catch (FileNotFoundException e) { + throw new MailboxException("Unable to read last ACL from "+ f.getAbsolutePath(), e); + } catch (IOException e) { + throw new MailboxException("Unable to read last ACL from "+ f.getAbsolutePath(), e); + } + finally { + IOUtils.closeQuietly(out); + } + } + + return null; + + } + }, true); + } + + } Index: mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirStore.java =================================================================== --- mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirStore.java (revision 1232377) +++ mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirStore.java (working copy) @@ -28,6 +28,7 @@ import org.apache.james.mailbox.MailboxPath; import org.apache.james.mailbox.MailboxPathLocker; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.maildir.mail.model.MaildirMailbox; import org.apache.james.mailbox.store.JVMMailboxPathLocker; import org.apache.james.mailbox.store.mail.ModSeqProvider; import org.apache.james.mailbox.store.mail.UidProvider; @@ -118,12 +119,9 @@ * @throws MailboxException If the mailbox folder doesn't exist or can't be read */ private Mailbox loadMailbox(MailboxSession session, File mailboxFile, MailboxPath mailboxPath) throws MailboxException { - long uidValidity; MaildirFolder folder = new MaildirFolder(mailboxFile.getAbsolutePath(), mailboxPath, locker); try { - uidValidity = folder.getUidValidity(); - return new SimpleMailbox(mailboxPath, uidValidity); - + return new MaildirMailbox(session, mailboxPath, folder); } catch (IOException e) { throw new MailboxException("Unable to load Mailbox " + mailboxPath, e); } Index: mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMailbox.java =================================================================== --- mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMailbox.java (revision 0) +++ mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMailbox.java (revision 0) @@ -0,0 +1,41 @@ +package org.apache.james.mailbox.maildir.mail.model; + +import java.io.IOException; + +import org.apache.james.mailbox.MailboxACL; +import org.apache.james.mailbox.MailboxException; +import org.apache.james.mailbox.MailboxPath; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.maildir.MaildirFolder; +import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox; + +public class MaildirMailbox extends SimpleMailbox { + + private MaildirFolder folder; + private MailboxSession session; + + public MaildirMailbox(MailboxSession session, MailboxPath path, MaildirFolder folder) throws IOException { + super(path, folder.getUidValidity()); + this.folder = folder; + this.session = session; + } + + @Override + public MailboxACL getACL() { + try { + return folder.getACL(session); + } catch (MailboxException e) { + throw new RuntimeException(e); + } + } + + @Override + public void setACL(MailboxACL acl) { + try { + folder.setACL(session, acl); + } catch (MailboxException e) { + throw new RuntimeException(e); + } + } + +} Index: mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/MaildirMailboxManagerTest.java =================================================================== --- mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/MaildirMailboxManagerTest.java (revision 1232377) +++ mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/MaildirMailboxManagerTest.java (working copy) @@ -27,8 +27,12 @@ import org.apache.commons.io.FileUtils; import org.apache.james.mailbox.AbstractMailboxManagerTest; import org.apache.james.mailbox.BadCredentialsException; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxExistsException; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.store.JVMMailboxPathLocker; import org.apache.james.mailbox.store.StoreMailboxManager; import org.junit.After; @@ -110,7 +114,11 @@ MaildirStore store = new MaildirStore(MAILDIR_HOME + "/%domain/%user", new JVMMailboxPathLocker()); MaildirMailboxSessionMapperFactory mf = new MaildirMailboxSessionMapperFactory(store); - StoreMailboxManager manager = new StoreMailboxManager(mf, null, new JVMMailboxPathLocker()); + + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + StoreMailboxManager manager = new StoreMailboxManager(mf, null, new JVMMailboxPathLocker(), aclResolver, groupMembershipResolver); manager.init(); setMailboxManager(manager); try { @@ -140,7 +148,10 @@ MaildirStore store = new MaildirStore(MAILDIR_HOME + "/%domain/%user", new JVMMailboxPathLocker()); MaildirMailboxSessionMapperFactory mf = new MaildirMailboxSessionMapperFactory(store); - StoreMailboxManager manager = new StoreMailboxManager(mf, null, new JVMMailboxPathLocker()); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + StoreMailboxManager manager = new StoreMailboxManager(mf, null, new JVMMailboxPathLocker(), aclResolver, groupMembershipResolver); manager.init(); setMailboxManager(manager); try { @@ -169,7 +180,10 @@ private void doTestListWithMaildirStoreConfiguration(String maildirStoreConfiguration) throws MailboxException, UnsupportedEncodingException { MaildirStore store = new MaildirStore(MAILDIR_HOME + maildirStoreConfiguration, new JVMMailboxPathLocker()); MaildirMailboxSessionMapperFactory mf = new MaildirMailboxSessionMapperFactory(store); - StoreMailboxManager manager = new StoreMailboxManager(mf, null, new JVMMailboxPathLocker()); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + StoreMailboxManager manager = new StoreMailboxManager(mf, null, new JVMMailboxPathLocker(), aclResolver, groupMembershipResolver); manager.init(); setMailboxManager(manager); try { Index: mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/MaildirStressTest.java =================================================================== --- mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/MaildirStressTest.java (revision 1232377) +++ mailbox/maildir/src/test/java/org/apache/james/mailbox/maildir/MaildirStressTest.java (working copy) @@ -23,8 +23,12 @@ import org.apache.commons.io.FileUtils; import org.apache.james.mailbox.AbstractStressTest; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.store.JVMMailboxPathLocker; import org.apache.james.mailbox.store.StoreMailboxManager; import org.junit.After; @@ -41,7 +45,10 @@ MaildirStore store = new MaildirStore(MAILDIR_HOME + "/%user", new JVMMailboxPathLocker()); MaildirMailboxSessionMapperFactory mf = new MaildirMailboxSessionMapperFactory(store); - mailboxManager = new StoreMailboxManager(mf, null, new JVMMailboxPathLocker()); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + mailboxManager = new StoreMailboxManager(mf, null, new JVMMailboxPathLocker(), aclResolver, groupMembershipResolver); mailboxManager.init(); } Index: mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/InMemoryMailboxManagerTest.java =================================================================== --- mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/InMemoryMailboxManagerTest.java (revision 1232377) +++ mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/InMemoryMailboxManagerTest.java (working copy) @@ -19,9 +19,13 @@ package org.apache.james.mailbox.inmemory; import org.apache.james.mailbox.BadCredentialsException; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.AbstractMailboxManagerTest; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.store.MockAuthenticator; import org.apache.james.mailbox.store.StoreMailboxManager; import org.junit.After; @@ -62,7 +66,10 @@ protected void createMailboxManager() throws MailboxException { InMemoryMailboxSessionMapperFactory factory = new InMemoryMailboxSessionMapperFactory(); - StoreMailboxManager mailboxManager = new StoreMailboxManager(factory, new MockAuthenticator()); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + + StoreMailboxManager mailboxManager = new StoreMailboxManager(factory, new MockAuthenticator(), aclResolver, groupMembershipResolver); mailboxManager.init(); setMailboxManager(mailboxManager); Index: mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-hbase.xml =================================================================== --- mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-hbase.xml (revision 1232377) +++ mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-hbase.xml (working copy) @@ -41,6 +41,8 @@ + + Index: mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-jcr.xml =================================================================== --- mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-jcr.xml (revision 1232377) +++ mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-jcr.xml (working copy) @@ -31,6 +31,8 @@ + + Index: mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-jpa.xml =================================================================== --- mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-jpa.xml (revision 1232377) +++ mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-jpa.xml (working copy) @@ -34,6 +34,8 @@ + + Index: mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-maildir.xml =================================================================== --- mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-maildir.xml (revision 1232377) +++ mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-maildir.xml (working copy) @@ -37,6 +37,8 @@ + + Index: mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-memory.xml =================================================================== --- mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-memory.xml (revision 1232377) +++ mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox-memory.xml (working copy) @@ -33,6 +33,8 @@ + + Index: mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox.xml =================================================================== --- mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox.xml (revision 1232377) +++ mailbox/spring/src/main/resources/META-INF/org/apache/james/spring-mailbox.xml (working copy) @@ -46,5 +46,14 @@ Mailbox Copier --> + + + + + Index: mailbox/store/src/main/java/org/apache/james/mailbox/store/MailboxMetaData.java =================================================================== --- mailbox/store/src/main/java/org/apache/james/mailbox/store/MailboxMetaData.java (revision 1232377) +++ mailbox/store/src/main/java/org/apache/james/mailbox/store/MailboxMetaData.java (working copy) @@ -24,9 +24,9 @@ import javax.mail.Flags; +import org.apache.james.mailbox.MailboxACL; import org.apache.james.mailbox.MessageManager; - /** * Describes the current state of a mailbox. */ @@ -43,9 +43,9 @@ private final boolean writeable; private final long highestModSeq; private final boolean modSeqPermanent; - - public MailboxMetaData(final List recent, final Flags premanentFlags, final long uidValidity, final long nextUid, final long highestModSeq, - final long messageCount, final long unseenCount, final Long firstUnseen, final boolean writeable, final boolean modSeqPermanent) { + private final MailboxACL acl; + + public MailboxMetaData(final List recent, final Flags premanentFlags, final long uidValidity, final long nextUid, final long highestModSeq, final long messageCount, final long unseenCount, final Long firstUnseen, final boolean writeable, final boolean modSeqPermanent, MailboxACL acl) { super(); if (recent == null) { this.recent = new ArrayList(); @@ -64,6 +64,7 @@ this.firstUnseen = firstUnseen; this.writeable = writeable; this.modSeqPermanent = modSeqPermanent; + this.acl = acl; } /** @@ -142,4 +143,14 @@ public boolean isModSeqPermanent() { return modSeqPermanent; } + + /* + * (non-Javadoc) + * + * @see org.apache.james.mailbox.MessageManager.MetaData#getACL() + */ + @Override + public MailboxACL getACL() { + return acl; + } } Index: mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java =================================================================== --- mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java (revision 1232377) +++ mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java (working copy) @@ -27,24 +27,26 @@ import java.util.Random; import org.apache.james.mailbox.BadCredentialsException; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxConstants; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxExistsException; import org.apache.james.mailbox.MailboxListener; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxMetaData; +import org.apache.james.mailbox.MailboxMetaData.Selectability; import org.apache.james.mailbox.MailboxNotFoundException; import org.apache.james.mailbox.MailboxPath; import org.apache.james.mailbox.MailboxPathLocker; +import org.apache.james.mailbox.MailboxPathLocker.LockAwareExecution; import org.apache.james.mailbox.MailboxQuery; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MailboxSession.SessionType; import org.apache.james.mailbox.MailboxSessionIdGenerator; import org.apache.james.mailbox.MessageRange; import org.apache.james.mailbox.RequestAware; import org.apache.james.mailbox.StandardMailboxMetaDataComparator; -import org.apache.james.mailbox.MailboxMetaData.Selectability; -import org.apache.james.mailbox.MailboxPathLocker.LockAwareExecution; -import org.apache.james.mailbox.MailboxSession.SessionType; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.model.Mailbox; import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox; @@ -74,11 +76,14 @@ private final MailboxSessionMapperFactory mailboxSessionMapperFactory; private final Authenticator authenticator; + + private final MailboxACLResolver aclResolver; + + private final GroupMembershipResolver groupMembershipResolver; + private final static Random RANDOM = new Random(); private int copyBatchSize = 0; - - private MailboxPathLocker locker; @@ -89,14 +94,16 @@ private int fetchBatchSize = DEFAULT_FETCH_BATCH_SIZE; - public StoreMailboxManager(MailboxSessionMapperFactory mailboxSessionMapperFactory, final Authenticator authenticator, final MailboxPathLocker locker) { + public StoreMailboxManager(MailboxSessionMapperFactory mailboxSessionMapperFactory, final Authenticator authenticator, final MailboxPathLocker locker, final MailboxACLResolver aclResolver, final GroupMembershipResolver groupMembershipResolver) { this.authenticator = authenticator; this.locker = locker; - this.mailboxSessionMapperFactory = mailboxSessionMapperFactory; + this.mailboxSessionMapperFactory = mailboxSessionMapperFactory; + this.aclResolver = aclResolver; + this.groupMembershipResolver = groupMembershipResolver; } - public StoreMailboxManager(MailboxSessionMapperFactory mailboxSessionMapperFactory, final Authenticator authenticator) { - this(mailboxSessionMapperFactory, authenticator, new JVMMailboxPathLocker()); + public StoreMailboxManager(MailboxSessionMapperFactory mailboxSessionMapperFactory, final Authenticator authenticator, final MailboxACLResolver aclResolver, final GroupMembershipResolver groupMembershipResolver) { + this(mailboxSessionMapperFactory, authenticator, new JVMMailboxPathLocker(), aclResolver, groupMembershipResolver); } public void setMailboxSessionIdGenerator(MailboxSessionIdGenerator idGenerator) { @@ -179,6 +186,13 @@ return locker; } + public MailboxACLResolver getAclResolver() { + return aclResolver; + } + + public GroupMembershipResolver getGroupMembershipResolver() { + return groupMembershipResolver; + } /** * Set the {@link AbstractDelegatingMailboxListener} to use with this {@link MailboxManager} instance. If none is set here a {@link HashMapDelegatingMailboxListener} instance will @@ -284,7 +298,7 @@ * @return storeMailbox */ protected StoreMessageManager createMessageManager(Mailbox mailbox, MailboxSession session) throws MailboxException { - return new StoreMessageManager(getMapperFactory(), getMessageSearchIndex(), getEventDispatcher(), getLocker(), mailbox); + return new StoreMessageManager(getMapperFactory(), getMessageSearchIndex(), getEventDispatcher(), getLocker(), mailbox, getAclResolver(), getGroupMembershipResolver()); } /** Index: mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java =================================================================== --- mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java (revision 1232377) +++ mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java (working copy) @@ -38,18 +38,24 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.TeeInputStream; +import org.apache.james.mailbox.MailboxACL; +import org.apache.james.mailbox.MailboxACL.MailboxACLRight; +import org.apache.james.mailbox.MailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxListener; import org.apache.james.mailbox.MailboxPathLocker; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MailboxSession.User; import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.MessageMetaData; import org.apache.james.mailbox.MessageRange; +import org.apache.james.mailbox.MessageResult.FetchGroup; import org.apache.james.mailbox.MessageResultIterator; import org.apache.james.mailbox.ReadOnlyException; import org.apache.james.mailbox.SearchQuery; +import org.apache.james.mailbox.UnsupportedRightException; import org.apache.james.mailbox.UpdatedFlags; -import org.apache.james.mailbox.MessageResult.FetchGroup; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.MessageMapper.FetchType; import org.apache.james.mailbox.store.mail.MessageMapperFactory; @@ -103,17 +109,23 @@ private final MessageMapperFactory mapperFactory; private final MessageSearchIndex index; + + private final MailboxACLResolver aclResolver; + + private final GroupMembershipResolver groupMembershipResolver; private MailboxPathLocker locker; private int fetchBatchSize; - public StoreMessageManager(final MessageMapperFactory mapperFactory, final MessageSearchIndex index, final MailboxEventDispatcher dispatcher, final MailboxPathLocker locker, final Mailbox mailbox) throws MailboxException { + public StoreMessageManager(final MessageMapperFactory mapperFactory, final MessageSearchIndex index, final MailboxEventDispatcher dispatcher, final MailboxPathLocker locker, final Mailbox mailbox, final MailboxACLResolver aclResolver, final GroupMembershipResolver groupMembershipResolver) throws MailboxException { this.mailbox = mailbox; this.dispatcher = dispatcher; this.mapperFactory = mapperFactory; this.index = index; this.locker = locker; + this.aclResolver = aclResolver; + this.groupMembershipResolver = groupMembershipResolver; } public void setFetchBatchSize(int fetchBatchSize) { @@ -395,6 +407,7 @@ */ public MetaData getMetaData(boolean resetRecent, MailboxSession mailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup fetchGroup) throws MailboxException { + final List recent; final Flags permanentFlags = getPermanentFlags(mailboxSession); final long uidValidity = getMailboxEntity().getUidValidity(); @@ -436,7 +449,10 @@ recent = new ArrayList(); break; } - return new MailboxMetaData(recent, permanentFlags, uidValidity, uidNext,highestModSeq, messageCount, unseenCount, firstUnseen, isWriteable(mailboxSession), isModSeqPermanent(mailboxSession)); + // FIXME + boolean resourceOwnerIsGroup = false; + MailboxACL resolvedAcl = aclResolver.applyGlobalACL(mailbox.getACL(), resourceOwnerIsGroup ); + return new MailboxMetaData(recent, permanentFlags, uidValidity, uidNext,highestModSeq, messageCount, unseenCount, firstUnseen, isWriteable(mailboxSession), isModSeqPermanent(mailboxSession), resolvedAcl ); } @@ -676,4 +692,15 @@ MessageMapper messageMapper = mapperFactory.getMessageMapper(session); return messageMapper.findFirstUnseenMessageUid(getMailboxEntity()); } + + public boolean hasRight(MailboxACLRight right, MailboxSession session) throws UnsupportedRightException { + User user = session.getUser(); + String userName = user != null ? user.getUserName() : null; + + // FIXME probably mailbox should provide the information whether the resource owner is group; otherwise user names and group names may clash + boolean resourceOwnerIsGroup = false; + + return aclResolver.hasRight(userName, groupMembershipResolver, right, mailbox.getACL(), mailbox.getUser(), resourceOwnerIsGroup); + } + } Index: mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/Mailbox.java =================================================================== --- mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/Mailbox.java (revision 1232377) +++ mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/Mailbox.java (working copy) @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.james.mailbox.store.mail.model; +import org.apache.james.mailbox.MailboxACL; + /** * Models long term mailbox data. */ @@ -70,4 +72,20 @@ * @return uid validity */ long getUidValidity(); + + + /** + * Gets the current ACL for this mailbox. + * + * @return ACL + */ + MailboxACL getACL(); + + /** + * Sets the current ACL for this mailbox. + * + * @param acl + */ + void setACL(MailboxACL acl); + } \ No newline at end of file Index: mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailbox.java =================================================================== --- mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailbox.java (revision 1232377) +++ mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailbox.java (working copy) @@ -18,7 +18,9 @@ ****************************************************************/ package org.apache.james.mailbox.store.mail.model.impl; +import org.apache.james.mailbox.MailboxACL; import org.apache.james.mailbox.MailboxPath; +import org.apache.james.mailbox.SimpleMailboxACL; import org.apache.james.mailbox.store.mail.model.Mailbox; public class SimpleMailbox implements Mailbox { @@ -28,6 +30,7 @@ private String user; private String name; private long uidValidity; + private MailboxACL acl = SimpleMailboxACL.EMPTY; public SimpleMailbox(MailboxPath path, long uidValidity) { this.namespace = path.getNamespace(); @@ -148,4 +151,20 @@ this.id = id; } + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#getACL() + */ + @Override + public MailboxACL getACL() { + return acl; + } + + /* (non-Javadoc) + * @see org.apache.james.mailbox.store.mail.model.Mailbox#setACL(org.apache.james.mailbox.MailboxACL) + */ + @Override + public void setACL(MailboxACL acl) { + this.acl = acl; + } + } Index: mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherFlagsTest.java =================================================================== --- mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherFlagsTest.java (revision 1232377) +++ mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherFlagsTest.java (working copy) @@ -26,9 +26,11 @@ import javax.mail.Flags; +import org.apache.james.mailbox.MailboxACL; import org.apache.james.mailbox.MailboxListener; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageResult; +import org.apache.james.mailbox.SimpleMailboxACL; import org.apache.james.mailbox.UpdatedFlags; import org.apache.james.mailbox.mock.MockMailboxSession; import org.apache.james.mailbox.store.MailboxEventDispatcher; @@ -102,6 +104,16 @@ public long getUidValidity() { return 0; } + + @Override + public MailboxACL getACL() { + return SimpleMailboxACL.EMPTY; + } + + @Override + public void setACL(MailboxACL acl) { + } + }; @Before Index: mailbox/tool/src/test/java/org/apache/james/mailbox/copier/MailboxCopierTest.java =================================================================== --- mailbox/tool/src/test/java/org/apache/james/mailbox/copier/MailboxCopierTest.java (revision 1232377) +++ mailbox/tool/src/test/java/org/apache/james/mailbox/copier/MailboxCopierTest.java (working copy) @@ -24,11 +24,15 @@ import junit.framework.Assert; import org.apache.james.mailbox.BadCredentialsException; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxPath; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; import org.apache.james.mailbox.mock.MockMailboxManager; import org.apache.james.mailbox.store.Authenticator; @@ -147,14 +151,19 @@ * @return a new InMemoryMailboxManager */ private MailboxManager newInMemoryMailboxManager() { - + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); + return new StoreMailboxManager( new InMemoryMailboxSessionMapperFactory(), new Authenticator() { public boolean isAuthentic(String userid, CharSequence passwd) { return true; } - }); + }, + aclResolver, + groupMembershipResolver + ); } Index: server/pop3server/src/test/java/org/apache/james/pop3server/POP3ServerTest.java =================================================================== --- server/pop3server/src/test/java/org/apache/james/pop3server/POP3ServerTest.java (revision 1232377) +++ server/pop3server/src/test/java/org/apache/james/pop3server/POP3ServerTest.java (working copy) @@ -34,11 +34,15 @@ import org.apache.commons.net.pop3.POP3MessageInfo; import org.apache.commons.net.pop3.POP3Reply; import org.apache.james.filesystem.api.mock.MockFileSystem; +import org.apache.james.mailbox.MailboxACLResolver; import org.apache.james.mailbox.MailboxConstants; import org.apache.james.mailbox.MailboxException; import org.apache.james.mailbox.MailboxPath; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.SimpleGroupMembershipResolver; +import org.apache.james.mailbox.UnionMailboxACLResolver; +import org.apache.james.mailbox.MailboxACLResolver.GroupMembershipResolver; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; import org.apache.james.mailbox.store.Authenticator; import org.apache.james.mailbox.store.StoreMailboxManager; @@ -110,19 +114,25 @@ chain.put("usersrepository", m_usersRepository); InMemoryMailboxSessionMapperFactory factory = new InMemoryMailboxSessionMapperFactory(); + MailboxACLResolver aclResolver = new UnionMailboxACLResolver(); + GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver(); - manager = new StoreMailboxManager(factory, new Authenticator() { - - public boolean isAuthentic(String userid, CharSequence passwd) { - try { - return m_usersRepository.test(userid, passwd.toString()); - } catch (UsersRepositoryException e) { - - e.printStackTrace(); - return false; - } - } - }); + manager = new StoreMailboxManager( + factory, + new Authenticator() { + public boolean isAuthentic(String userid, CharSequence passwd) { + try { + return m_usersRepository.test(userid, passwd.toString()); + } catch (UsersRepositoryException e) { + + e.printStackTrace(); + return false; + } + } + }, + aclResolver, + groupMembershipResolver + ); manager.init(); chain.put("mailboxmanager", manager);