Merge pull request #2934 from fkiss/master-truststore

KEYCLOAK-2283 added email truststore test
This commit is contained in:
Stian Thorgersen 2016-06-17 14:05:38 +02:00 committed by GitHub
commit 8f3cfed7c5
15 changed files with 512 additions and 9 deletions

View file

@ -102,6 +102,7 @@
<picketlink.version>2.7.0.Final</picketlink.version> <picketlink.version>2.7.0.Final</picketlink.version>
<selenium.version>2.35.0</selenium.version> <selenium.version>2.35.0</selenium.version>
<xml-apis.version>1.4.01</xml-apis.version> <xml-apis.version>1.4.01</xml-apis.version>
<subethasmtp.version>3.1.7</subethasmtp.version>
<!-- Maven Plugins --> <!-- Maven Plugins -->
<embedmongo.plugin.version>0.1.12</embedmongo.plugin.version> <embedmongo.plugin.version>0.1.12</embedmongo.plugin.version>
@ -432,6 +433,12 @@
<version>${greenmail.version}</version> <version>${greenmail.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.subethamail</groupId>
<artifactId>subethasmtp</artifactId>
<version>${subethasmtp.version}</version>
<scope>test</scope>
</dependency>
<!-- Apache DS --> <!-- Apache DS -->
<dependency> <dependency>

View file

@ -277,6 +277,84 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-keystore</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${auth.server.home}/standalone/configuration</outputDirectory>
<resources>
<resource>
<directory>${common.resources}/keystore</directory>
<includes>
<include>keycloak.jks</include>
<include>keycloak.truststore</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>inject-truststore-into-keycloak-server-json</id>
<phase>process-resources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<ant antfile="../build-truststore.xml" inheritRefs="true">
<target name="inject-truststore"/>
</ant>
</target>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>ant-contrib</groupId>
<artifactId>ant-contrib</artifactId>
<version>1.0b3</version>
<exclusions>
<exclusion>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-apache-bsf</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.apache.bsf</groupId>
<artifactId>bsf-api</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>rhino</groupId>
<artifactId>js</artifactId>
<version>1.7R2</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins> </plugins>
</build> </build>
</profile> </profile>

View file

@ -67,6 +67,11 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.subethamail</groupId>
<artifactId>subethasmtp</artifactId>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>com.icegreen</groupId> <groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId> <artifactId>greenmail</artifactId>

View file

@ -29,8 +29,16 @@ public class VerifyEmail extends Authenticate {
@FindBy(xpath = "//div[@id='kc-form-wrapper']/p") @FindBy(xpath = "//div[@id='kc-form-wrapper']/p")
private WebElement instruction; private WebElement instruction;
@FindBy(id = "kc-error-message")
private WebElement error;
public String getInstructionMessage() { public String getInstructionMessage() {
waitUntilElement(instruction).is().present(); waitUntilElement(instruction).is().present();
return instruction.getText(); return instruction.getText();
} }
public String getErrorMessage() {
waitUntilElement(error).is().present();
return error.getText();
}
} }

View file

@ -25,4 +25,6 @@ public class MailServerConfiguration {
public static final String FROM = "server@mail.test"; public static final String FROM = "server@mail.test";
public static final String HOST = "localhost"; public static final String HOST = "localhost";
public static final String PORT = "3025"; public static final String PORT = "3025";
public static final String PORT_SSL = "3465";
public static final String STARTTLS = "true";
} }

View file

@ -0,0 +1,86 @@
package org.keycloak.testsuite.util;
import org.subethamail.smtp.MessageContext;
import org.subethamail.smtp.MessageHandler;
import org.subethamail.smtp.MessageHandlerFactory;
import org.subethamail.smtp.RejectException;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import java.io.*;
import java.util.Properties;
public class MessageHandlerFactoryImpl implements MessageHandlerFactory {
MimeMessage message;
public MessageHandler create(MessageContext ctx) {
return new Handler(ctx);
}
class Handler implements MessageHandler {
MessageContext ctx;
public Handler(MessageContext ctx) {
this.ctx = ctx;
}
public void from(String from) throws RejectException {
System.out.println("FROM:" + from);
}
public void recipient(String recipient) throws RejectException {
System.out.println("RECIPIENT:" + recipient);
}
public void data(InputStream data) throws IOException {
String rawMail = this.convertStreamToString(data);
Session session = Session.getDefaultInstance(new Properties());
InputStream is = new ByteArrayInputStream(rawMail.getBytes());
try
{
message = new MimeMessage(session, is);
setMessage(message);
}
catch (MessagingException e)
{
e.printStackTrace();
}
}
public void done() {
System.out.println("Finished");
}
public String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}
public MimeMessage getMessage(){
return message;
}
public void setMessage(MimeMessage msg){
this.message = msg;
}
}

View file

@ -0,0 +1,37 @@
package org.keycloak.testsuite.util;
import org.jboss.logging.Logger;
import org.subethamail.smtp.MessageContext;
import org.subethamail.smtp.MessageHandler;
import java.io.InputStream;
public class MessageHandlerImpl implements MessageHandler {
MessageContext context;
private static final Logger log = Logger.getLogger(MessageHandlerImpl.class);
MessageHandlerImpl(MessageContext context) {
this.context = context;
}
@Override
public void from(String from) {
log.info("FROM: ${from}");
}
@Override
public void recipient(String recipient) {
log.info("RECIPIENT: ${recipient}");
}
@Override
public void data(InputStream data) {
log.info("DATA");
}
@Override
public void done() {
log.info("DONE");
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.testsuite.account;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.TestRealmKeycloakTest;
import org.keycloak.testsuite.auth.page.AuthRealm;
import org.keycloak.testsuite.auth.page.account.AccountManagement;
import org.keycloak.testsuite.auth.page.login.OIDCLogin;
import org.keycloak.testsuite.auth.page.login.VerifyEmail;
import org.keycloak.testsuite.util.*;
import static org.junit.Assert.assertEquals;
import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
/**
*
* @author fkiss
*/
public class TrustStoreEmailTest extends TestRealmKeycloakTest {
@Page
protected OIDCLogin testRealmLoginPage;
@Page
protected AuthRealm testRealmPage;
@Page
protected AccountManagement accountManagement;
@Page
private VerifyEmail testRealmVerifyEmailPage;
private UserRepresentation user;
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
log.info("enable verify email and configure smtp server to run with ssl in test realm");
user = findUserInRealmRep(testRealm, "test-user@localhost");
testRealm.setSmtpServer(SslMailServer.getServerConfiguration());
testRealm.setVerifyEmail(true);
}
@Override
public void setDefaultPageUriParameters() {
super.setDefaultPageUriParameters();
testRealmPage.setAuthRealm("test");
testRealmVerifyEmailPage.setAuthRealm(testRealmPage);
accountManagement.setAuthRealm(testRealmPage);
testRealmLoginPage.setAuthRealm(testRealmPage);
}
@After
public void afterTrustStoreEmailTest() {
SslMailServer.stop();
}
@Test
public void verifyEmailWithSslEnabled() {
SslMailServer.startWithSsl(this.getClass().getClassLoader().getResource(SslMailServer.PRIVATE_KEY).getFile());
accountManagement.navigateTo();
testRealmLoginPage.form().login(user.getUsername(), "password");
assertEquals("You need to verify your email address to activate your account.",
testRealmVerifyEmailPage.getFeedbackText());
String verifyEmailUrl = assertEmailAndGetUrl(MailServerConfiguration.FROM, user.getEmail(),
"Someone has created a Test account with this email address.", true);
log.info("navigating to url from email: " + verifyEmailUrl);
driver.navigate().to(verifyEmailUrl);
assertCurrentUrlStartsWith(accountManagement);
accountManagement.signOut();
testRealmLoginPage.form().login(user);
assertCurrentUrlStartsWith(accountManagement);
}
@Test
public void verifyEmailWithSslWrongCertificate() {
SslMailServer.startWithSsl(this.getClass().getClassLoader().getResource(SslMailServer.INVALID_KEY).getFile());
accountManagement.navigateTo();
loginPage.form().login(user);
assertEquals("Failed to send email, please try again later.\n" +
"« Back to Application",
testRealmVerifyEmailPage.getErrorMessage());
}
}

View file

@ -30,11 +30,15 @@ public class MailAssert {
private static final Logger log = Logger.getLogger(MailAssert.class); private static final Logger log = Logger.getLogger(MailAssert.class);
public static String assertEmailAndGetUrl(String from, String recipient, String content) { public static String assertEmailAndGetUrl(String from, String recipient, String content, Boolean sslEnabled) {
try { try {
MimeMessage message = MailServer.getLastReceivedMessage(); MimeMessage message;
assertNotNull("There is no received email.", message); if (sslEnabled){
message= SslMailServer.getLastReceivedMessage();
} else {
message = MailServer.getLastReceivedMessage();
} assertNotNull("There is no received email.", message);
assertEquals(recipient, message.getRecipients(RecipientType.TO)[0].toString()); assertEquals(recipient, message.getRecipients(RecipientType.TO)[0].toString());
assertEquals(from, message.getFrom()[0].toString()); assertEquals(from, message.getFrom()[0].toString());

View file

@ -0,0 +1,151 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed 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.keycloak.testsuite.util;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Map;
import javax.mail.internet.MimeMessage;
import javax.net.ssl.*;
import org.jboss.logging.Logger;
import org.subethamail.smtp.server.SMTPServer;
import static org.keycloak.testsuite.util.MailServerConfiguration.*;
import static org.keycloak.testsuite.util.MailServerConfiguration.PORT_SSL;
import static org.keycloak.testsuite.util.MailServerConfiguration.STARTTLS;
public class SslMailServer {
private static final Logger log = Logger.getLogger(MailServer.class);
public static final String PRIVATE_KEY = "keystore/keycloak.jks";
public static final String TRUSTED_CERTIFICATE = "keystore/keycloak.truststore";
//private key tested with invalid certificate
public static final String INVALID_KEY = "keystore/email_invalid.jks";
private static MessageHandlerFactoryImpl messageHandlerFactory = new MessageHandlerFactoryImpl();
private static SMTPServer smtpServer;
private static Map<String, String> serverConfiguration = new HashMap<>();
public static void start() {
smtpServer = new SMTPServer(messageHandlerFactory);
smtpServer.setHostName(HOST);
smtpServer.setPort(Integer.parseInt(PORT));
smtpServer.start();
log.info("Started mail server (" + smtpServer.getHostName() + ":" + smtpServer.getPort() + ")");
}
public static void stop() {
if (smtpServer != null) {
log.info("Stopping mail server (" + smtpServer.getHostName() + ":" + smtpServer.getPort() + ")");
// Suppress error from SubEthaSmtp on shutdown
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
if (!(e.getCause() instanceof SocketException && e.getStackTrace()[0].getClassName()
.equals("org.subethamail.smtp.server.Session"))) {
log.error("Exception in thread \"" + t.getName() + "\" ");
log.error(e.getMessage(), e);
}
}
});
smtpServer.stop();
}
}
public static void startWithSsl(String privateKey){
InputStream keyStoreIS = null;
try {
keyStoreIS = new FileInputStream(privateKey);
char[] keyStorePassphrase = "secret".toCharArray();
KeyStore ksKeys = null;
ksKeys = KeyStore.getInstance("JKS");
ksKeys.load(keyStoreIS, keyStorePassphrase);
// KeyManager decides which key material to use.
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ksKeys, keyStorePassphrase);
// Trust store for client authentication.
InputStream trustStoreIS = new FileInputStream(String.valueOf(MailServer.class.getClassLoader().getResource(TRUSTED_CERTIFICATE).getFile()));
char[] trustStorePassphrase = "secret".toCharArray();
KeyStore ksTrust = KeyStore.getInstance("JKS");
ksTrust.load(trustStoreIS, trustStorePassphrase);
// TrustManager decides which certificate authorities to use.
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ksTrust);
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
smtpServer = new SMTPServer(messageHandlerFactory) {
@Override
public SSLSocket createSSLSocket(Socket socket) throws IOException {
InetSocketAddress remoteAddress =
(InetSocketAddress) socket.getRemoteSocketAddress();
SSLSocketFactory sf = sslContext.getSocketFactory();
SSLSocket s = (SSLSocket) (sf.createSocket(
socket, remoteAddress.getHostName(), socket.getPort(), true));
// we are a server
s.setUseClientMode(false);
// select protocols and cipher suites
s.setEnabledProtocols(s.getSupportedProtocols());
s.setEnabledCipherSuites(s.getSupportedCipherSuites());
return s;
}
};
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | UnrecoverableKeyException | KeyManagementException | CertificateException e) {
throw new RuntimeException(e);
}
smtpServer.setHostName(HOST);
smtpServer.setPort(Integer.parseInt(PORT_SSL));
smtpServer.setEnableTLS(true);
smtpServer.start();
log.info("Started mail server (" + smtpServer.getHostName() + ":" + smtpServer.getPort() + ")");
}
public static Map<String, String> getServerConfiguration() {
serverConfiguration.put("from", FROM);
serverConfiguration.put("host", HOST);
serverConfiguration.put("port", PORT_SSL);
serverConfiguration.put("starttls", STARTTLS);
return serverConfiguration;
}
public static MimeMessage getLastReceivedMessage() throws InterruptedException {
return messageHandlerFactory.getMessage();
}
}

View file

@ -121,10 +121,10 @@
"truststore": { "truststore": {
"file": { "file": {
"file": "${keycloak.truststore.file:src/main/keystore/keycloak.truststore}", "file": "${keycloak.truststore.file:src/test/resources/keystore/keycloak.truststore}",
"password": "${keycloak.truststore.password:secret}", "password": "${keycloak.truststore.password:secret}",
"hostname-verification-policy": "${keycloak.truststore.policy:WILDCARD}", "hostname-verification-policy": "${keycloak.truststore.policy:WILDCARD}",
"disabled": "${keycloak.truststore.disabled:true}" "disabled": "${keycloak.truststore.disabled:false}"
} }
} }
} }

View file

@ -736,6 +736,16 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.subethamail</groupId>
<artifactId>subethasmtp</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Keycloak deps for tests --> <!-- Keycloak deps for tests -->