]> WPIA git - gigi.git/blob - tests/club/wpia/gigi/testUtils/ConfiguredTest.java
16abb06cf2623c36de562da1dbbba8b354e51419
[gigi.git] / tests / club / wpia / gigi / testUtils / ConfiguredTest.java
1 package club.wpia.gigi.testUtils;
2
3 import static org.junit.Assert.*;
4
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.ObjectInputStream;
10 import java.io.ObjectOutputStream;
11 import java.math.BigInteger;
12 import java.security.GeneralSecurityException;
13 import java.security.KeyFactory;
14 import java.security.KeyPair;
15 import java.security.KeyPairGenerator;
16 import java.security.PrivateKey;
17 import java.security.PublicKey;
18 import java.security.SecureRandom;
19 import java.security.Signature;
20 import java.security.spec.RSAPrivateKeySpec;
21 import java.security.spec.RSAPublicKeySpec;
22 import java.sql.SQLException;
23 import java.text.SimpleDateFormat;
24 import java.util.Calendar;
25 import java.util.Date;
26 import java.util.Properties;
27 import java.util.Random;
28 import java.util.TimeZone;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31
32 import org.junit.AfterClass;
33 import org.junit.BeforeClass;
34
35 import club.wpia.gigi.GigiApiException;
36 import club.wpia.gigi.database.DatabaseConnection;
37 import club.wpia.gigi.database.DatabaseConnection.Link;
38 import club.wpia.gigi.database.GigiPreparedStatement;
39 import club.wpia.gigi.database.SQLFileManager.ImportType;
40 import club.wpia.gigi.dbObjects.CATS.CATSType;
41 import club.wpia.gigi.dbObjects.CertificateProfile;
42 import club.wpia.gigi.dbObjects.Domain;
43 import club.wpia.gigi.dbObjects.DomainPingType;
44 import club.wpia.gigi.dbObjects.User;
45 import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail;
46 import club.wpia.gigi.util.DatabaseManager;
47 import club.wpia.gigi.util.DomainAssessment;
48 import club.wpia.gigi.util.Notary;
49 import club.wpia.gigi.util.PEM;
50 import club.wpia.gigi.util.PasswordHash;
51 import club.wpia.gigi.util.ServerConstants;
52 import club.wpia.gigi.util.TimeConditions;
53 import sun.security.pkcs10.PKCS10;
54 import sun.security.pkcs10.PKCS10Attributes;
55 import sun.security.x509.X500Name;
56
57 /**
58  * Base class for a Testsuite that makes use of the config variables that define
59  * the environment.
60  */
61 public abstract class ConfiguredTest {
62
63     static Properties testProps = new Properties();
64
65     public static Properties getTestProps() {
66         return testProps;
67     }
68
69     private static boolean envInited = false;
70
71     /**
72      * Some password that fulfills the password criteria.
73      */
74     public static final String TEST_PASSWORD = "xvXV12°§";
75
76     public static final String DIFFICULT_CHARS = "ÜÖÄß𐀀";
77
78     @BeforeClass
79     public static void initEnvironmentHook() throws IOException {
80         initEnvironment();
81     }
82
83     private static Link l;
84
85     public static Properties initEnvironment() throws IOException {
86         TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
87         if (envInited) {
88             try {
89                 synchronized (ConfiguredTest.class) {
90                     if (l == null) {
91                         l = DatabaseConnection.newLink(false);
92                     }
93                 }
94             } catch (InterruptedException e) {
95                 throw new Error(e);
96             }
97             return generateProps();
98         }
99         envInited = true;
100         try (FileInputStream inStream = new FileInputStream("config/test.properties")) {
101             testProps.load(inStream);
102         }
103         Properties props = generateProps();
104         ServerConstants.init(props);
105         TimeConditions.init(props);
106         DomainAssessment.init(props);
107         PasswordHash.init(props);
108
109         if ( !DatabaseConnection.isInited()) {
110             DatabaseConnection.init(testProps);
111             try {
112                 synchronized (ConfiguredTest.class) {
113                     if (l == null) {
114                         l = DatabaseConnection.newLink(false);
115                     }
116                 }
117             } catch (InterruptedException e) {
118                 throw new Error(e);
119             }
120         }
121
122         return props;
123     }
124
125     @AfterClass
126     public static void closeDBLink() {
127         synchronized (ConfiguredTest.class) {
128             if (l != null) {
129                 l.close();
130                 l = null;
131             }
132         }
133     }
134
135     private static Properties generateProps() throws Error {
136         Properties mainProps = new Properties();
137         mainProps.setProperty("name.secure", testProps.getProperty("name.secure"));
138         mainProps.setProperty("name.www", testProps.getProperty("name.www"));
139         mainProps.setProperty("name.static", testProps.getProperty("name.static"));
140         mainProps.setProperty("name.api", testProps.getProperty("name.api"));
141         mainProps.setProperty("name.suffix", testProps.getProperty("name.suffix"));
142
143         mainProps.setProperty("appName", "SomeCA");
144         mainProps.setProperty("appIdentifier", "someca");
145
146         mainProps.setProperty("https.port", testProps.getProperty("serverPort.https"));
147         mainProps.setProperty("http.port", testProps.getProperty("serverPort.http"));
148
149         File out = new File("financial.dat");
150         if ( !out.exists()) {
151             try (FileOutputStream fos = new FileOutputStream(out)) {
152                 fos.write("google.com\ntwitter.com\n".getBytes("UTF-8"));
153             } catch (IOException e) {
154                 throw new Error(e);
155             }
156         }
157         mainProps.setProperty("highFinancialValue", out.getAbsolutePath());
158         mainProps.setProperty("scrypt.params", "1;1;1");
159         return mainProps;
160     }
161
162     public static KeyPair generateKeypair() throws GeneralSecurityException {
163         KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
164         kpg.initialize(4096);
165         KeyPair keyPair = null;
166         File f = new File("testKeypair");
167         if (f.exists()) {
168             try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
169                 keyPair = (KeyPair) ois.readObject();
170             } catch (ClassNotFoundException e) {
171                 e.printStackTrace();
172             } catch (IOException e) {
173                 e.printStackTrace();
174             }
175         } else {
176             keyPair = kpg.generateKeyPair();
177             try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f))) {
178                 oos.writeObject(keyPair);
179                 oos.close();
180             } catch (IOException e) {
181                 e.printStackTrace();
182             }
183         }
184         return keyPair;
185     }
186
187     public static KeyPair generateBrokenKeypair() throws GeneralSecurityException {
188         KeyPair keyPair = null;
189         File f = new File("testBrokenKeypair");
190         if (f.exists()) {
191             try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
192                 keyPair = (KeyPair) ois.readObject();
193             } catch (ClassNotFoundException e) {
194                 e.printStackTrace();
195             } catch (IOException e) {
196                 e.printStackTrace();
197             }
198         } else {
199             // -----BEGIN SHAMELESSLY ADAPTED BLOCK-----
200             /**
201              * Modified original RSA key generator to use three primes with one
202              * prime set to fixed value to allow simple checking for such faulty
203              * keys.
204              *
205              * @link sun.security.rsa.RSAKeyPairGenerator#generateKeyPair
206              */
207
208             KeyFactory factory = KeyFactory.getInstance("RSA");
209             Random random = new SecureRandom();
210             int keySize = 4096;
211             long r_lv = 7331;
212
213             int lp = (keySize + 1) >> 1;
214             int lr = BigInteger.valueOf(r_lv).bitLength();
215             int lq = keySize - lp - lr;
216
217             BigInteger e = BigInteger.valueOf(7331);
218
219             keyPair = null;
220             while (keyPair == null) {
221                 // generate two random primes of size lp/lq
222                 BigInteger p, q, r, n;
223
224                 p = BigInteger.probablePrime(lp, random);
225                 r = BigInteger.valueOf(r_lv);
226                 do {
227                     q = BigInteger.probablePrime(lq, random);
228
229                     // convention is for p > q > r
230                     if (p.compareTo(q) < 0) {
231                         BigInteger tmp = p;
232                         p = q;
233                         q = tmp;
234                     }
235
236                     // modulus n = p * q * r
237                     n = p.multiply(q).multiply(r);
238
239                     // even with correctly sized p, q and r, there is a chance
240                     // that n will be one bit short. re-generate the smaller
241                     // prime if so.
242                 } while (n.bitLength() < keySize);
243
244                 // phi = (p - 1) * (q - 1) * (r - 1) must be relative prime to e
245                 // otherwise RSA just won't work ;-)
246                 BigInteger p1 = p.subtract(BigInteger.ONE);
247                 BigInteger q1 = q.subtract(BigInteger.ONE);
248                 BigInteger r1 = r.subtract(BigInteger.ONE);
249                 BigInteger phi = p1.multiply(q1).multiply(r1);
250
251                 // generate new p and q until they work. typically
252                 if (e.gcd(phi).equals(BigInteger.ONE) == false) {
253                     continue;
254                 }
255
256                 // private exponent d is the inverse of e mod phi
257                 BigInteger d = e.modInverse(phi);
258
259                 RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(n, e);
260                 RSAPrivateKeySpec privateSpec = new RSAPrivateKeySpec(n, d);
261                 PublicKey publicKey = factory.generatePublic(publicSpec);
262                 PrivateKey privateKey = factory.generatePrivate(privateSpec);
263                 keyPair = new KeyPair(publicKey, privateKey);
264             }
265             // -----END SHAMELESSLY ADAPTED BLOCK-----
266
267             try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f))) {
268                 oos.writeObject(keyPair);
269                 oos.close();
270             } catch (IOException ioe) {
271                 ioe.printStackTrace();
272             }
273         }
274         return keyPair;
275     }
276
277     public static String generatePEMCSR(KeyPair kp, String dn) throws GeneralSecurityException, IOException {
278         return generatePEMCSR(kp, dn, new PKCS10Attributes());
279     }
280
281     public static String generatePEMCSR(KeyPair kp, String dn, PKCS10Attributes atts) throws GeneralSecurityException, IOException {
282         return generatePEMCSR(kp, dn, atts, "SHA512WithRSA");
283     }
284
285     public static String generatePEMCSR(KeyPair kp, String dn, PKCS10Attributes atts, String signature) throws GeneralSecurityException, IOException {
286         PKCS10 p10 = new PKCS10(kp.getPublic(), atts);
287         Signature s = Signature.getInstance(signature);
288         s.initSign(kp.getPrivate());
289         p10.encodeAndSign(new X500Name(dn), s);
290         return PEM.encode("CERTIFICATE REQUEST", p10.getEncoded());
291     }
292
293     static int count = 0;
294
295     public static String createRandomIDString() {
296         final char[] chars = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
297         final int idStringLength = 16;
298
299         Random sr;
300         sr = new Random();
301
302         StringBuilder sb = new StringBuilder(idStringLength);
303         for (int i = 0; i < idStringLength; i++) {
304             sb.append(chars[sr.nextInt(chars.length)]);
305         }
306
307         return sb.toString();
308     }
309
310     public static synchronized String createUniqueName() {
311         return "test" + createRandomIDString() + "a" + (count++) + "u";
312     }
313
314     public static CertificateProfile getClientProfile() {
315         return CertificateProfile.getByName("client");
316     }
317
318     public static int countRegex(String text, String pattern) {
319         Pattern p = Pattern.compile(pattern);
320         Matcher m = p.matcher(text);
321         int i = 0;
322         while (m.find()) {
323             i++;
324         }
325         return i;
326     }
327
328     public static void makeAgent(int uid) {
329         try (GigiPreparedStatement ps1 = new GigiPreparedStatement("INSERT INTO cats_passed SET user_id=?, variant_id=?, language='en_EN', version='1'")) {
330             ps1.setInt(1, uid);
331             ps1.setInt(2, CATSType.AGENT_CHALLENGE.getId());
332             ps1.execute();
333         }
334
335         try (GigiPreparedStatement ps2 = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, points='100'")) {
336             ps2.setInt(1, uid);
337             ps2.setInt(2, User.getById(uid).getPreferredName().getId());
338             ps2.execute();
339         }
340     }
341
342     public MailReceiver getMailReceiver() {
343         throw new Error("Feature requires Business or ManagedTest.");
344     }
345
346     public void verify(Domain d) {
347         try {
348             d.addPing(DomainPingType.EMAIL, "admin");
349             TestMail testMail = getMailReceiver().receive("admin@" + d.getSuffix());
350             testMail.verify();
351             assertTrue(d.isVerified());
352         } catch (GigiApiException e) {
353             throw new Error(e);
354         } catch (IOException e) {
355             throw new Error(e);
356         }
357     }
358
359     public static void purgeOnlyDB() throws SQLException, IOException {
360         System.out.println("... resetting Database");
361         long ms = System.currentTimeMillis();
362         try {
363             DatabaseManager.run(new String[] {
364                     testProps.getProperty("sql.driver"), testProps.getProperty("sql.url"), testProps.getProperty("sql.user"), testProps.getProperty("sql.password")
365             }, ImportType.TRUNCATE);
366         } catch (ClassNotFoundException e) {
367             e.printStackTrace();
368         }
369         System.out.println("Database reset complete in " + (System.currentTimeMillis() - ms) + " ms.");
370     }
371
372     public static String validVerificationDateString() {
373         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
374         Calendar c = Calendar.getInstance();
375         c.setTimeInMillis(System.currentTimeMillis());
376         c.add(Calendar.MONTH, -Notary.LIMIT_MAX_MONTHS_VERIFICATION + 1);
377         return sdf.format(new Date(c.getTimeInMillis()));
378     }
379 }