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