]> WPIA git - gigi.git/blob - tests/org/cacert/gigi/testUtils/ManagedTest.java
Implement short-circuit logic for emails locally in test env.
[gigi.git] / tests / org / cacert / gigi / testUtils / ManagedTest.java
1 package org.cacert.gigi.testUtils;
2
3 import static org.junit.Assert.*;
4
5 import java.io.BufferedReader;
6 import java.io.DataOutputStream;
7 import java.io.FileInputStream;
8 import java.io.IOException;
9 import java.io.InputStreamReader;
10 import java.io.OutputStream;
11 import java.io.UnsupportedEncodingException;
12 import java.net.HttpURLConnection;
13 import java.net.InetSocketAddress;
14 import java.net.MalformedURLException;
15 import java.net.Socket;
16 import java.net.URL;
17 import java.net.URLConnection;
18 import java.net.URLEncoder;
19 import java.nio.file.Files;
20 import java.nio.file.Paths;
21 import java.security.KeyManagementException;
22 import java.security.NoSuchAlgorithmException;
23 import java.security.Principal;
24 import java.security.PrivateKey;
25 import java.security.cert.X509Certificate;
26 import java.sql.PreparedStatement;
27 import java.sql.ResultSet;
28 import java.sql.SQLException;
29 import java.util.Properties;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32
33 import javax.net.ssl.HttpsURLConnection;
34 import javax.net.ssl.KeyManager;
35 import javax.net.ssl.SSLContext;
36 import javax.net.ssl.X509KeyManager;
37
38 import org.cacert.gigi.DevelLauncher;
39 import org.cacert.gigi.database.DatabaseConnection;
40 import org.cacert.gigi.testUtils.TestEmailReciever.TestMail;
41 import org.cacert.gigi.util.DatabaseManager;
42 import org.cacert.gigi.util.ServerConstants;
43 import org.cacert.gigi.util.SimpleSigner;
44 import org.junit.After;
45 import org.junit.AfterClass;
46 import org.junit.BeforeClass;
47
48 public class ManagedTest {
49         /**
50          * Some password that fullfills the password criteria.
51          */
52         protected static final String TEST_PASSWORD = "xvXV12°§";
53
54         private final String registerService = "/register";
55
56         private static TestEmailReciever ter;
57         private static Process gigi;
58         private static String url = "localhost:4443";
59
60         public static String getServerName() {
61                 return url;
62         }
63
64         static Properties testProps = new Properties();
65         static {
66                 InitTruststore.run();
67                 HttpURLConnection.setFollowRedirects(false);
68         }
69
70         @BeforeClass
71         public static void connectToServer() {
72                 try {
73                         testProps.load(new FileInputStream("config/test.properties"));
74                         if (!DatabaseConnection.isInited()) {
75                                 DatabaseConnection.init(testProps);
76                         }
77                         System.out.println("... purging Database");
78                         DatabaseManager.run(new String[] { testProps.getProperty("sql.driver"), testProps.getProperty("sql.url"),
79                                         testProps.getProperty("sql.user"), testProps.getProperty("sql.password") });
80                         String type = testProps.getProperty("type");
81                         Properties mainProps = generateMainProps();
82                         ServerConstants.init(mainProps);
83                         if (type.equals("local")) {
84                                 url = testProps.getProperty("name.www") + ":" + testProps.getProperty("serverPort");
85                                 String[] parts = testProps.getProperty("mail").split(":", 2);
86                                 ter = new TestEmailReciever(new InetSocketAddress(parts[0], Integer.parseInt(parts[1])));
87                                 return;
88                         }
89                         url = testProps.getProperty("name.www") + ":" + testProps.getProperty("serverPort");
90                         gigi = Runtime.getRuntime().exec(testProps.getProperty("java"));
91                         DataOutputStream toGigi = new DataOutputStream(gigi.getOutputStream());
92                         System.out.println("... starting server");
93
94                         byte[] cacerts = Files.readAllBytes(Paths.get("config/cacerts.jks"));
95                         byte[] keystore = Files.readAllBytes(Paths.get("config/keystore.pkcs12"));
96
97                         DevelLauncher.writeGigiConfig(toGigi, "changeit".getBytes(), "changeit".getBytes(), mainProps, cacerts,
98                                 keystore);
99                         toGigi.flush();
100
101                         final BufferedReader br = new BufferedReader(new InputStreamReader(gigi.getErrorStream()));
102                         String line;
103                         while ((line = br.readLine()) != null && !line.contains("Server:main: Started")) {
104                         }
105                         new Thread() {
106                                 @Override
107                                 public void run() {
108                                         String line;
109                                         try {
110                                                 while ((line = br.readLine()) != null) {
111                                                         System.err.println(line);
112                                                 }
113                                         } catch (IOException e) {
114                                                 e.printStackTrace();
115                                         }
116                                 }
117                         }.start();
118                         if (line == null) {
119                                 throw new Error("Server startup failed");
120                         }
121                         ter = new TestEmailReciever(new InetSocketAddress("localhost", 8473));
122                         SimpleSigner.runSigner();
123                 } catch (IOException e) {
124                         throw new Error(e);
125                 } catch (ClassNotFoundException e1) {
126                         e1.printStackTrace();
127                 } catch (SQLException e1) {
128                         e1.printStackTrace();
129                 } catch (InterruptedException e) {
130                         e.printStackTrace();
131                 }
132
133         }
134
135         private static Properties generateMainProps() {
136                 Properties mainProps = new Properties();
137                 mainProps.setProperty("host", "127.0.0.1");
138                 mainProps.setProperty("name.secure", testProps.getProperty("name.secure"));
139                 mainProps.setProperty("name.www", testProps.getProperty("name.www"));
140                 mainProps.setProperty("name.static", testProps.getProperty("name.static"));
141
142                 mainProps.setProperty("port", testProps.getProperty("serverPort"));
143                 mainProps.setProperty("emailProvider", "org.cacert.gigi.email.TestEmailProvider");
144                 mainProps.setProperty("emailProvider.port", "8473");
145                 mainProps.setProperty("sql.driver", testProps.getProperty("sql.driver"));
146                 mainProps.setProperty("sql.url", testProps.getProperty("sql.url"));
147                 mainProps.setProperty("sql.user", testProps.getProperty("sql.user"));
148                 mainProps.setProperty("sql.password", testProps.getProperty("sql.password"));
149                 return mainProps;
150         }
151
152         @AfterClass
153         public static void tearDownServer() {
154                 String type = testProps.getProperty("type");
155                 ter.destroy();
156                 if (type.equals("local")) {
157                         return;
158                 }
159                 gigi.destroy();
160                 try {
161                         SimpleSigner.stopSigner();
162                 } catch (InterruptedException e) {
163                         e.printStackTrace();
164                 }
165         }
166
167         @After
168         public void removeMails() {
169                 ter.reset();
170         }
171
172         public TestMail waitForMail() {
173                 try {
174                         return ter.recieve();
175                 } catch (InterruptedException e) {
176                         throw new Error(e);
177                 }
178         }
179
180         public static TestEmailReciever getMailReciever() {
181                 return ter;
182         }
183
184         public String runRegister(String param) throws IOException {
185                 URL regist = new URL("https://" + getServerName() + registerService);
186                 HttpURLConnection uc = (HttpURLConnection) regist.openConnection();
187                 HttpURLConnection csrfConn = (HttpURLConnection) regist.openConnection();
188
189                 String headerField = csrfConn.getHeaderField("Set-Cookie");
190                 headerField = stripCookie(headerField);
191
192                 String csrf = getCSRF(csrfConn);
193                 uc.addRequestProperty("Cookie", headerField);
194                 uc.setDoOutput(true);
195                 uc.getOutputStream().write((param + "&csrf=" + csrf).getBytes());
196                 String d = IOUtils.readURL(uc);
197                 return d;
198         }
199
200         public String fetchStartErrorMessage(String d) throws IOException {
201                 String formFail = "<div class='formError'>";
202                 int idx = d.indexOf(formFail);
203                 if (idx == -1) {
204                         return null;
205                 }
206                 String startError = d.substring(idx + formFail.length(), idx + 100).trim();
207                 return startError;
208         }
209
210         public void registerUser(String firstName, String lastName, String email, String password) {
211                 try {
212                         String query = "fname=" + URLEncoder.encode(firstName, "UTF-8") + "&lname="
213                                 + URLEncoder.encode(lastName, "UTF-8") + "&email=" + URLEncoder.encode(email, "UTF-8") + "&pword1="
214                                 + URLEncoder.encode(password, "UTF-8") + "&pword2=" + URLEncoder.encode(password, "UTF-8")
215                                 + "&day=1&month=1&year=1910&cca_agree=1";
216                         String data = fetchStartErrorMessage(runRegister(query));
217                         assertTrue(data, data.startsWith("</div>"));
218                 } catch (UnsupportedEncodingException e) {
219                         throw new Error(e);
220                 } catch (IOException e) {
221                         throw new Error(e);
222                 }
223         }
224
225         public int createVerifiedUser(String firstName, String lastName, String email, String password) {
226                 registerUser(firstName, lastName, email, password);
227                 try {
228                         TestMail tm = ter.recieve();
229                         String verifyLink = tm.extractLink();
230                         String[] parts = verifyLink.split("\\?");
231                         URL u = new URL("https://" + getServerName() + "/verify?" + parts[1]);
232                         u.openStream().close();
233                         ;
234                         PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id FROM users where email=?");
235                         ps.setString(1, email);
236                         ResultSet rs = ps.executeQuery();
237                         if (rs.next()) {
238                                 return rs.getInt(1);
239                         }
240                         throw new Error();
241                 } catch (InterruptedException e) {
242                         throw new Error(e);
243                 } catch (IOException e) {
244                         throw new Error(e);
245                 } catch (SQLException e) {
246                         throw new Error(e);
247                 }
248         }
249
250         /**
251          * Creates a new user with 100 Assurance points given by an (invalid)
252          * assurance.
253          * 
254          * @param firstName
255          *            the first name
256          * @param lastName
257          *            the last name
258          * @param email
259          *            the email
260          * @param password
261          *            the password
262          * @return a new userid.
263          */
264         public int createAssuranceUser(String firstName, String lastName, String email, String password) {
265                 int uid = createVerifiedUser(firstName, lastName, email, password);
266                 try {
267                         PreparedStatement ps = DatabaseConnection.getInstance().prepare(
268                                 "INSERT INTO `cats_passed` SET `user_id`=?, `variant_id`=?");
269                         ps.setInt(1, uid);
270                         ps.setInt(2, 0);
271                         ps.execute();
272                         ps = DatabaseConnection.getInstance().prepare("INSERT INTO `notary` SET `from`=?, `to`=?, points='100'");
273                         ps.setInt(1, uid);
274                         ps.setInt(2, uid);
275                         ps.execute();
276
277                 } catch (SQLException e) {
278                         throw new Error(e);
279                 }
280                 return uid;
281         }
282
283         static int count = 0;
284
285         public String createUniqueName() {
286                 return "test" + System.currentTimeMillis() + "a" + (count++);
287         }
288
289         private String stripCookie(String headerField) {
290                 return headerField.substring(0, headerField.indexOf(';'));
291         }
292
293         public static final String SECURE_REFERENCE = "/account/certs/email";
294
295         public boolean isLoggedin(String cookie) throws IOException {
296                 URL u = new URL("https://" + getServerName() + SECURE_REFERENCE);
297                 HttpURLConnection huc = (HttpURLConnection) u.openConnection();
298                 huc.addRequestProperty("Cookie", cookie);
299                 return huc.getResponseCode() == 200;
300         }
301
302         public String login(String email, String pw) throws IOException {
303                 URL u = new URL("https://" + getServerName() + "/login");
304                 HttpURLConnection huc = (HttpURLConnection) u.openConnection();
305                 huc.setDoOutput(true);
306                 OutputStream os = huc.getOutputStream();
307                 String data = "username=" + URLEncoder.encode(email, "UTF-8") + "&password=" + URLEncoder.encode(pw, "UTF-8");
308                 os.write(data.getBytes());
309                 os.flush();
310                 String headerField = huc.getHeaderField("Set-Cookie");
311                 return stripCookie(headerField);
312         }
313
314         public String login(final PrivateKey pk, final X509Certificate ce) throws NoSuchAlgorithmException,
315                 KeyManagementException, IOException, MalformedURLException {
316
317                 HttpURLConnection connection = (HttpURLConnection) new URL("https://"
318                         + getServerName().replaceFirst("^www.", "secure.") + "/login").openConnection();
319                 authenticateClientCert(pk, ce, connection);
320                 if (connection.getResponseCode() == 302) {
321                         assertEquals("https://" + getServerName().replaceFirst("^www.", "secure.").replaceFirst(":443$", "") + "/",
322                                 connection.getHeaderField("Location").replaceFirst(":443$", ""));
323                         return stripCookie(connection.getHeaderField("Set-Cookie"));
324                 } else {
325                         return null;
326                 }
327         }
328
329         public void authenticateClientCert(final PrivateKey pk, final X509Certificate ce, HttpURLConnection connection)
330                 throws NoSuchAlgorithmException, KeyManagementException {
331                 KeyManager km = new X509KeyManager() {
332
333                         @Override
334                         public String chooseClientAlias(String[] arg0, Principal[] arg1, Socket arg2) {
335                                 return "client";
336                         }
337
338                         @Override
339                         public String chooseServerAlias(String arg0, Principal[] arg1, Socket arg2) {
340                                 return null;
341                         }
342
343                         @Override
344                         public X509Certificate[] getCertificateChain(String arg0) {
345                                 return new X509Certificate[] { ce };
346                         }
347
348                         @Override
349                         public String[] getClientAliases(String arg0, Principal[] arg1) {
350                                 return new String[] { "client" };
351                         }
352
353                         @Override
354                         public PrivateKey getPrivateKey(String arg0) {
355                                 if (arg0.equals("client")) {
356                                         return pk;
357                                 }
358                                 return null;
359                         }
360
361                         @Override
362                         public String[] getServerAliases(String arg0, Principal[] arg1) {
363                                 return new String[] { "client" };
364                         }
365                 };
366                 SSLContext sc = SSLContext.getInstance("TLS");
367                 sc.init(new KeyManager[] { km }, null, null);
368                 if (connection instanceof HttpsURLConnection) {
369                         ((HttpsURLConnection) connection).setSSLSocketFactory(sc.getSocketFactory());
370                 }
371         }
372
373         public String getCSRF(URLConnection u) throws IOException {
374                 String content = IOUtils.readURL(u);
375                 Pattern p = Pattern.compile("<input type='hidden' name='csrf' value='([^']+)'>");
376                 Matcher m = p.matcher(content);
377                 if (!m.find()) {
378                         throw new Error("No CSRF Token");
379                 }
380                 return m.group(1);
381         }
382
383         public static String[] generateCSR(String dn) throws IOException {
384                 Process p = Runtime.getRuntime().exec(
385                         new String[] { "openssl", "req", "-newkey", "rsa:1024", "-nodes", "-subj", dn, "-config",
386                                         "keys/selfsign.config" });
387                 String csr = IOUtils.readURL(new InputStreamReader(p.getInputStream()));
388
389                 String[] parts = csr.split("(?<=-----)\n(?=-----)");
390                 if (parts.length != 2) {
391                         System.err.println(IOUtils.readURL(new InputStreamReader(p.getErrorStream())));
392                         throw new Error();
393                 }
394                 return parts;
395         }
396
397         public String executeBasicWebInteraction(String cookie, String path, String query) throws IOException,
398                 MalformedURLException, UnsupportedEncodingException {
399                 URLConnection uc = new URL("https://" + getServerName() + path).openConnection();
400                 uc.addRequestProperty("Cookie", cookie);
401                 String csrf = getCSRF(uc);
402
403                 uc = new URL("https://" + getServerName() + path).openConnection();
404                 uc.addRequestProperty("Cookie", cookie);
405                 uc.setDoOutput(true);
406                 OutputStream os = uc.getOutputStream();
407                 os.write(("csrf=" + URLEncoder.encode(csrf, "UTF-8") + "&" //
408                 + query//
409                 ).getBytes());
410                 os.flush();
411                 String error = fetchStartErrorMessage(IOUtils.readURL(uc));
412                 return error;
413         }
414
415 }