http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml new file mode 100644 index 0000000..258f264 --- /dev/null +++ b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailetcontainer.xml @@ -0,0 +1,103 @@ +<?xml version="1.0"?> + +<!-- + 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. + --> + +<mailetcontainer enableJmx="false"> + + <context> + <postmaster>postmas...@james.minet.net</postmaster> + </context> + + <spooler> + <threads>20</threads> + </spooler> + + <processors> + <processor state="root" enableJmx="false"> + <mailet match="All" class="PostmasterAlias"/> + <mailet match="RelayLimit=30" class="Null"/> + <!-- Hook on sievemana...@james.linagora.com + Mail send to this address will get interpreted with SIEVE Manage --> + <mailet match="All" class="ToProcessor"> + <processor>transport</processor> + </mailet> + </processor> + + <processor state="error" enableJmx="false"> + <mailet match="All" class="Bounce"/> + </processor> + + + <processor state="transport" enableJmx="false"> + <mailet match="SMTPAuthSuccessful" class="SetMimeHeader"> + <name>X-UserIsAuth</name> + <value>true</value> + </mailet> + <mailet match="All" class="RemoveMimeHeader"> + <name>bcc</name> + </mailet> + <mailet match="RecipientIsLocal" class="LocalDelivery"/> + <mailet match="HostIsLocal" class="ToProcessor"> + <processor>local-address-error</processor> + <notice>550 - Requested action not taken: no such user here</notice> + </mailet> + <mailet match="SMTPAuthSuccessful" class="RemoteDelivery"> + <outgoingQueue>outgoing</outgoingQueue> + <delayTime>5000, 100000, 500000</delayTime> + <maxRetries>25</maxRetries> + <maxDnsProblemRetries>0</maxDnsProblemRetries> + <deliveryThreads>10</deliveryThreads> + <sendpartial>true</sendpartial> + <bounceProcessor>bounces</bounceProcessor> + </mailet> + <mailet match="All" class="ToProcessor"> + <processor>relay-denied</processor> + </mailet> + </processor> + + <processor state="spam" enableJmx="false"> + <mailet match="All" class="ToRepository"> + <repositoryPath>file://var/mail/spam/</repositoryPath> + </mailet> + </processor> + + <processor state="local-address-error" enableJmx="false"> + <mailet match="All" class="Bounce"> + <attachment>none</attachment> + </mailet> + </processor> + + <processor state="relay-denied" enableJmx="false"> + <mailet match="All" class="Bounce"> + <attachment>none</attachment> + </mailet> + </processor> + + <processor state="bounces" enableJmx="false"> + <mailet match="All" class="DSNBounce"> + <passThrough>false</passThrough> + </mailet> + </processor> + + </processors> + +</mailetcontainer> + +
http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailrepositorystore.xml ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailrepositorystore.xml b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailrepositorystore.xml new file mode 100644 index 0000000..3ca4a1d --- /dev/null +++ b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/mailrepositorystore.xml @@ -0,0 +1,31 @@ +<?xml version="1.0"?> + +<!-- + 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. + --> + +<mailrepositorystore> + <mailrepositories> + <mailrepository class="org.apache.james.mailrepository.file.FileMailRepository"> + <protocols> + <protocol>file</protocol> + </protocols> + <config FIFO="false" CACHEKEYS="true"/> + </mailrepository> + </mailrepositories> +</mailrepositorystore> http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/pop3server.xml ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/pop3server.xml b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/pop3server.xml new file mode 100644 index 0000000..e4187da --- /dev/null +++ b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/pop3server.xml @@ -0,0 +1,42 @@ +<?xml version="1.0"?> +<!-- + 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. + --> + + +<pop3servers> + <pop3server enabled="true"> + <jmxName>pop3server</jmxName> + <bind>0.0.0.0:1110</bind> + <connectionBacklog>200</connectionBacklog> + <tls socketTLS="false" startTLS="false"> + <!-- To create a new keystore execute: + keytool -genkey -alias james -keyalg RSA -keystore /path/to/james/conf/keystore + --> + <keystore>file://conf/keystore</keystore> + <secret>james72laBalle</secret> + <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> + </tls> + <connectiontimeout>1200</connectiontimeout> + <connectionLimit>0</connectionLimit> + <connectionLimitPerIP>0</connectionLimitPerIP> + <handlerchain> + <handler class="org.apache.james.pop3server.core.CoreCmdHandlerLoader"/> + </handlerchain> + </pop3server> +</pop3servers> http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/smtpserver.xml ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/smtpserver.xml b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/smtpserver.xml new file mode 100644 index 0000000..a3d4b8f --- /dev/null +++ b/server/protocols/jmap-integration-testing/cassandra-jmap-integration-testing/src/test/resources/smtpserver.xml @@ -0,0 +1,105 @@ +<?xml version="1.0"?> + +<!-- + 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. + --> + +<smtpservers> + <smtpserver enabled="true"> + <jmxName>smtpserver-global</jmxName> + <bind>0.0.0.0:1025</bind> + <connectionBacklog>200</connectionBacklog> + <tls socketTLS="false" startTLS="false"> + <keystore>file://conf/keystore</keystore> + <secret>james72laBalle</secret> + <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> + <algorithm>SunX509</algorithm> + </tls> + <connectiontimeout>360</connectiontimeout> + <connectionLimit>0</connectionLimit> + <connectionLimitPerIP>0</connectionLimitPerIP> + <authRequired>false</authRequired> + <authorizedAddresses>0.0.0.0/0</authorizedAddresses> + <verifyIdentity>true</verifyIdentity> + <maxmessagesize>0</maxmessagesize> + <addressBracketsEnforcement>true</addressBracketsEnforcement> + <smtpGreeting>JAMES Linagora's SMTP awesome Server</smtpGreeting> + <handlerchain> + <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/> + <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> + </handlerchain> + </smtpserver> + <smtpserver enabled="true"> + <jmxName>smtpserver-TLS</jmxName> + <bind>0.0.0.0:10465</bind> + <connectionBacklog>200</connectionBacklog> + <tls socketTLS="false" startTLS="false"> + <keystore>file://conf/keystore</keystore> + <secret>james72laBalle</secret> + <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> + <algorithm>SunX509</algorithm> + </tls> + <connectiontimeout>360</connectiontimeout> + <connectionLimit>0</connectionLimit> + <connectionLimitPerIP>0</connectionLimitPerIP> + <!-- + Authorize only local users + --> + <authRequired>true</authRequired> + <authorizedAddresses>0.0.0.0/0</authorizedAddresses> + <!-- Trust authenticated users --> + <verifyIdentity>false</verifyIdentity> + <maxmessagesize>0</maxmessagesize> + <addressBracketsEnforcement>true</addressBracketsEnforcement> + <smtpGreeting>JAMES Linagora's SMTP awesome Server</smtpGreeting> + <handlerchain> + <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/> + <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> + </handlerchain> + </smtpserver> + <smtpserver enabled="true"> + <jmxName>smtpserver-authenticated</jmxName> + <bind>0.0.0.0:1587</bind> + <connectionBacklog>200</connectionBacklog> + <tls socketTLS="false" startTLS="false"> + <keystore>file://conf/keystore</keystore> + <secret>james72laBalle</secret> + <provider>org.bouncycastle.jce.provider.BouncyCastleProvider</provider> + <algorithm>SunX509</algorithm> + </tls> + <connectiontimeout>360</connectiontimeout> + <connectionLimit>0</connectionLimit> + <connectionLimitPerIP>0</connectionLimitPerIP> + <!-- + Authorize only local users + --> + <authRequired>true</authRequired> + <authorizedAddresses>0.0.0.0/0</authorizedAddresses> + <!-- Trust authenticated users --> + <verifyIdentity>false</verifyIdentity> + <maxmessagesize>0</maxmessagesize> + <addressBracketsEnforcement>true</addressBracketsEnforcement> + <smtpGreeting>JAMES Linagora's SMTP awesome Server</smtpGreeting> + <handlerchain> + <handler class="org.apache.james.smtpserver.fastfail.ValidRcptHandler"/> + <handler class="org.apache.james.smtpserver.CoreCmdHandlerLoader"/> + </handlerchain> + </smtpserver> +</smtpservers> + + http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml new file mode 100644 index 0000000..084c9e1 --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/pom.xml @@ -0,0 +1,241 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>james-server</artifactId> + <groupId>org.apache.james</groupId> + <version>3.0.0-beta5-SNAPSHOT</version> + <relativePath>../../../pom.xml</relativePath> + </parent> + + <artifactId>james-server-jmap-integration-testing</artifactId> + <packaging>jar</packaging> + + <name>Apache James :: Server :: JMAP :: Integration testing Common</name> + + <profiles> + <profile> + <id>noTest</id> + <activation> + <os> + <family>windows</family> + </os> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <skipTests>true</skipTests> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>disable-build-for-older-jdk</id> + <activation> + <jdk>(,1.8)</jdk> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <phase>none</phase> + </execution> + <execution> + <id>jar</id> + <phase>none</phase> + </execution> + <execution> + <id>test-jar</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>default-compile</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testCompile</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <executions> + <execution> + <id>default-test</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-source-plugin</artifactId> + <executions> + <execution> + <id>attach-sources</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-install-plugin</artifactId> + <executions> + <execution> + <id>default-install</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <executions> + <execution> + <id>default-resources</id> + <phase>none</phase> + </execution> + <execution> + <id>default-testResources</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-site-plugin</artifactId> + <executions> + <execution> + <id>attach-descriptor</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>build-for-jdk-8</id> + <activation> + <jdk>[1.8,)</jdk> + </activation> + <dependencies> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-guice-common</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.james</groupId> + <artifactId>james-server-guice-common</artifactId> + <scope>test</scope> + <type>test-jar</type> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>18.0</version> <!--$NO-MVN-MAN-VER$--> + </dependency> + <dependency> + <groupId>com.jayway.restassured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>${assertj-3.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>java-hamcrest</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.testcontainers</groupId> + <artifactId>testcontainers</artifactId> + <version>1.0.2</version> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <archive> + <manifest> + <mainClass>fully.qualified.MainClass</mainClass> + </manifest> + </archive> + <descriptorRefs> + <descriptorRef>jar-with-dependencies</descriptorRef> + </descriptorRefs> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>disable-animal-sniffer</id> + <activation> + <jdk>[1.6,)</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>animal-sniffer-maven-plugin</artifactId> + <executions> + <execution> + <id>check_java_6</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/ContainerTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/ContainerTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/ContainerTest.java new file mode 100644 index 0000000..d1c0bc4 --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/ContainerTest.java @@ -0,0 +1,45 @@ +/**************************************************************** + * 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.jmap; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.apache.http.client.fluent.Request; +import org.apache.http.client.fluent.Response; +import org.apache.http.client.utils.URIBuilder; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; + +public class ContainerTest { + + @Rule public GenericContainer container = new GenericContainer("nginx:1.7.1") + .withExposedPorts(80); + + @Test + public void containerShouldBeReachableOnExposedPort() throws IOException, URISyntaxException { + String containerIpAddress = container.getContainerIpAddress(); + Integer containerPort = container.getMappedPort(80); + Response response = Request.Get(new URIBuilder().setScheme("http").setHost(containerIpAddress).setPort(containerPort).build()).execute(); + assertThat(response.returnResponse().getStatusLine().getStatusCode()).isEqualTo(200); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/FixedDateZonedDateTimeProvider.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/FixedDateZonedDateTimeProvider.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/FixedDateZonedDateTimeProvider.java new file mode 100644 index 0000000..3dddcec --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/FixedDateZonedDateTimeProvider.java @@ -0,0 +1,39 @@ +/**************************************************************** + * 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.jmap; + +import java.time.ZonedDateTime; + +import org.apache.james.jmap.utils.ZonedDateTimeProvider; + +public class FixedDateZonedDateTimeProvider implements ZonedDateTimeProvider { + + private ZonedDateTime zonedDateTime; + + public void setFixedDateTime(ZonedDateTime zonedDateTime) { + this.zonedDateTime = zonedDateTime; + } + + @Override + public ZonedDateTime get() { + return zonedDateTime; + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java new file mode 100644 index 0000000..e7ad88b --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JMAPAuthenticationTest.java @@ -0,0 +1,474 @@ +/**************************************************************** + * 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.jmap; + +import static com.jayway.restassured.RestAssured.given; +import static com.jayway.restassured.RestAssured.with; +import static com.jayway.restassured.config.EncoderConfig.encoderConfig; +import static com.jayway.restassured.config.RestAssuredConfig.newConfig; + +import static org.hamcrest.Matchers.both; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.isA; +import static org.hamcrest.Matchers.notNullValue; + +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.UUID; + +import org.apache.james.GuiceJamesServer; +import org.apache.james.jmap.model.ContinuationToken; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.base.Charsets; +import com.jayway.restassured.RestAssured; +import com.jayway.restassured.http.ContentType; + +public abstract class JMAPAuthenticationTest { + + private static final ZonedDateTime oldDate = ZonedDateTime.parse("2011-12-03T10:15:30+01:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME); + private static final ZonedDateTime newDate = ZonedDateTime.parse("2011-12-03T10:16:30+01:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME); + private static final ZonedDateTime afterExpirationDate = ZonedDateTime.parse("2011-12-03T10:30:31+01:00", DateTimeFormatter.ISO_OFFSET_DATE_TIME); + + protected abstract GuiceJamesServer<?> createJmapServer(FixedDateZonedDateTimeProvider zonedDateTimeProvider); + + private UserCredentials userCredentials; + private FixedDateZonedDateTimeProvider zonedDateTimeProvider; + private GuiceJamesServer<?> jmapServer; + + @Before + public void setup() throws Throwable { + zonedDateTimeProvider = new FixedDateZonedDateTimeProvider(); + zonedDateTimeProvider.setFixedDateTime(oldDate); + jmapServer = createJmapServer(zonedDateTimeProvider); + jmapServer.start(); + RestAssured.port = jmapServer.getJmapPort(); + RestAssured.config = newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8)); + + + userCredentials = UserCredentials.builder() + .username("u...@domain.tld") + .password("password") + .build(); + + + String domain = "domain.tld"; + jmapServer.serverProbe().addDomain(domain); + jmapServer.serverProbe().addUser(userCredentials.getUsername(), userCredentials.getPassword()); + + } + + @After + public void teardown() { + jmapServer.stop(); + } + + @Test + public void mustReturnMalformedRequestWhenContentTypeIsMissing() { + given() + .accept(ContentType.JSON) + .when() + .post("/authentication") + .then() + .statusCode(400); + } + + @Test + public void mustReturnMalformedRequestWhenContentTypeIsNotJson() { + given() + .contentType(ContentType.XML) + .accept(ContentType.JSON) + .when() + .post("/authentication") + .then() + .statusCode(400); + } + + @Test + public void mustReturnMalformedRequestWhenAcceptIsMissing() { + given() + .contentType(ContentType.JSON) + .when() + .post("/authentication") + .then() + .statusCode(400); + } + + @Test + public void mustReturnMalformedRequestWhenAcceptIsNotJson() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.XML) + .when() + .post("/authentication") + .then() + .statusCode(400); + } + + @Test + public void mustReturnMalformedRequestWhenCharsetIsNotUTF8() { + given() + .contentType("application/json; charset=ISO-8859-1") + .accept(ContentType.JSON) + .when() + .post("/authentication") + .then() + .statusCode(400); + } + + @Test + public void mustReturnMalformedRequestWhenBodyIsEmpty() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .post("/authentication") + .then() + .statusCode(400); + } + + @Test + public void mustReturnMalformedRequestWhenBodyIsNotAcceptable() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"badAttributeName\": \"value\"}") + .when() + .post("/authentication") + .then() + .statusCode(400); + } + + @Test + public void mustReturnJsonResponse() throws Exception { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"username\": \"" + userCredentials.getUsername() + "\", \"clientName\": \"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe Bloggâs iPhone\"}") + .when() + .post("/authentication") + .then() + .statusCode(200) + .contentType(ContentType.JSON); + } + + @Test + public void methodShouldContainPasswordWhenValidResquest() throws Exception { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"username\": \"" + userCredentials.getUsername() + "\", \"clientName\": \"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe Bloggâs iPhone\"}") + .when() + .post("/authentication") + .then() + .statusCode(200) + .body("methods", hasItem(userCredentials.getPassword())); + } + + @Test + public void mustReturnContinuationTokenWhenValidResquest() throws Exception { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"username\": \"" + userCredentials.getUsername() + "\", \"clientName\": \"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe Bloggâs iPhone\"}") + .when() + .post("/authentication") + .then() + .statusCode(200) + .body("continuationToken", isA(String.class)); + } + + @Test + public void mustReturnAuthenticationFailedWhenBadPassword() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"token\": \"" + continuationToken + "\", \"method\": \"password\", \"password\": \"badpassword\"}") + .when() + .post("/authentication") + .then() + .statusCode(401); + } + + @Test + public void mustReturnAuthenticationFailedWhenContinuationTokenIsRejectedByTheContinuationTokenManager() throws Exception { + ContinuationToken badContinuationToken = new ContinuationToken(userCredentials.getUsername(), newDate, "badSignature"); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"token\": \"" + badContinuationToken.serialize() + "\", \"method\": \"password\", \"password\": \"" + userCredentials.getPassword() + "\"}") + .when() + .post("/authentication") + .then() + .statusCode(401); + } + + @Test + public void mustReturnRestartAuthenticationWhenContinuationTokenIsExpired() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + zonedDateTimeProvider.setFixedDateTime(afterExpirationDate); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"token\": \"" + continuationToken + "\", \"method\": \"password\", \"password\": \"" + userCredentials.getPassword() + "\"}") + .when() + .post("/authentication") + .then() + .statusCode(403); + } + + @Test + public void mustReturnAuthenticationFailedWhenUsersRepositoryException() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"token\": \"" + continuationToken + "\", \"method\": \"password\", \"password\": \"" + "wrong password" + "\"}") + .when() + .post("/authentication") + .then() + .statusCode(401); + } + + @Test + public void mustReturnCreatedWhenGoodPassword() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + zonedDateTimeProvider.setFixedDateTime(newDate); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"token\": \"" + continuationToken + "\", \"method\": \"password\", \"password\": \"" + userCredentials.getPassword() + "\"}") + .when() + .post("/authentication") + .then() + .statusCode(201); + } + + @Test + public void mustSendJsonContainingAccessTokenAndEndpointsWhenGoodPassword() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + zonedDateTimeProvider.setFixedDateTime(newDate); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"token\": \"" + continuationToken + "\", \"method\": \"password\", \"password\": \"" + userCredentials.getPassword() + "\"}") + .when() + .post("/authentication") + .then() + .body("accessToken", isA(String.class)) + .body("api", equalTo("/jmap")) + .body("eventSource", both(isA(String.class)).and(notNullValue())) + .body("upload", both(isA(String.class)).and(notNullValue())) + .body("download", both(isA(String.class)).and(notNullValue())); + } + + @Test + public void getMustReturnUnauthorizedWithoutAuthroizationHeader() throws Exception { + given() + .when() + .get("/authentication") + .then() + .statusCode(401); + } + + @Test + public void getMustReturnUnauthorizedWithoutAValidAuthroizationHeader() throws Exception { + given() + .header("Authorization", UUID.randomUUID()) + .when() + .get("/authentication") + .then() + .statusCode(401); + } + + @Test + public void getMustReturnEndpointsWhenValidAuthorizationHeader() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + String token = fromGoodAccessTokenRequest(continuationToken); + + given() + .header("Authorization", token) + .when() + .get("/authentication") + .then() + .statusCode(200) + .body("api", equalTo("/jmap")) + .body("eventSource", both(isA(String.class)).and(notNullValue())) + .body("upload", both(isA(String.class)).and(notNullValue())) + .body("download", both(isA(String.class)).and(notNullValue())); + } + + @Test + public void getMustReturnEndpointsWhenValidJwtAuthorizationHeader() throws Exception { + String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" + + "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZ" + + "DN_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf" + + "49t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2" + + "qNOR8Q31ydinyqzXvCSzVJOf6T60-w"; + + given() + .header("Authorization", "Bearer " + token) + .when() + .get("/authentication") + .then() + .statusCode(200); + } + + @Test + public void getMustReturnEndpointsWhenValidUnkwnonUserJwtAuthorizationHeader() throws Exception { + String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzM3IiwibmFtZSI6Ik5ldyBVc2VyIn0.ci8U04EOWKpi_y" + + "faKs8fnCBcu1mWs8fvf-t9SDP2kkvDDfD-ya4sEGn4ueCp2dA2ndefZfVu_IfvdlVtxqzSf0tQ-dFKrIe-OtSKhI2otjWctLtk9A" + + "G7jpWkXoDgr5IOVmsqg37Zxc2bgkLkC5FJqV6oCp51TNQTH6zZbXIUeuGFbHj2-iJeX8sACKTQB0llwc6TFm7GYUF03rv4DfJjqp" + + "Kd0g8RdnlevSOjV-gGzvKEItugtexS5pgOZ2GYcvqEUDb9EnQR7Qe2EzPAX_FCJfGhlv7bDQlTgOHHAjqw2lD4-zeAznw-3wlYLS" + + "zhi4ivvPjT-y2T5wnnhzeeYOpYOQ"; + + given() + .header("Authorization", "Bearer " + token) + .when() + .get("/authentication") + .then() + .statusCode(200); + } + + @Test + public void getMustReturnBadCredentialsWhenInvalidJwtAuthorizationHeader() throws Exception { + String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" + + "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZ" + + "EN_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf" + + "49t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2" + + "qNOR8Q31ydinyqzXvCSzVJOf6T60-w"; + + given() + .header("Authorization", "Bearer " + token) + .when() + .get("/authentication") + .then() + .statusCode(401); + } + + @Test + public void optionsRequestsShouldNeverRequireAuthentication() { + given() + .when() + .options("/authentication") + .then() + .statusCode(200); + } + + @Test + public void getMustReturnEndpointsWhenCorrectAuthentication() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + zonedDateTimeProvider.setFixedDateTime(newDate); + + String accessToken = fromGoodAccessTokenRequest(continuationToken); + + given() + .header("Authorization", accessToken) + .when() + .get("/authentication") + .then() + .statusCode(200) + .body("api", isA(String.class)); + } + + @Test + public void deleteMustReturnUnauthenticatedWithoutAuthorizationHeader() throws Exception { + given() + .when() + .delete("/authentication") + .then() + .statusCode(401); + } + + @Test + public void deleteMustReturnUnauthenticatedWithoutAValidAuthroizationHeader() throws Exception { + given() + .header("Authorization", UUID.randomUUID()) + .when() + .delete("/authentication") + .then() + .statusCode(401); + } + + @Test + public void deleteMustReturnOKNoContentOnValidAuthorizationToken() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + String token = fromGoodAccessTokenRequest(continuationToken); + given() + .header("Authorization", token) + .when() + .delete("/authentication") + .then() + .statusCode(204); + } + + @Test + public void deleteMustInvalidAuthorizationOnCorrectAuthorization() throws Exception { + String continuationToken = fromGoodContinuationTokenRequest(); + zonedDateTimeProvider.setFixedDateTime(newDate); + + String accessToken = fromGoodAccessTokenRequest(continuationToken); + + goodDeleteAccessTokenRequest(accessToken); + + given() + .header("Authorization", accessToken) + .when() + .get("/authentication") + .then() + .statusCode(401); + } + + private String fromGoodContinuationTokenRequest() { + return with() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"username\": \"" + userCredentials.getUsername() + "\", \"clientName\": \"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe Bloggâs iPhone\"}") + .post("/authentication") + .body() + .path("continuationToken") + .toString(); + } + + private String fromGoodAccessTokenRequest(String continuationToken) { + return with() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"token\": \"" + continuationToken + "\", \"method\": \"password\", \"password\": \"" + userCredentials.getPassword() + "\"}") + .post("/authentication") + .path("accessToken") + .toString(); + } + + private void goodDeleteAccessTokenRequest(String accessToken) { + with() + .header("Authorization", accessToken) + .delete("/authentication"); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapAuthentication.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapAuthentication.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapAuthentication.java new file mode 100644 index 0000000..b9fa503 --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/JmapAuthentication.java @@ -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.jmap; + +import static com.jayway.restassured.RestAssured.with; + +import org.apache.james.jmap.api.access.AccessToken; + +import com.jayway.restassured.http.ContentType; + +public class JmapAuthentication { + + public static AccessToken authenticateJamesUser(String username, String password) { + String continuationToken = getContinuationToken(username); + + return AccessToken.fromString( + with() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"token\": \"" + continuationToken + "\", \"method\": \"password\", \"password\": \"" + password + "\"}") + .post("/authentication") + .body() + .jsonPath() + .getString("accessToken") + ); + } + + private static String getContinuationToken(String username) { + return with() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"username\": \"" + username + "\", \"clientName\": \"Mozilla Thunderbird\", \"clientVersion\": \"42.0\", \"deviceName\": \"Joe Bloggâs iPhone\"}") + .post("/authentication") + .body() + .path("continuationToken") + .toString(); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/UserCredentials.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/UserCredentials.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/UserCredentials.java new file mode 100644 index 0000000..25a1994 --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/UserCredentials.java @@ -0,0 +1,66 @@ +/**************************************************************** + * 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.jmap; + +public class UserCredentials { + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private String username; + private String password; + + private Builder() { + } + + public Builder username(String username) { + this.username = username; + return this; + } + + public Builder password(String password) { + this.password = password; + return this; + } + + public UserCredentials build() { + return new UserCredentials(username, password); + } + } + + private final String username; + private final String password; + + private UserCredentials(String username, String password) { + this.username = username; + this.password = password; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/517a4cfe/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMailboxesMethodTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMailboxesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMailboxesMethodTest.java new file mode 100644 index 0000000..dfafe9f --- /dev/null +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMailboxesMethodTest.java @@ -0,0 +1,421 @@ +/**************************************************************** + * 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.jmap.methods.integration; + +import static com.jayway.restassured.RestAssured.given; +import static com.jayway.restassured.config.EncoderConfig.encoderConfig; +import static com.jayway.restassured.config.RestAssuredConfig.newConfig; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.isEmptyOrNullString; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import java.io.ByteArrayInputStream; +import java.util.Date; + +import javax.mail.Flags; + +import org.apache.james.GuiceJamesServer; +import org.apache.james.jmap.JmapAuthentication; +import org.apache.james.jmap.api.access.AccessToken; +import org.apache.james.mailbox.model.MailboxConstants; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.store.mail.model.Mailbox; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.common.base.Charsets; +import com.jayway.restassured.RestAssured; +import com.jayway.restassured.http.ContentType; + +public abstract class GetMailboxesMethodTest { + private static final String NAME = "[0][0]"; + private static final String ARGUMENTS = "[0][1]"; + + protected abstract GuiceJamesServer<?> createJmapServer(); + + private AccessToken accessToken; + private String username; + private GuiceJamesServer<?> jmapServer; + + @Before + public void setup() throws Throwable { + jmapServer = createJmapServer(); + jmapServer.start(); + RestAssured.port = jmapServer.getJmapPort(); + RestAssured.config = newConfig().encoderConfig(encoderConfig().defaultContentCharset(Charsets.UTF_8)); + + String domain = "domain.tld"; + username = "username@" + domain; + String password = "password"; + jmapServer.serverProbe().addDomain(domain); + jmapServer.serverProbe().addUser(username, password); + accessToken = JmapAuthentication.authenticateJamesUser(username, password); + } + + @After + public void teardown() { + jmapServer.stop(); + } + + @Test + public void getMailboxesShouldErrorNotSupportedWhenRequestContainsNonNullAccountId() throws Exception { + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"accountId\": \"1\"}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("error")) + .body(ARGUMENTS + ".type", equalTo("Not yet implemented")); + } + + @Test + public void getMailboxesShouldReturnEmptyWhenIdsDoesntMatch() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "name"); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": [\"notAMailboxId\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", hasSize(0)); + } + + @Test + public void getMailboxesShouldReturnMailboxesWhenIdsMatch() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "INBOX"); + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "myMailbox"); + + Mailbox<?> mailbox = jmapServer.serverProbe().getMailbox(MailboxConstants.USER_NAMESPACE, username, "INBOX"); + Mailbox<?> mailbox2 = jmapServer.serverProbe().getMailbox(MailboxConstants.USER_NAMESPACE, username, "myMailbox"); + + String mailboxId = mailbox.getMailboxId().serialize(); + String mailboxId2 = mailbox2.getMailboxId().serialize(); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\", \"" + mailboxId2 + "\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", hasSize(2)) + .body(ARGUMENTS + ".list[0].id", equalTo(mailboxId)) + .body(ARGUMENTS + ".list[1].id", equalTo(mailboxId2)); + } + + @Test + public void getMailboxesShouldReturnOnlyMatchingMailboxesWhenIdsGiven() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "INBOX"); + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "myMailbox"); + + Mailbox<?> mailbox = jmapServer.serverProbe().getMailbox(MailboxConstants.USER_NAMESPACE, username, "INBOX"); + + String mailboxId = mailbox.getMailboxId().serialize(); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": [\"" + mailboxId + "\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(ARGUMENTS + ".list[0].id", equalTo(mailboxId)); + } + + @Test + public void getMailboxesShouldReturnEmptyWhenIdsIsEmpty() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "INBOX"); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": []}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", empty()); + } + + @Test + public void getMailboxesShouldReturnAllMailboxesWhenIdsIsNull() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "INBOX"); + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "myMailbox"); + + Mailbox<?> mailbox = jmapServer.serverProbe().getMailbox(MailboxConstants.USER_NAMESPACE, username, "INBOX"); + Mailbox<?> mailbox2 = jmapServer.serverProbe().getMailbox(MailboxConstants.USER_NAMESPACE, username, "myMailbox"); + + String mailboxId = mailbox.getMailboxId().serialize(); + String mailboxId2 = mailbox2.getMailboxId().serialize(); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": null}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", hasSize(2)) + .body(ARGUMENTS + ".list[0].id", equalTo(mailboxId)) + .body(ARGUMENTS + ".list[1].id", equalTo(mailboxId2)); + } + + @Test + public void getMailboxesShouldErrorInvalidArgumentsWhenRequestIsInvalid() throws Exception { + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"ids\": true}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("error")) + .body(ARGUMENTS + ".type", equalTo("invalidArguments")) + .body(ARGUMENTS + ".description", equalTo("Can not deserialize instance of java.util.ArrayList out of VALUE_TRUE token\n" + + " at [Source: {\"ids\":true}; line: 1, column: 2] (through reference chain: org.apache.james.jmap.model.Builder[\"ids\"])")); + } + + @Test + public void getMailboxesShouldReturnEmptyListWhenNoMailboxes() throws Exception { + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", empty()); + } + + @Test + public void getMailboxesShouldReturnDefaultMailboxesWhenAuthenticatedUserDoesntHaveAnAccountYet() throws Exception { + + String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzM3QGRvbWFpbi50bGQiLCJuYW1lIjoiTmV3IFVzZXIif" + + "Q.fxNWNzksXyCij2ooVi-QfGe9vTicF2N9FDtWSJdjWTjhwoQ_i0dgiT8clp4dtOJzy78hB2UkAW-iq7z3PR_Gz0qFah7EbYoEs" + + "5lQs1UlhNGCRTvIsyR8qHUXtA6emw9x0nuMnswtyXhzoA-cEHCArrMxMeWhTYi2l4od3G8Irrvu1Yc5hKLwLgPdnImbKyB5a89T" + + "vzuZE8-FVyMmhlaJA2T1GpbsaUnfE1ki_bBzqMHTD_Ob7oSVzz2UOiOeL-ombn1X9GbYQ2I-Ob4V84WHONYxw0VjPHlj9saZ2n7" + + "2RJTBsIo6flJT-MchaEvTYBvuV_wlCCQYjI1g7mdeD6aXfw"; + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", "Bearer " + token) + .body("[[\"getMailboxes\", {}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", hasSize(3)) + .body(ARGUMENTS + ".list.name", hasItems("INBOX", "Outbox", "Sent")); + } + + @Test + public void getMailboxesShouldErrorWithBadJWTToken() { + + String badAuthToken = "BADTOKENOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" + + "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZ" + + "DN_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf" + + "49t1Tbv21ZqYM5Ht2vrhJWczFbuC-TD-8zJkXhjTmA1GVgomIX5dx1cH-dZX1wANNmshUJGHgepWlPU-5VIYxPEhb219RMLJIELMY2" + + "qNOR8Q31ydinyqzXvCSzVJOf6T60-w"; + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", "Bearer " + badAuthToken) + .body("[[\"getMailboxes\", {}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(401); + } + + @Test + public void getMailboxesShouldReturnMailboxesWhenAvailable() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "name"); + + jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "name"), + new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags()); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list[0].name", equalTo("name")) + .body(ARGUMENTS + ".list[0].parentId", nullValue()) + .body(ARGUMENTS + ".list[0].role", nullValue()) + .body(ARGUMENTS + ".list[0].sortOrder", equalTo(1000)) + .body(ARGUMENTS + ".list[0].mustBeOnlyMailbox", equalTo(false)) + .body(ARGUMENTS + ".list[0].mayReadItems", equalTo(false)) + .body(ARGUMENTS + ".list[0].mayAddItems", equalTo(false)) + .body(ARGUMENTS + ".list[0].mayRemoveItems", equalTo(false)) + .body(ARGUMENTS + ".list[0].mayCreateChild", equalTo(false)) + .body(ARGUMENTS + ".list[0].mayRename", equalTo(false)) + .body(ARGUMENTS + ".list[0].mayDelete", equalTo(false)) + .body(ARGUMENTS + ".list[0].totalMessages", equalTo(1)) + .body(ARGUMENTS + ".list[0].unreadMessages", equalTo(1)) + .body(ARGUMENTS + ".list[0].unreadThreads", equalTo(0)); + } + + @Test + public void getMailboxesShouldReturnFilteredMailboxesPropertiesWhenRequestContainsFilterProperties() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "name"); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"properties\" : [\"unreadMessages\", \"sortOrder\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list[0].id", not(isEmptyOrNullString())) + .body(ARGUMENTS + ".list[0].name", nullValue()) + .body(ARGUMENTS + ".list[0].parentId", nullValue()) + .body(ARGUMENTS + ".list[0].role", nullValue()) + .body(ARGUMENTS + ".list[0].sortOrder", equalTo(1000)) + .body(ARGUMENTS + ".list[0].mustBeOnlyMailbox", nullValue()) + .body(ARGUMENTS + ".list[0].mayReadItems", nullValue()) + .body(ARGUMENTS + ".list[0].mayAddItems", nullValue()) + .body(ARGUMENTS + ".list[0].mayRemoveItems", nullValue()) + .body(ARGUMENTS + ".list[0].mayCreateChild", nullValue()) + .body(ARGUMENTS + ".list[0].mayRename", nullValue()) + .body(ARGUMENTS + ".list[0].mayDelete", nullValue()) + .body(ARGUMENTS + ".list[0].totalMessages", nullValue()) + .body(ARGUMENTS + ".list[0].unreadMessages", equalTo(0)) + .body(ARGUMENTS + ".list[0].unreadThreads", nullValue()); + } + + @Test + public void getMailboxesShouldReturnIdWhenRequestContainsEmptyPropertyListFilter() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "name"); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"properties\" : []}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list[0].id", not(isEmptyOrNullString())) + .body(ARGUMENTS + ".list[0].name", nullValue()); + } + + @Test + public void getMailboxesShouldIgnoreUnknownPropertiesWhenRequestContainsUnknownPropertyListFilter() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "name"); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {\"properties\" : [\"unknown\"]}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list[0].id", not(isEmptyOrNullString())) + .body(ARGUMENTS + ".list[0].name", nullValue()); + } + + @Test + public void getMailboxesShouldReturnMailboxesWithSortOrder() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "inbox"); + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "trash"); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", hasSize(2)) + .body(ARGUMENTS + ".list[0].name", equalTo("inbox")) + .body(ARGUMENTS + ".list[0].sortOrder", equalTo(10)) + .body(ARGUMENTS + ".list[1].name", equalTo("trash")) + .body(ARGUMENTS + ".list[1].sortOrder", equalTo(60)); + } + + @Test + public void getMailboxesShouldReturnMailboxesWithRolesInLowerCase() throws Exception { + jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "outbox"); + + given() + .accept(ContentType.JSON) + .contentType(ContentType.JSON) + .header("Authorization", accessToken.serialize()) + .body("[[\"getMailboxes\", {}, \"#0\"]]") + .when() + .post("/jmap") + .then() + .statusCode(200) + .body(NAME, equalTo("mailboxes")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(ARGUMENTS + ".list[0].role", equalTo("outbox")); + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org