KEYCLOAK-2283 added email truststore test
This commit is contained in:
parent
2d036b58a6
commit
b50513a946
15 changed files with 512 additions and 9 deletions
7
pom.xml
7
pom.xml
|
@ -99,6 +99,7 @@
|
|||
<picketlink.version>2.7.0.Final</picketlink.version>
|
||||
<selenium.version>2.35.0</selenium.version>
|
||||
<xml-apis.version>1.4.01</xml-apis.version>
|
||||
<subethasmtp.version>3.1.7</subethasmtp.version>
|
||||
|
||||
<!-- Maven Plugins -->
|
||||
<embedmongo.plugin.version>0.1.12</embedmongo.plugin.version>
|
||||
|
@ -419,6 +420,12 @@
|
|||
<version>${greenmail.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.subethamail</groupId>
|
||||
<artifactId>subethasmtp</artifactId>
|
||||
<version>${subethasmtp.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache DS -->
|
||||
<dependency>
|
||||
|
|
|
@ -277,6 +277,84 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</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>
|
||||
</build>
|
||||
</profile>
|
||||
|
|
|
@ -67,6 +67,11 @@
|
|||
<artifactId>junit</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.subethamail</groupId>
|
||||
<artifactId>subethasmtp</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.icegreen</groupId>
|
||||
<artifactId>greenmail</artifactId>
|
||||
|
|
|
@ -29,8 +29,16 @@ public class VerifyEmail extends Authenticate {
|
|||
@FindBy(xpath = "//div[@id='kc-form-wrapper']/p")
|
||||
private WebElement instruction;
|
||||
|
||||
@FindBy(id = "kc-error-message")
|
||||
private WebElement error;
|
||||
|
||||
public String getInstructionMessage() {
|
||||
waitUntilElement(instruction).is().present();
|
||||
return instruction.getText();
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
waitUntilElement(error).is().present();
|
||||
return error.getText();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,4 +25,6 @@ public class MailServerConfiguration {
|
|||
public static final String FROM = "server@mail.test";
|
||||
public static final String HOST = "localhost";
|
||||
public static final String PORT = "3025";
|
||||
public static final String PORT_SSL = "3465";
|
||||
public static final String STARTTLS = "true";
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -30,11 +30,15 @@ public class MailAssert {
|
|||
|
||||
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 {
|
||||
MimeMessage message = MailServer.getLastReceivedMessage();
|
||||
assertNotNull("There is no received email.", message);
|
||||
MimeMessage 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(from, message.getFrom()[0].toString());
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -117,10 +117,10 @@
|
|||
|
||||
"truststore": {
|
||||
"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}",
|
||||
"hostname-verification-policy": "${keycloak.truststore.policy:WILDCARD}",
|
||||
"disabled": "${keycloak.truststore.disabled:true}"
|
||||
"disabled": "${keycloak.truststore.disabled:false}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -736,6 +736,16 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</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 -->
|
||||
|
||||
|
|
Loading…
Reference in a new issue