ALF-11837 - now will authenticate via both from fields - also a refactor in anticipation of implemeting multiple recipeints and authentication.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@32731 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mark Rogers
2011-12-13 14:53:18 +00:00
parent e18d1961ab
commit c34da6ea33
5 changed files with 134 additions and 156 deletions

View File

@@ -29,6 +29,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.email.EmailDelivery;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailService; import org.alfresco.service.cmr.email.EmailService;
@@ -155,27 +156,27 @@ public class EmailServiceImpl implements EmailService
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void importMessage(EmailMessage message) public void importMessage(EmailDelivery delivery, EmailMessage message)
{ {
processMessage(null, message); processMessage(delivery, null, message);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public void importMessage(NodeRef nodeRef, EmailMessage message) public void importMessage(EmailDelivery delivery, NodeRef nodeRef, EmailMessage message)
{ {
processMessage(nodeRef, message); processMessage(delivery, nodeRef, message);
} }
/** /**
* Process the message. Method is called after filtering by sender's address. * Process the message. Method is called after filtering by sender's address.
* * @param delivery - who gets the message and who is it from (may be different from the contents of the message)
* @param nodeRef Addressed node (target node). * @param nodeRef Addressed node (target node).
* @param message Email message * @param message Email message
* @throws EmailMessageException Any exception occured inside the method will be converted and thrown as <code>EmailMessageException</code> * @throws EmailMessageException Any exception occured inside the method will be converted and thrown as <code>EmailMessageException</code>
*/ */
private void processMessage(final NodeRef nodeRef, final EmailMessage message) private void processMessage(final EmailDelivery delivery, final NodeRef nodeRef, final EmailMessage message)
{ {
if (!emailInboundEnabled) if (!emailInboundEnabled)
{ {
@@ -186,10 +187,50 @@ public class EmailServiceImpl implements EmailService
// Get the username for the process using the system account // Get the username for the process using the system account
final RetryingTransactionCallback<String> getUsernameCallback = new RetryingTransactionCallback<String>() final RetryingTransactionCallback<String> getUsernameCallback = new RetryingTransactionCallback<String>()
{ {
public String execute() throws Throwable public String execute() throws Throwable
{ {
String from = message.getFrom(); String userName = null;
return getUsername(from);
userName = getUsername(delivery.getFrom());
if(userName == null)
{
if(logger.isDebugEnabled())
{
logger.debug("unable to find user for from: " + delivery.getFrom() + "trying message next");
}
userName = getUsername(message.getFrom());
}
if (userName == null)
{
if(unknownUser.isEmpty())
{
if(logger.isDebugEnabled())
{
logger.debug("unable to find user for from: " + message.getFrom());
}
throw new EmailMessageException(ERR_UNKNOWN_SOURCE_ADDRESS, message.getFrom());
}
else
{
if(logger.isDebugEnabled())
{
logger.debug("unable to find user for from - return anonymous: ");
}
userName = unknownUser;
}
}
// Ensure that the user is part of the Email Contributors group
if (userName == null || !isEmailContributeUser(userName))
{
throw new EmailMessageException(ERR_USER_NOT_EMAIL_CONTRIBUTOR, userName);
}
return userName;
} }
}; };
RunAsWork<String> getUsernameRunAsWork = new RunAsWork<String>() RunAsWork<String> getUsernameRunAsWork = new RunAsWork<String>()
@@ -206,7 +247,8 @@ public class EmailServiceImpl implements EmailService
{ {
public Object execute() throws Throwable public Object execute() throws Throwable
{ {
String recipient = message.getTo(); //String recipient = message.getTo();
String recipient = delivery.getRecipient();
NodeRef targetNodeRef = null; NodeRef targetNodeRef = null;
if (nodeRef == null) if (nodeRef == null)
{ {
@@ -331,7 +373,7 @@ public class EmailServiceImpl implements EmailService
* Authenticate in Alfresco repository by sender's e-mail address. * Authenticate in Alfresco repository by sender's e-mail address.
* *
* @param from Sender's email address * @param from Sender's email address
* @return User name * @return User name or null if the user does not exist.
* @throws EmailMessageException Exception will be thrown if authentication is failed. * @throws EmailMessageException Exception will be thrown if authentication is failed.
*/ */
private String getUsername(String from) private String getUsername(String from)
@@ -345,23 +387,7 @@ public class EmailServiceImpl implements EmailService
{ {
if (resultSet.length() == 0) if (resultSet.length() == 0)
{ {
if (unknownUser == null || unknownUser.length() == 0) return null;
{
if(logger.isDebugEnabled())
{
logger.debug("unable to find user for from: " + from);
}
throw new EmailMessageException(ERR_UNKNOWN_SOURCE_ADDRESS, from);
}
else
{
if(logger.isDebugEnabled())
{
logger.debug("unable to find user for from - return anonymous: " + from);
}
userName = unknownUser;
}
} }
else else
{ {
@@ -381,16 +407,15 @@ public class EmailServiceImpl implements EmailService
} }
finally finally
{ {
resultSet.close(); if(resultSet != null)
} {
// Ensure that the user is part of the Email Contributors group resultSet.close();
if (userName == null || !isEmailContributeUser(userName)) }
{
throw new EmailMessageException(ERR_USER_NOT_EMAIL_CONTRIBUTOR, userName);
} }
return userName; return userName;
} }
/** /**
* Check that the user is the member in <b>EMAIL_CONTRIBUTORS</b> group * Check that the user is the member in <b>EMAIL_CONTRIBUTORS</b> group
* *

View File

@@ -39,6 +39,7 @@ import org.alfresco.email.server.impl.subetha.SubethaEmailMessage;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.email.EmailDelivery;
import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailMessageException;
import org.alfresco.service.cmr.email.EmailService; import org.alfresco.service.cmr.email.EmailService;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -196,9 +197,11 @@ public class EmailServiceImplTest extends TestCase
InputStream is = new StringInputStream(bos.toString()); InputStream is = new StringInputStream(bos.toString());
assertNotNull("is is null", is); assertNotNull("is is null", is);
SubethaEmailMessage m = new SubethaEmailMessage(is); SubethaEmailMessage m = new SubethaEmailMessage(is);
EmailDelivery delivery = new EmailDelivery(to, from, null);
emailService.importMessage(m); emailService.importMessage(delivery, m);
fail("anonymous user not rejected"); fail("anonymous user not rejected");
} }
catch (EmailMessageException e) catch (EmailMessageException e)
@@ -235,9 +238,11 @@ public class EmailServiceImplTest extends TestCase
InputStream is = new StringInputStream(bos.toString()); InputStream is = new StringInputStream(bos.toString());
assertNotNull("is is null", is); assertNotNull("is is null", is);
SubethaEmailMessage m = new SubethaEmailMessage(is); SubethaEmailMessage m = new SubethaEmailMessage(is);
EmailDelivery delivery = new EmailDelivery(to, from, null);
emailService.importMessage(m); emailService.importMessage(delivery, m);
} }
/** /**
@@ -271,9 +276,11 @@ public class EmailServiceImplTest extends TestCase
InputStream is = new StringInputStream(bos.toString()); InputStream is = new StringInputStream(bos.toString());
assertNotNull("is is null", is); assertNotNull("is is null", is);
SubethaEmailMessage m = new SubethaEmailMessage(is); SubethaEmailMessage m = new SubethaEmailMessage(is);
EmailDelivery delivery = new EmailDelivery(to, from, null);
emailService.importMessage(m); emailService.importMessage(delivery,m);
} }
// /** // /**
@@ -385,8 +392,9 @@ public class EmailServiceImplTest extends TestCase
assertNotNull("is is null", is); assertNotNull("is is null", is);
SubethaEmailMessage m = new SubethaEmailMessage(is); SubethaEmailMessage m = new SubethaEmailMessage(is);
EmailDelivery delivery = new EmailDelivery(to, from, null);
emailService.importMessage(m); emailService.importMessage(delivery, m);
} }
@@ -474,12 +482,14 @@ public class EmailServiceImplTest extends TestCase
*/ */
logger.debug("Step 1: turn on Overwite Duplicates"); logger.debug("Step 1: turn on Overwite Duplicates");
folderEmailMessageHandler.setOverwriteDuplicates(true); folderEmailMessageHandler.setOverwriteDuplicates(true);
EmailDelivery delivery = new EmailDelivery(to, from, null);
emailService.importMessage(m); emailService.importMessage(delivery, m);
assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
assertEquals("assocs not 1", 1, assocs.size()); assertEquals("assocs not 1", 1, assocs.size());
assertEquals("name of link not as expected", assocs.get(0).getQName(), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT)); assertEquals("name of link not as expected", assocs.get(0).getQName(), QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT));
emailService.importMessage(m); emailService.importMessage(delivery, m);
assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
assertEquals("assocs not 1", 1, assocs.size()); assertEquals("assocs not 1", 1, assocs.size());
@@ -488,10 +498,10 @@ public class EmailServiceImplTest extends TestCase
*/ */
logger.debug("Step 2: turn off Overwite Duplicates"); logger.debug("Step 2: turn off Overwite Duplicates");
folderEmailMessageHandler.setOverwriteDuplicates(false); folderEmailMessageHandler.setOverwriteDuplicates(false);
emailService.importMessage(m); emailService.importMessage(delivery, m);
assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
assertEquals("assocs not 2", 2, assocs.size()); assertEquals("assocs not 2", 2, assocs.size());
emailService.importMessage(m); emailService.importMessage(delivery, m);
assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
assertEquals("assocs not 3", 3, assocs.size()); assertEquals("assocs not 3", 3, assocs.size());
@@ -507,10 +517,10 @@ public class EmailServiceImplTest extends TestCase
m = new SubethaEmailMessage(is); m = new SubethaEmailMessage(is);
folderEmailMessageHandler.setOverwriteDuplicates(false); folderEmailMessageHandler.setOverwriteDuplicates(false);
emailService.importMessage(m); emailService.importMessage(delivery, m);
assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
assertEquals("assocs not 4", 4, assocs.size()); assertEquals("assocs not 4", 4, assocs.size());
emailService.importMessage(m); emailService.importMessage(delivery, m);
assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
assertEquals("assocs not 5", 5, assocs.size()); assertEquals("assocs not 5", 5, assocs.size());
@@ -528,9 +538,9 @@ public class EmailServiceImplTest extends TestCase
assertNotNull("is is null", is); assertNotNull("is is null", is);
m = new SubethaEmailMessage(is); m = new SubethaEmailMessage(is);
folderEmailMessageHandler.setOverwriteDuplicates(false); folderEmailMessageHandler.setOverwriteDuplicates(false);
emailService.importMessage(m); emailService.importMessage(delivery, m);
emailService.importMessage(m); emailService.importMessage(delivery, m);
emailService.importMessage(m); emailService.importMessage(delivery, m);
assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
List<QName> assocNames = new Vector<QName>(); List<QName> assocNames = new Vector<QName>();
@@ -624,9 +634,11 @@ public class EmailServiceImplTest extends TestCase
InputStream is = new StringInputStream(bos.toString()); InputStream is = new StringInputStream(bos.toString());
assertNotNull("is is null", is); assertNotNull("is is null", is);
SubethaEmailMessage m = new SubethaEmailMessage(is); SubethaEmailMessage m = new SubethaEmailMessage(is);
EmailDelivery delivery = new EmailDelivery(to, from, null);
emailService.importMessage(m); emailService.importMessage(delivery, m);
/** /**
* Step 2 * Step 2
@@ -638,7 +650,7 @@ public class EmailServiceImplTest extends TestCase
{ {
logger.debug("Step 2"); logger.debug("Step 2");
emailServiceImpl.setEmailContributorsAuthority("EMAIL_CONTRIBUTORS"); emailServiceImpl.setEmailContributorsAuthority("EMAIL_CONTRIBUTORS");
emailService.importMessage(m); emailService.importMessage(delivery, m);
fail("not thrown out"); fail("not thrown out");
} }
catch (EmailMessageException e) catch (EmailMessageException e)

View File

@@ -20,6 +20,7 @@ package org.alfresco.email.server;
import org.alfresco.email.server.impl.subetha.SubethaEmailMessage; import org.alfresco.email.server.impl.subetha.SubethaEmailMessage;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.email.EmailDelivery;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailService; import org.alfresco.service.cmr.email.EmailService;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -50,22 +51,22 @@ public class EmailServiceRemotable extends AbstractLifecycleBean implements Emai
this.rmiRegistryPort = rmiRegistryPort; this.rmiRegistryPort = rmiRegistryPort;
} }
public void importMessage(EmailMessage message) public void importMessage(EmailDelivery delivery, EmailMessage message)
{ {
if (message instanceof SubethaEmailMessage) if (message instanceof SubethaEmailMessage)
{ {
((SubethaEmailMessage) message).setRmiRegistry(rmiRegistryHost, rmiRegistryPort); ((SubethaEmailMessage) message).setRmiRegistry(rmiRegistryHost, rmiRegistryPort);
} }
emailServiceProxy.importMessage(message); emailServiceProxy.importMessage(delivery, message);
} }
public void importMessage(NodeRef nodeRef, EmailMessage message) public void importMessage(EmailDelivery delivery, NodeRef nodeRef, EmailMessage message)
{ {
if (message instanceof SubethaEmailMessage) if (message instanceof SubethaEmailMessage)
{ {
((SubethaEmailMessage) message).setRmiRegistry(rmiRegistryHost, rmiRegistryPort); ((SubethaEmailMessage) message).setRmiRegistry(rmiRegistryHost, rmiRegistryPort);
} }
emailServiceProxy.importMessage(nodeRef, message); emailServiceProxy.importMessage(delivery, nodeRef, message);
} }
@Override @Override

View File

@@ -28,6 +28,7 @@ import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress; import javax.mail.internet.InternetAddress;
import org.alfresco.email.server.EmailServer; import org.alfresco.email.server.EmailServer;
import org.alfresco.service.cmr.email.EmailDelivery;
import org.alfresco.service.cmr.email.EmailMessage; import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessageException; import org.alfresco.service.cmr.email.EmailMessageException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@@ -95,7 +96,10 @@ public class SubethaEmailServer extends EmailServer
private List<String> EMPTY_LIST = new LinkedList<String>(); private List<String> EMPTY_LIST = new LinkedList<String>();
private MessageContext messageContext; private MessageContext messageContext;
List<Delivery> deliveries = new ArrayList<Delivery>();
private String from;
List<EmailDelivery> deliveries = new ArrayList<EmailDelivery>();
public Handler(MessageContext messageContext) public Handler(MessageContext messageContext)
{ {
@@ -107,13 +111,12 @@ public class SubethaEmailServer extends EmailServer
return messageContext; return messageContext;
} }
public void from(String fromString) throws RejectException public void from(String fromString) throws RejectException
{ {
String from = fromString;
try try
{ {
InternetAddress a = new InternetAddress(from); InternetAddress a = new InternetAddress(fromString);
from = a.getAddress(); from = a.getAddress();
} }
catch (AddressException e) catch (AddressException e)
@@ -136,79 +139,30 @@ public class SubethaEmailServer extends EmailServer
public void recipient(String recipient) throws RejectException public void recipient(String recipient) throws RejectException
{ {
deliveries.add(new Delivery(recipient)); deliveries.add(new EmailDelivery(recipient, from, null));
} }
public void data(InputStream data) throws TooMuchDataException, IOException, RejectException public void data(InputStream data) throws TooMuchDataException, IOException, RejectException
{
try
{
if (deliveries.size() > 0)
{
Delivery delivery = deliveries.get(0);
processDelivery(delivery, data);
}
}
finally
{
// // DH: As per comments in ETHREEOH-2252, I am very concerned about the need to do the clear() here.
// // If this message is stateful (as it must be, given the API) then the need to clear
// // the list of delivery recipients ('deliveries') implies that Subetha is re-using
// // the instance.
// // Later versions of Subetha appear to define the behaviour better. Un upgrade of
// // the library would be a good idea.
deliveries.clear();
}
// See ALFCOM-3165: Support multiple recipients for inbound Subetha email messages
//
// Duplicate messages coming in
// http://www.subethamail.org/se/archive_msg.jsp?msgId=20938
// if (deliveries.size() == 1)
// {
// Delivery delivery = deliveries.get(0);
// processDelivery(delivery, data);
// }
// else if (deliveries.size() > 1)
// {
// DeferredFileOutputStream dfos = null;
// try
// {
// dfos = new DeferredFileOutputStream(DEFAULT_DATA_DEFERRED_SIZE);
//
// byte[] bytes = new byte[1024 * 8];
// for (int len = -1; (len = data.read(bytes)) != -1;)
// {
// dfos.write(bytes, 0, len);
// }
// for (Delivery delivery : deliveries)
// {
// processDelivery(delivery, dfos.getInputStream());
// }
// }
// finally
// {
// try
// {
// dfos.close();
// }
// catch (Exception e)
// {
// }
// }
// }
}
private void processDelivery(Delivery delivery, InputStream data) throws RejectException
{ {
EmailMessage emailMessage; EmailMessage emailMessage;
try try
{ {
emailMessage = new SubethaEmailMessage(data); emailMessage = new SubethaEmailMessage(data);
getEmailService().importMessage(emailMessage);
// Only send to the first receipient - TODO send to all recipients
if (deliveries.size() > 0)
{
EmailDelivery delivery = deliveries.get(0);
getEmailService().importMessage(delivery, emailMessage);
}
} }
catch (EmailMessageException e) catch (EmailMessageException e)
{ {
throw new RejectException(554, e.getMessage()); if(log.isDebugEnabled())
{
log.debug("about to raise EmailMessageException", e);
}
throw new RejectException(554, e.getMessage());
} }
catch (Throwable e) catch (Throwable e)
{ {
@@ -217,40 +171,26 @@ public class SubethaEmailServer extends EmailServer
} }
} }
public List<String> getAuthenticationMechanisms() // public List<String> getAuthenticationMechanisms()
{ // {
return EMPTY_LIST; // return EMPTY_LIST;
} // }
//
public boolean auth(String clientInput, StringBuffer response) throws RejectException // public boolean auth(String clientInput, StringBuffer response) throws RejectException
{ // {
return true; // return true;
} // }
//
public void resetState() // public void resetState()
{ // {
} // }
//
@Override @Override
public void done() public void done()
{ {
deliveries.clear();
} }
}; };
class Delivery
{
private String recipient;
public Delivery(String recipient)
{
this.recipient = recipient;
}
public String getRecipient()
{
return recipient;
}
};
} }

View File

@@ -40,20 +40,20 @@ public interface EmailService
/** /**
* Processes an email message. The message's content is intended for a node found by * Processes an email message. The message's content is intended for a node found by
* examining the email's target address. * examining the email's target address.
* * @param delivery instructions - who gets the message and who is it from
* @param message the email message * @param message the email message
* @throws EmailMessageRejectException if the message is rejected for <b>any</b> reason * @throws EmailMessageRejectException if the message is rejected for <b>any</b> reason
*/ */
@Auditable(parameters = { "message" }) @Auditable(parameters = { "message" })
void importMessage(EmailMessage message); void importMessage(EmailDelivery delivery, EmailMessage message);
/** /**
* Process an email message. The message's content is intended for a specific node. * Process an email message. The message's content is intended for a specific node.
* * @param delivery instructions - who gets the message and who is it from
* @param nodeRef the node to import the message to * @param nodeRef the node to import the message to
* @param message the email message * @param message the email message
* @throws EmailMessageRejectException if the message is rejected for <b>any</b> reason * @throws EmailMessageRejectException if the message is rejected for <b>any</b> reason
*/ */
@Auditable(parameters = { "nodeRef", "message" }) @Auditable(parameters = { "nodeRef", "message" })
void importMessage(NodeRef nodeRef, EmailMessage message); void importMessage(EmailDelivery delivery, NodeRef nodeRef, EmailMessage message);
} }