Skip to content

Commit

Permalink
JAMES-1856 Write integration tests for SMIME Sign
Browse files Browse the repository at this point in the history
  • Loading branch information
chibenwa committed Nov 17, 2016
1 parent f3a7bec commit 17c79db
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 58 deletions.
5 changes: 5 additions & 0 deletions server/mailet/integration-testing/pom.xml
Expand Up @@ -170,6 +170,11 @@
<groupId>org.apache.james</groupId>
<artifactId>james-server-memory-guice</artifactId>
</dependency>
<dependency>
<groupId>org.apache.james</groupId>
<artifactId>apache-mailet-crypto</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
Expand Down
Expand Up @@ -19,21 +19,17 @@

package org.apache.james.mailets;

import java.io.IOException;
import java.nio.channels.SocketChannel;

import org.apache.commons.net.imap.IMAPClient;
import org.apache.commons.net.smtp.SMTPClient;
import org.apache.james.mailbox.model.MailboxConstants;
import org.apache.james.mailets.configuration.CommonProcessors;
import org.apache.james.mailets.configuration.MailetContainer;
import org.apache.james.mailets.utils.IMAPMessageReader;
import org.apache.james.mailets.utils.SMTPMessageSender;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import com.google.common.base.Throwables;
import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.Duration;
import com.jayway.awaitility.core.ConditionFactory;
Expand Down Expand Up @@ -90,54 +86,11 @@ public void simpleMailShouldBeSent() throws Exception {
jamesServer.getServerProbe().addUser(recipient, PASSWORD);
jamesServer.getServerProbe().createMailbox(MailboxConstants.USER_NAMESPACE, recipient, "INBOX");

SMTPClient smtpClient = new SMTPClient();
try (SocketChannel socketChannel = SocketChannel.open()) {
smtpClient.connect(LOCALHOST_IP, SMTP_PORT);
sendMessage(smtpClient, from, recipient);

calmlyAwait.atMost(Duration.ONE_MINUTE).until(() -> messageHasBeenSent(smtpClient));
calmlyAwait.atMost(Duration.ONE_MINUTE).until(() -> userReceivedMessage(recipient));
} finally {
smtpClient.disconnect();
}
}

private void sendMessage(SMTPClient smtpClient, String from, String recipient) {
try {
smtpClient.helo(DEFAULT_DOMAIN);
smtpClient.setSender(from);
smtpClient.rcpt("<" + recipient + ">");
smtpClient.sendShortMessageData("subject: test\r\n" +
"\r\n" +
"content\r\n" +
".\r\n");
} catch (IOException e) {
throw Throwables.propagate(e);
}
}

private boolean messageHasBeenSent(SMTPClient smtpClient) throws IOException {
return smtpClient.getReplyString()
.contains("250 2.6.0 Message received");
}

private boolean userReceivedMessage(String user) {
IMAPClient imapClient = new IMAPClient();
try {
imapClient.connect(LOCALHOST_IP, IMAP_PORT);
imapClient.login(user, PASSWORD);
imapClient.select("INBOX");
imapClient.fetch("1:1", "ALL");
String replyString = imapClient.getReplyString();
return replyString.contains("OK FETCH completed");
} catch (IOException e) {
throw Throwables.propagate(e);
} finally {
try {
imapClient.close();
} catch (IOException e) {
throw Throwables.propagate(e);
}
try (SMTPMessageSender messageSender = SMTPMessageSender.noAuthentication(LOCALHOST_IP, SMTP_PORT, DEFAULT_DOMAIN);
IMAPMessageReader imapMessageReader = new IMAPMessageReader(LOCALHOST_IP, IMAP_PORT)) {
messageSender.sendMessage(from, recipient);
calmlyAwait.atMost(Duration.ONE_MINUTE).until(messageSender::messageHasBeenSent);
calmlyAwait.atMost(Duration.ONE_MINUTE).until(() -> imapMessageReader.userReceivedMessage(recipient, PASSWORD));
}
}
}
Expand Up @@ -53,7 +53,8 @@ public class TemporaryFilesystemModule extends AbstractModule {
"pop3server.xml",
"recipientrewritetable.xml",
"smtpserver.xml",
"usersrepository.xml");
"usersrepository.xml",
"smime_1.p12");

private final Supplier<File> workingDirectory;

Expand Down
Expand Up @@ -24,6 +24,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.Arrays;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.io.IOUtils;
Expand All @@ -34,6 +35,9 @@
import org.apache.james.utils.ExtendedServerProbe;
import org.junit.rules.TemporaryFolder;

import com.google.common.collect.ImmutableList;
import com.google.inject.Module;

public class TemporaryJamesServer {

private static final String MAILETCONTAINER_CONFIGURATION_FILENAME = "mailetcontainer.xml";
Expand All @@ -43,13 +47,16 @@ public class TemporaryJamesServer {
private final GuiceJamesServer jamesServer;


public TemporaryJamesServer(TemporaryFolder temporaryFolder, MailetContainer mailetContainer) throws Exception {
public TemporaryJamesServer(TemporaryFolder temporaryFolder, MailetContainer mailetContainer, Module... additionalModules) throws Exception {
appendMailetConfigurations(temporaryFolder, mailetContainer);

jamesServer = new GuiceJamesServer()
.combineWith(MemoryJamesServerMain.inMemoryServerModule)
.overrideWith(new TestJMAPServerModule(LIMIT_TO_3_MESSAGES),
new TemporaryFilesystemModule(temporaryFolder));
.overrideWith(ImmutableList.<Module>builder().addAll(Arrays.asList(additionalModules))
.add(new TestJMAPServerModule(LIMIT_TO_3_MESSAGES))
.add(new TemporaryFilesystemModule(temporaryFolder))
.build()
.toArray(new Module[additionalModules.length + 2]));

jamesServer.start();
}
Expand Down
@@ -0,0 +1,180 @@
/****************************************************************
* 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.mailets.crypto;

import static org.assertj.core.api.Assertions.assertThat;

import java.time.ZonedDateTime;

import org.apache.james.mailbox.model.MailboxConstants;
import org.apache.james.mailets.TemporaryJamesServer;
import org.apache.james.mailets.configuration.CommonProcessors;
import org.apache.james.mailets.configuration.MailetConfiguration;
import org.apache.james.mailets.configuration.MailetContainer;
import org.apache.james.mailets.configuration.ProcessorConfiguration;
import org.apache.james.mailets.utils.IMAPMessageReader;
import org.apache.james.mailets.utils.SMTPMessageSender;
import org.apache.james.util.date.ZonedDateTimeProvider;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.Duration;
import com.jayway.awaitility.core.ConditionFactory;

public class SMIMESignIntegrationTest {


private static final ZonedDateTime DATE_2015 = ZonedDateTime.parse("2015-10-15T14:10:00Z");
private static final String DEFAULT_DOMAIN = "domain";
private static final String LOCALHOST_IP = "127.0.0.1";
private static final int IMAP_PORT = 1143;
private static final int SMTP_PORT = 1025;
private static final int SMTP_SECURE_PORT = 10465;
private static final String PASSWORD = "secret";

@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();

private TemporaryJamesServer jamesServer;
private ConditionFactory calmlyAwait;
public static final String FROM = "user@" + DEFAULT_DOMAIN;
public static final String RECIPIENT = "user2@" + DEFAULT_DOMAIN;

@Before
public void setup() throws Exception {
temporaryFolder.newFile("smime.pk12");

MailetContainer mailetContainer = MailetContainer.builder()
.postmaster("postmaster@" + DEFAULT_DOMAIN)
.threads(5)
.addProcessor(CommonProcessors.root())
.addProcessor(CommonProcessors.error())
.addProcessor(ProcessorConfiguration.builder()
.state("transport")
.enableJmx(true)
.addMailet(MailetConfiguration.builder()
.match("SMTPAuthSuccessful")
.clazz("SetMimeHeader")
.addProperty("name", "X-UserIsAuth")
.addProperty("value", "true")
.build())
.addMailet(MailetConfiguration.builder()
.match("HasMailAttribute=org.apache.james.SMIMECheckSignature")
.clazz("SetMimeHeader")
.addProperty("name", "X-WasSigned")
.addProperty("value", "true")
.build())
.addMailet(MailetConfiguration.builder()
.match("All")
.clazz("RemoveMimeHeader")
.addProperty("name", "bcc")
.build())
.addMailet(MailetConfiguration.builder()
.match("All")
.clazz("RecipientRewriteTable")
.build())
.addMailet(MailetConfiguration.builder()
.match("RecipientIsLocal")
.clazz("org.apache.james.jmap.mailet.VacationMailet")
.build())
.addMailet(MailetConfiguration.builder()
.clazz("SMIMESign")
.match("SenderIsLocal")
.addProperty("keyStoreFileName", temporaryFolder.getRoot().getAbsoluteFile().getAbsolutePath() + "/conf/smime_1.p12")
.addProperty("keyStorePassword", "totototo")
.addProperty("keyStoreType", "PKCS12")
.addProperty("debug", "true")
.build())
.addMailet(MailetConfiguration.builder()
.match("RecipientIsLocal")
.clazz("LocalDelivery")
.build())
.addMailet(MailetConfiguration.builder()
.match("SMTPAuthSuccessful")
.clazz("RemoteDelivery")
.addProperty("outgoingQueue", "outgoing")
.addProperty("delayTime", "5000, 100000, 500000")
.addProperty("maxRetries", "25")
.addProperty("maxDnsProblemRetries", "0")
.addProperty("deliveryThreads", "10")
.addProperty("sendpartial", "true")
.addProperty("bounceProcessor", "bounces")
.build())
.addMailet(MailetConfiguration.builder()
.match("All")
.clazz("ToProcessor")
.addProperty("processor", "relay-denied")
.build())
.build())
.addProcessor(CommonProcessors.spam())
.addProcessor(CommonProcessors.localAddressError())
.addProcessor(CommonProcessors.relayDenied())
.addProcessor(CommonProcessors.bounces())
.addProcessor(CommonProcessors.sieveManagerCheck())
.build();

jamesServer = new TemporaryJamesServer(temporaryFolder, mailetContainer,
binder -> binder.bind(ZonedDateTimeProvider.class).toInstance(() -> DATE_2015));
Duration slowPacedPollInterval = Duration.FIVE_HUNDRED_MILLISECONDS;
calmlyAwait = Awaitility.with().pollInterval(slowPacedPollInterval).and().with().pollDelay(slowPacedPollInterval).await();

jamesServer.getServerProbe().addDomain(DEFAULT_DOMAIN);
jamesServer.getServerProbe().addUser(FROM, PASSWORD);
jamesServer.getServerProbe().addUser(RECIPIENT, PASSWORD);
jamesServer.getServerProbe().createMailbox(MailboxConstants.USER_NAMESPACE, RECIPIENT, "INBOX");
}

@After
public void tearDown() {
jamesServer.shutdown();
}

@Test
public void authenticatedMessagesShouldBeSigned() throws Exception {

try (SMTPMessageSender messageSender = SMTPMessageSender.authentication(LOCALHOST_IP, SMTP_SECURE_PORT, DEFAULT_DOMAIN, FROM, PASSWORD);
IMAPMessageReader imapMessageReader = new IMAPMessageReader(LOCALHOST_IP, IMAP_PORT)) {
messageSender.sendMessage(FROM, RECIPIENT);
calmlyAwait.atMost(Duration.ONE_MINUTE).until(messageSender::messageHasBeenSent);
calmlyAwait.atMost(Duration.ONE_MINUTE).until(() -> imapMessageReader.userReceivedMessage(RECIPIENT, PASSWORD));

assertThat(imapMessageReader.readFirstMessageInInbox(RECIPIENT, PASSWORD))
.containsSequence("Content-Description: S/MIME Cryptographic Signature");
}
}

@Test
public void NonAuthenticatedMessagesShouldNotBeSigned() throws Exception {
try (SMTPMessageSender messageSender = SMTPMessageSender.noAuthentication(LOCALHOST_IP, SMTP_PORT, DEFAULT_DOMAIN);
IMAPMessageReader imapMessageReader = new IMAPMessageReader(LOCALHOST_IP, IMAP_PORT)) {
messageSender.sendMessage(FROM, RECIPIENT);
calmlyAwait.atMost(Duration.ONE_MINUTE).until(messageSender::messageHasBeenSent);
calmlyAwait.atMost(Duration.ONE_MINUTE).until(() -> imapMessageReader.userReceivedMessage(RECIPIENT, PASSWORD));

assertThat(imapMessageReader.readFirstMessageInInbox(RECIPIENT, PASSWORD))
.doesNotContain("Content-Description: S/MIME Cryptographic Signature");
}
}

}
@@ -0,0 +1,55 @@
/****************************************************************
* 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.mailets.utils;

import java.io.Closeable;
import java.io.IOException;

import org.apache.commons.net.imap.IMAPClient;

public class IMAPMessageReader implements Closeable {

private final IMAPClient imapClient;

public IMAPMessageReader(String host, int port) throws IOException {
imapClient = new IMAPClient();
imapClient.connect(host, port);
}

public boolean userReceivedMessage(String user, String password) throws IOException {
imapClient.login(user, password);
imapClient.select("INBOX");
imapClient.fetch("1:1", "ALL");
return imapClient.getReplyString()
.contains("OK FETCH completed");
}

public String readFirstMessageInInbox(String user, String password) throws IOException {
imapClient.login(user, password);
imapClient.select("INBOX");
imapClient.fetch("1:1", "(BODY[])");
return imapClient.getReplyString();
}

@Override
public void close() throws IOException {
imapClient.close();
}
}

0 comments on commit 17c79db

Please sign in to comment.