]> WPIA git - gigi.git/blob - util-testing/org/cacert/gigi/DevelLauncher.java
Merge "Update notes about password security"
[gigi.git] / util-testing / org / cacert / gigi / DevelLauncher.java
1 package org.cacert.gigi;
2
3 import static org.cacert.gigi.Gigi.*;
4
5 import java.awt.Desktop;
6 import java.io.BufferedReader;
7 import java.io.ByteArrayInputStream;
8 import java.io.ByteArrayOutputStream;
9 import java.io.DataOutputStream;
10 import java.io.FileInputStream;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.InputStreamReader;
14 import java.io.OutputStream;
15 import java.lang.reflect.Field;
16 import java.net.URISyntaxException;
17 import java.net.URL;
18 import java.nio.file.Files;
19 import java.nio.file.Path;
20 import java.nio.file.Paths;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.Properties;
25
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28 import javax.servlet.http.HttpSession;
29
30 import org.cacert.gigi.dbObjects.ObjectCache;
31 import org.cacert.gigi.dbObjects.User;
32 import org.cacert.gigi.localisation.Language;
33 import org.cacert.gigi.output.template.Template;
34 import org.cacert.gigi.output.template.TranslateCommand;
35 import org.cacert.gigi.pages.LoginPage;
36 import org.cacert.gigi.pages.Page;
37 import org.cacert.gigi.pages.account.certs.CertificateRequest;
38 import org.cacert.gigi.pages.main.RegisterPage;
39 import org.cacert.gigi.util.AuthorizationContext;
40 import org.cacert.gigi.util.ServerConstants;
41 import org.kamranzafar.jtar.TarEntry;
42 import org.kamranzafar.jtar.TarHeader;
43 import org.kamranzafar.jtar.TarOutputStream;
44
45 public class DevelLauncher {
46
47     public static void main(String[] args) throws Exception {
48         Properties mainProps = new Properties();
49         try (FileInputStream inStream = new FileInputStream("config/gigi.properties")) {
50             mainProps.load(inStream);
51         }
52         for (int i = 0; i < args.length; i++) {
53             if (args[i].equals("--port")) {
54                 mainProps.setProperty("port", args[i + 1]);
55             }
56             i++;
57         }
58         killPreviousInstance(mainProps);
59
60         ByteArrayOutputStream chunkConfig = new ByteArrayOutputStream();
61         DataOutputStream dos = new DataOutputStream(chunkConfig);
62         byte[] cacerts = Files.readAllBytes(Paths.get("config/cacerts.jks"));
63         byte[] keystore = null;
64         Path p = Paths.get("config/keystore.pkcs12");
65         if (p.toFile().exists()) {
66             keystore = Files.readAllBytes(p);
67         } else {
68             mainProps.setProperty("proxy", "true");
69         }
70
71         DevelLauncher.writeGigiConfig(dos, "changeit".getBytes("UTF-8"), "changeit".getBytes("UTF-8"), mainProps, cacerts, keystore);
72         dos.flush();
73         InputStream oldin = System.in;
74         System.setIn(new ByteArrayInputStream(chunkConfig.toByteArray()));
75         new Launcher().boot();
76         addDevelPage(true);
77         new Thread("ticket awaiter") {
78
79             @Override
80             public void run() {
81                 try {
82                     Thread.sleep(8000);
83                 } catch (InterruptedException e) {
84                     e.printStackTrace();
85                 }
86                 try {
87                     if ( !ticketUsed) {
88                         Desktop.getDesktop().browse(new URL("http://" + ServerConstants.getWwwHostNamePort() + "/ticketWait").toURI());
89                     }
90                 } catch (IOException e) {
91                     e.printStackTrace();
92                 } catch (URISyntaxException e) {
93                     e.printStackTrace();
94                 }
95             }
96         }.start();
97         System.setIn(oldin);
98         BufferedReader br = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
99         System.out.println("Cacert-gigi system sucessfully started.");
100         System.out.println("Press enter to shutdown.");
101         br.readLine();
102         System.exit(0);
103     }
104
105     private static void killPreviousInstance(Properties mainProps) {
106         try {
107             String targetPort = mainProps.getProperty("http.port");
108             String targetHost = mainProps.getProperty("name.www");
109             URL u = new URL("http://" + targetHost + ":" + targetPort + "/kill");
110             u.openStream();
111         } catch (IOException e) {
112         }
113     }
114
115     public static void addDevelPage(boolean withToken) {
116         try {
117             Field instF = Gigi.class.getDeclaredField("instance");
118             Field pageF = Gigi.class.getDeclaredField("pages");
119             instF.setAccessible(true);
120             pageF.setAccessible(true);
121             Object gigi = instF.get(null);
122
123             // Check if we got a proper map (as much as we can tell)
124             Object pagesObj = pageF.get(gigi);
125             @SuppressWarnings("unchecked")
126             HashMap<String, Page> pages = pagesObj instanceof Map ? new HashMap<>((Map<String, Page>) pagesObj) : null;
127
128             pages.put("/manage", new Page("Page-manager") {
129
130                 @Override
131                 public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
132                     ObjectCache.clearAllCaches();
133                     RegisterPage.RATE_LIMIT.bypass();
134                     LoginPage.RATE_LIMIT.bypass();
135                     CertificateRequest.RATE_LIMIT.bypass();
136                     resp.getWriter().println("All caches cleared.");
137                     System.out.println("Caches cleared.");
138
139                 }
140
141                 @Override
142                 public boolean needsLogin() {
143                     return false;
144                 }
145
146             });
147
148             pages.put("/kill", new Page("Kill") {
149
150                 /**
151                  * The contained call to {@link System#exit(int)} is mainly
152                  * needed to kill this instance immediately if another
153                  * {@link DevelLauncher} is booting up to free all ports This is
154                  * required for fast development cycles.
155                  * 
156                  * @see #killPreviousInstance(Properties)
157                  */
158                 @Override
159                 public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
160                     System.exit(0);
161                 }
162
163                 @Override
164                 public boolean needsLogin() {
165                     return false;
166                 }
167             });
168
169             if (withToken) {
170                 addTicketPage(pages);
171             }
172
173             pageF.set(gigi, Collections.unmodifiableMap(pages));
174         } catch (ReflectiveOperationException e) {
175             e.printStackTrace();
176         }
177     }
178
179     static boolean ticketUsed = false;
180
181     private static void addTicketPage(HashMap<String, Page> pages) {
182         pages.put("/ticketWait", new Page("ticket") {
183
184             Template t = new Template(DevelLauncher.class.getResource("DevelTicketWait.templ"));
185
186             @Override
187             public boolean needsLogin() {
188                 return false;
189             }
190
191             @Override
192             public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
193                 resp.setHeader("content-security-policy", "");
194                 t.output(resp.getWriter(), getLanguage(req), new HashMap<String, Object>());
195             }
196
197         });
198         pages.put("/ticket", new Page("ticket") {
199
200             @Override
201             public boolean beforeTemplate(HttpServletRequest req, HttpServletResponse resp) throws IOException {
202                 // TODO Auto-generated method stub
203                 if ( !ticketUsed) {
204                     HttpSession sess = req.getSession();
205                     User user = User.getById(1);
206                     if (user == null) {
207                         resp.getWriter().println("ticket consumed but no user available for that action");
208                         ticketUsed = true;
209                         return true;
210                     }
211                     sess.setAttribute(LOGGEDIN, true);
212                     sess.setAttribute(Language.SESSION_ATTRIB_NAME, user.getPreferredLocale());
213                     sess.setAttribute(AUTH_CONTEXT, new AuthorizationContext(user, user));
214                     req.getSession().setAttribute(LOGIN_METHOD, new TranslateCommand("Ticket"));
215                     resp.getWriter().println("ticket consumed");
216                     ticketUsed = true;
217                 }
218                 return true;
219             }
220
221             @Override
222             public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {}
223
224             @Override
225             public boolean needsLogin() {
226                 return false;
227             }
228         });
229     }
230
231     public static void writeGigiConfig(OutputStream target, byte[] keystorepw, byte[] truststorepw, Properties mainprop, byte[] cacerts, byte[] keystore) throws IOException {
232         TarOutputStream tos = new TarOutputStream(target);
233         ByteArrayOutputStream baos = new ByteArrayOutputStream();
234         mainprop.store(baos, "");
235
236         putTarEntry(baos.toByteArray(), tos, "gigi.properties");
237         putTarEntry(keystorepw, tos, "keystorepw");
238         putTarEntry(truststorepw, tos, "truststorepw");
239         putTarEntry(keystore, tos, "keystore.pkcs12");
240         putTarEntry(cacerts, tos, "cacerts.jks");
241         tos.close();
242
243     }
244
245     private static void putTarEntry(byte[] data, TarOutputStream tos, String name) throws IOException {
246         if (data == null) {
247             return;
248         }
249         TarHeader th = new TarHeader();
250         th.name = new StringBuffer(name);
251         th.size = data.length;
252         tos.putNextEntry(new TarEntry(th));
253         tos.write(data);
254     }
255
256 }