gessnerfl / fake-smtp-server

A simple SMTP Server for Testing purposes. Emails are stored in an in-memory database and rendered in a Web UI
Apache License 2.0
437 stars 90 forks source link

Authenticate does not properly authenticate #16

Closed pat-lego closed 5 years ago

pat-lego commented 5 years ago

When users have .auth() or (.userName() and .password()) enabled on the @Rule for the SMTP server the authentication mechanism of the mock server throws a Null Pointer Error.

Debugged it and saw that the error occurs at the following line: if (clientInput.trim().equals(AUTH_CANCEL_COMMAND)) in the AuthCommand class returns null instead of the an empty string.

This is stopping users from properly testing the SMTP server with Authentication.

gessnerfl commented 5 years ago

@pat-lego I tried to reproduce the issue but without success. Basically if (clientInput.trim().equals(AUTH_CANCEL_COMMAND)) is part of the smtp server library https://github.com/voodoodyne/subethasmtp which I used in this project.

I created a new branch with integration tests. Can you please provide more details how the servers are configured and also a bit more details about the client setup. A stacktrace or log would also be helpful.

pat-lego commented 5 years ago

I have the following in my Class:

@ClassRule public static FakeSmtpRule smtpServer = new FakeSmtpRule(ServerConfiguration.create().port(2525).charset("UTF-8").userName("someusername").password("somepassword"));

I use the javax.mail library in the OpenJDK 64 bit Java 11 to send emails to the fake SMTP server and when I try to send emails with that username and password I get a NullPointerError.

Let me know if you require more information to reproduce this error.

gessnerfl commented 5 years ago

@pat-lego It would be great to get some more details about the FakeSmtpRule and the ServerConfiguration. What is it doing specifically? Can you maybe share some code snippets. This could help me to reproduce the issue.

pat-lego commented 5 years ago

Email Client:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.commons.io.IOUtils;

import com.outreach.core.enumerations.EncryptionType;

public class SendEmail 
{
    private Properties properties;
    private Session session;

    public SendEmail(String host, String port, String username, String password, EncryptionType e) {
        this.properties = new Properties();
        this.properties.put("mail.smtp.host", host);
        this.properties.put("mail.smtp.port", port);
        this.properties.put("mail.smtp.auth", "true");

        if(e.equals(EncryptionType.TLS))
            this.properties.put("mail.smtp.starttls.enable","true");

        if(e.equals(EncryptionType.SSL)) {
            this.properties.put("mail.smtp.socketFactory.port", port);
            this.properties.put("mail.smtp.socketFactory.class",
                    "javax.net.ssl.SSLSocketFactory");
        }

        this.session = Session.getInstance(this.properties,
                new javax.mail.Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(username, password);
            }
        });
    }

/**
     * Send an email to anyone in either HTML or plain text format
     * @param from User to send the email
     * @param recipient User(s) to receive the email
     * @param subject The message to send
     * @param body The content of the email
     * @param html If the email is in HTML format or not
     * @param encoding The type of character encoding to use for the message
     * @param attachments documents to add to the email
     * @throws AddressException Invalid address 
     * @throws MessagingException Invalid Message
     */
    public void sendMessage(String from, String[] recipient, String subject, String body, Boolean html, String encoding, List<Attachment> attachments) throws AddressException, MessagingException {
        Message message = new MimeMessage(this.session);
        message.setFrom(new InternetAddress(from));
        message.setRecipients(Message.RecipientType.TO,
                InternetAddress.parse(String.join(",", recipient)));
        message.setSubject(subject);

        Multipart multipart;
        if(attachments == null) {
            multipart = this.setContent(html, new MimeBodyPart(), body, encoding);
        }
        else {
            BodyPart messageBodyPart = new MimeBodyPart();
            multipart = this.setContent(html, messageBodyPart, body, encoding);

            for(int i = 0; i < attachments.size(); i++) {
                SendEmail.addAttachment(multipart, attachments.get(i), attachments.get(i).getFileName());
            }
        }

        message.setContent(multipart);
        Transport.send(message);
    }

SMTP Server:

private static SendEmail sendTLSEmail;
    private static SendEmail sendSSLEmail;
    private static String emailTemplate;
    private static String pdf1;
    private static String pdf2;

    @ClassRule
    public static FakeSmtpRule smtpServer = new FakeSmtpRule(ServerConfiguration.create().port(2525).charset("UTF-8").relayDomains("localhost").userName("uottawa.outreach@gmail.com").password("uottawaTestTest");

    @BeforeClass
    public static void static_init() throws IOException {
        TestSendEmail.sendTLSEmail = new SendEmail("localhost", "2525", "uottawa.outreach@gmail.com", "uottawaTestTest", EncryptionType.TLS);
        TestSendEmail.sendSSLEmail = new SendEmail("localhost", "2525", "uottawa.outreach.developers@gmail.com", "uottawa123", EncryptionType.SSL);
        assertNotNull(TestSendEmail.sendTLSEmail);
        assertNotNull(TestSendEmail.sendSSLEmail);

        TestSendEmail.emailTemplate = IOUtils.toString(TestSendEmail.class.getResourceAsStream("/email-templates/template.html"), "UTF-8");
        assertNotNull(TestSendEmail.emailTemplate);

        TestSendEmail.pdf1 = IOUtils.toString(TestSendEmail.class.getResourceAsStream("/email-attachments/sample.pdf"), "UTF-8");
        assertNotNull(TestSendEmail.pdf1);

        TestSendEmail.pdf2 = IOUtils.toString(TestSendEmail.class.getResourceAsStream("/email-attachments/pdf-sample.pdf"), "UTF-8");
        assertNotNull(TestSendEmail.pdf2);
    }

    @Test
    public void assertRunning() {
        assertTrue(smtpServer.isRunning());
    }

    @Test
    public void sendEmailTLS() throws AddressException, MessagingException {
        TestSendEmail.sendTLSEmail.sendMessage("patrique.legault@uottawa.ca", new String[] {"pat@aesstaff.ca"}, "Test", "Test 1,2,3", false, "UTF-8");
    }

    @Test
    public void sendEmailTLSMultiReceipients() throws AddressException, MessagingException {
        TestSendEmail.sendTLSEmail.sendMessage("patrique.legault@uottawa.ca",  new String[] { "patrique.legault@gmail.com", "pat@aesstaff.ca"}, "Test", "Test 1,2,3", false, "UTF-8");
    }

    @Test
    public void sendEmailSSL() throws AddressException, MessagingException {
        TestSendEmail.sendSSLEmail.sendMessage("patrique.legault@uottawa.ca", new String[] {"pat@aesstaff.ca"}, "Test", "Test 1,2,3", false, "UTF-8");
    }

    @Test(expected = java.lang.NullPointerException.class)
    public void sendEmailNoFrom() throws AddressException, MessagingException {
        TestSendEmail.sendTLSEmail.sendMessage(null, new String[] {"pat@aesstaff.ca"}, "Test", "Test 1,2,3", false, "UTF-8");
    }

Above is a snippet of the mail server and mail client that I wrote. Let me know if this helps with the current issue.

When trying to authenticate developers should start seeing errors appear in the tests saying that Authentication could be completed due to the fact that the clientInput variable is null.

gessnerfl commented 5 years ago

@pat-lego I now extended the test cases with your implementation in SendEmail. However I can still not reproduce the issue. You can compare this with the following class and test case:

I'm still wondering what this code is doing:

@ClassRule
public static FakeSmtpRule smtpServer = new FakeSmtpRule(ServerConfiguration.create().port(2525).charset("UTF-8").relayDomains("localhost").userName("uottawa.outreach@gmail.com").password("uottawaTestTest");
pat-lego commented 5 years ago

@gessnerfl

The following line of code is what creates the SMTP server that I then run my client against. I assume that this line creates an SMTP server with the following credentials (userName: uottawa.outreach@gmail.com and password: uottawaTestTest) required to send emails to it. As seen from the documentation here: https://github.com/sleroy/fakesmtp-junit-runner/blob/master/README.md

@ClassRule
public static FakeSmtpRule smtpServer = new FakeSmtpRule(ServerConfiguration.create().port(2525).charset("UTF-8").relayDomains("localhost").userName("uottawa.outreach@gmail.com").password("uottawaTestTest");

Just like in GMAIL in order to send an email you must authenticate yourself against the SMTP servers in order to get the server to send an email. In the code snippet that you have provided to me I did not see anywhere the client authenticating itself against the SMTP server.

I see that in the Mail Sender class that you have procided, it does not use the userName and password provided to the SMTP server and thus for that reason it does not truly perform authentication.

pat-lego commented 5 years ago

This is the error stack that I get from my code:

1 [main] INFO com.github.sleroy.junit.mail.server.MailServer - Configuration of the server. ServerConfiguration [port=2525, bindAddress=null, storageCharsetName=UTF-8, authentication=Authentication [userName=test, password=test]]
4605 [main] INFO org.subethamail.smtp.server.SMTPServer - SMTP server *:2525 starting
4621 [org.subethamail.smtp.server.ServerThread *:2525] INFO org.subethamail.smtp.server.ServerThread - SMTP server *:2525 started
4915 [org.subethamail.smtp.server.Session-/127.0.0.1:54217] ERROR org.subethamail.smtp.server.Session - Unexpected error in the SMTP handler thread
java.lang.NullPointerException
    at org.subethamail.smtp.command.AuthCommand.execute(AuthCommand.java:88)
    at org.subethamail.smtp.server.RequireTLSCommandWrapper.execute(RequireTLSCommandWrapper.java:30)
    at org.subethamail.smtp.server.CommandHandler.handleCommand(CommandHandler.java:99)
    at org.subethamail.smtp.server.Session.runCommandLoop(Session.java:244)
    at org.subethamail.smtp.server.Session.run(Session.java:145)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "pool-1-thread-1" java.lang.NullPointerException
    at org.subethamail.smtp.command.AuthCommand.execute(AuthCommand.java:88)
    at org.subethamail.smtp.server.RequireTLSCommandWrapper.execute(RequireTLSCommandWrapper.java:30)
    at org.subethamail.smtp.server.CommandHandler.handleCommand(CommandHandler.java:99)
    at org.subethamail.smtp.server.Session.runCommandLoop(Session.java:244)
    at org.subethamail.smtp.server.Session.run(Session.java:145)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
4921 [main] INFO com.github.sleroy.junit.mail.server.MailServer - Stopping the server
4921 [main] INFO org.subethamail.smtp.server.SMTPServer - SMTP server *:2525 stopping
4922 [org.subethamail.smtp.server.ServerThread *:2525] INFO org.subethamail.smtp.server.ServerThread - SMTP server *:2525 stopped
gessnerfl commented 5 years ago

@pat-lego I do use login. The MailSender provides both options. However I have the feeling that you mix the projects. I do not see how this project relates to the implementation in https://github.com/sleroy/fakesmtp-junit-runner. I think it is not even used there. The mentioned project also uses https://github.com/voodoodyne/subethasmtp natively. So I guess this is the right place for the issue. Or am I wrong?

pat-lego commented 5 years ago

You are correct I did mix up the projects but I am still able to get the error. Now that being said I am not too sure if it is the GitHub project that is not sending in the data correctly to the https://github.com/voodoodyne/subethasmtp project or if it is a bug within the core project.

My guess based off of your testing is that it's how the other project is communicating with the main library. I will log a ticket on the other github project.

I apologize for the mistake.

Thank you for all your help.

On Dec 12, 2018 2:04 AM, Florian Gessner notifications@github.com wrote:

@pat-legohttps://github.com/pat-lego I do use login. The MailSender provides both options. However I have the feeling that you mix the projects. I do not see how this project relates to the implementation in https://github.com/sleroy/fakesmtp-junit-runner. I think it is not even used there. The mentioned project also uses https://github.com/voodoodyne/subethasmtp natively. So I guess this is the right place for the issue. Or am I wrong?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/gessnerfl/fake-smtp-server/issues/16#issuecomment-446484266, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AjOE1HBbko55431QVBg4rCqQSp6NAB_Dks5u4KqLgaJpZM4ZIuOv.

jabbadev commented 1 year ago

@pat-lego excuse me Patrique i have the same error in my code.

have you fix the problem ?

can you share with me some information about the solution ?