1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package no.feide.moria.servlet;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.HashMap;
29 import java.util.TreeMap;
30 import java.util.Properties;
31 import java.util.ResourceBundle;
32 import java.util.Vector;
33 import javax.net.ssl.HttpsURLConnection;
34 import java.net.URL;
35 import java.net.Authenticator;
36 import java.net.PasswordAuthentication;
37 import java.net.MalformedURLException;
38
39 import javax.servlet.ServletException;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletResponse;
42 import javax.xml.parsers.SAXParser;
43 import javax.xml.parsers.SAXParserFactory;
44
45 import no.feide.moria.controller.AuthenticationException;
46 import no.feide.moria.controller.AuthorizationException;
47 import no.feide.moria.controller.DirectoryUnavailableException;
48 import no.feide.moria.controller.IllegalInputException;
49 import no.feide.moria.controller.InoperableStateException;
50 import no.feide.moria.controller.MoriaController;
51 import no.feide.moria.log.MessageLogger;
52 import no.feide.moria.store.MoriaStore;
53 import no.feide.moria.store.MoriaStoreException;
54 import no.feide.moria.store.MoriaStoreFactory;
55
56 /***
57 * The StatusServlet shows the status of Moria.
58 * @version $Revision: 1.17 $
59 */
60 public class StatusServlet
61 extends MoriaServlet {
62
63 /*** Used for logging. */
64 private final MessageLogger log = new MessageLogger(StatusServlet.class);
65
66 /***
67 * List of parameters required by <code>StatusServlet</code>.
68 * <br>
69 * <br>
70 * Current required parameters are:
71 * <ul>
72 * <li><code>RequestUtil.PROP_BACKENDSTATUS_STATUS_XML</code>
73 * </ul>
74 * @see RequestUtil#PROP_BACKENDSTATUS_STATUS_XML
75 */
76 private static final String[] REQUIRED_PARAMETERS = {
77 RequestUtil.PROP_BACKENDSTATUS_STATUS_XML,
78 RequestUtil.PROP_COOKIE_LANG };
79
80 /***
81 *
82 * @return the required parameters for this servlet.
83 */
84 public static String[] getRequiredParameters() {
85 return REQUIRED_PARAMETERS;
86 }
87
88 /***
89 * A hash map containing the attributes for a test-user.
90 * Each item in the hashmap maps from a user name to an
91 * backendStatusUser class instance
92 */
93 private HashMap backendDataUsers = null;
94
95 /***
96 * Monitor for the status xml file.
97 */
98 private FileMonitor statusFileMonitor = null;
99
100 /***
101 * The organization the test user comes from.
102 */
103 private static final String STATUS_ATTRIBUTE = "eduPersonAffiliation";
104
105 /***
106 * The name of the service.
107 */
108 private static final String STATUS_PRINCIPAL = "status";
109
110 /***
111 * Implements a simple xml parser that parses the status.xml file
112 * into a HashMap with BackendStatusUser instances.
113 *
114 * @see BackendStatusHandler
115 * @see BackendStatusUser
116 */
117 public final synchronized HashMap getBackendStatusData() {
118 if (statusFileMonitor == null || statusFileMonitor.hasChanged()) {
119 Properties config = getConfig();
120 if (config != null) {
121 BackendStatusHandler handler = new BackendStatusHandler();
122 SAXParserFactory factory = SAXParserFactory.newInstance();
123 try {
124 String filename = (String) config.get(RequestUtil.PROP_BACKENDSTATUS_STATUS_XML);
125 SAXParser saxParser = factory.newSAXParser();
126 saxParser.parse(new File(filename), handler);
127 statusFileMonitor = new FileMonitor(filename);
128 } catch (Throwable t) {
129 log.logCritical("Error parsing status.xml");
130 } finally {
131 backendDataUsers = handler.getAttribs();
132 }
133 }
134 }
135 return backendDataUsers;
136 }
137
138 private void printTable() {
139 HashMap backendStatusData = getBackendStatusData();
140
141 }
142
143 /***
144 * Checks the config
145 *
146 * @param bundle
147 * @return
148 */
149 private Vector checkConfigStatus(ResourceBundle bundle) {
150 Vector statusConfig = new Vector();
151
152 try {
153 this.getServletConfig(InformationServlet.getRequiredParameters(), log);
154 } catch (IllegalStateException e) {
155 String errorMsg = bundle.getString("config_info");
156 statusConfig.add(errorMsg);
157 }
158
159
160 try {
161 this.getServletConfig(LoginServlet.getRequiredParameters(), log);
162 } catch (IllegalStateException e) {
163 String errorMsg = bundle.getString("config_login");
164 statusConfig.add(errorMsg);
165 }
166
167
168 try {
169 this.getServletConfig(StatisticsServlet.getRequiredParameters(), log);
170 } catch (IllegalStateException e) {
171 String errorMsg = bundle.getString("config_statistics");
172 statusConfig.add(errorMsg);
173 }
174
175 return statusConfig;
176 }
177
178 /***
179 * Checks the modules
180 *
181 * @param bundle
182 * @return
183 */
184 private Vector checkModules(ResourceBundle bundle) {
185 Map statusMap = MoriaController.getStatus();
186 Vector statusMsg = new Vector();
187
188 if (statusMap != null) {
189
190 String[] states = {"moria", "init", "am", "dm", "sm", "web"};
191 Map moduleNames = new HashMap();
192 moduleNames.put("moria", "Moria");
193 moduleNames.put("init", "Controller");
194 moduleNames.put("am", "Authorization manager");
195 moduleNames.put("dm", "Directory manager");
196 moduleNames.put("sm", "Store manager");
197 moduleNames.put("web", "Web application");
198
199 for (int i = 0; i < states.length; i++) {
200
201 Object stateObject = statusMap.get(states[i]);
202 Boolean isReady = new Boolean(false);
203
204 if (stateObject instanceof Boolean) {
205 isReady = (Boolean) stateObject;
206 }
207
208 if (states[i].equals("moria") && isReady.booleanValue()) {
209 statusMsg = new Vector();
210 break;
211 } else if (!isReady.booleanValue()) {
212 String errormsg = bundle.getString("module_1") + moduleNames.get(states[i]) + bundle.getString("module_2") + "<br>";
213 statusMsg.add(errormsg);
214 }
215 }
216 }
217 return statusMsg;
218 }
219
220 /***
221 * Checks the SOAP page.
222 *
223 * @param bundle
224 * @return
225 */
226 private Vector checkSoap(ResourceBundle bundle) {
227
228 Vector soapMsg = new Vector();
229
230 try {
231 URL url = new URL("https://login.feide.no/moria2/v2_1/Authentication?wsdl");
232 java.net.Authenticator.setDefault(new Authenticator() {
233 protected java.net.PasswordAuthentication getPasswordAuthentication()
234 {
235 char[] passwd = {'d', 'e', 'm', 'o', '_', 's', 'e', 'r', 'v', 'i', 'c', 'e'};
236 return new PasswordAuthentication(new String("demo_service"), passwd);
237 }
238 });
239
240
241 HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
242 connection.setRequestMethod("GET");
243 connection.connect();
244 int code = connection.getResponseCode();
245 if (code != HttpsURLConnection.HTTP_OK) {
246 String err = new String(bundle.getString("soap_error") + new Integer(code).toString());
247 soapMsg.add(err);
248 }
249
250 } catch(MalformedURLException e) {
251 String err = bundle.getString("soap_exception1");
252 soapMsg.add(err);
253 } catch (IOException e) {
254 String err = bundle.getString("soap_exception2");
255 soapMsg.add(err);
256 }
257
258 return soapMsg;
259 }
260
261 public Vector checkBackend(ResourceBundle bundle) {
262
263 Vector backendMsg = new Vector();
264
265 for (Iterator iterator = backendDataUsers.keySet().iterator(); iterator.hasNext();) {
266 String key = (String) iterator.next();
267 BackendStatusUser userData = (BackendStatusUser) backendDataUsers.get(key);
268 String org = userData.getOrganization();
269 try {
270 final Map attributes = MoriaController.directNonInteractiveAuthentication(new String[] {STATUS_ATTRIBUTE},
271 userData.getName(), userData.getPassword(), STATUS_PRINCIPAL);
272 } catch (Exception e) {
273 String err = bundle.getString("ldap_error") + org;
274 backendMsg.add(err);
275 }
276 }
277 return backendMsg;
278 }
279
280 /***
281 * Handles the GET requests.
282 * @param request
283 * The HTTP request object.
284 * @param response
285 * The HTTP response object.
286 * @throws java.io.IOException
287 * If an input or output error is detected when the servlet
288 * handles the GET request.
289 * @throws javax.servlet.ServletException
290 * If the request for the GET could not be handled.
291 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
292 * javax.servlet.http.HttpServletResponse)
293 */
294 public final void doGet(final HttpServletRequest request, final HttpServletResponse response)
295 throws IOException, ServletException {
296 getBackendStatusData();
297
298 Properties config = getConfig();
299 response.setContentType("text/html");
300 PrintWriter out = response.getWriter();
301 String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n";
302
303
304 String language = null;
305 String langFromCookie = null;
306 if (config != null && request.getCookies() != null) {
307 langFromCookie = RequestUtil.getCookieValue((String) config.get(RequestUtil.PROP_COOKIE_LANG), request.getCookies());
308 }
309
310 if (request.getParameter(RequestUtil.PARAM_LANG) != null) {
311 language = request.getParameter(RequestUtil.PARAM_LANG);
312 response.addCookie(RequestUtil.createCookie(
313 (String) config.get(RequestUtil.PROP_COOKIE_LANG),
314 language,
315 new Integer((String) config.get(RequestUtil.PROP_COOKIE_LANG_TTL)).intValue()));
316 }
317
318
319 final ResourceBundle bundle = RequestUtil.getBundle(
320 RequestUtil.BUNDLE_STATUSSERVLET,
321 language, langFromCookie, null,
322 request.getHeader("Accept-Language"),
323 (String) config.get(RequestUtil.PROP_LOGIN_DEFAULT_LANGUAGE));
324
325
326 out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
327 out.println(docType + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=" + bundle.getLocale() + ">");
328 out.println("<head><link rel=\"icon\" href=\"/favicon.ico\" type=\"image/png\">");
329 out.println("<style type=\"text/css\">\n@import url(\"../resource/stil.css\");\n</style>");
330 out.println("<link rel=\"author\" href=\"mailto:" + config.get(RequestUtil.RESOURCE_MAIL)+ "\">");
331 out.println("<title>" + bundle.getString("header_title") + "</title></head><body>");
332
333
334 out.println("<table summary=\"Layout-tabell\" class=\"invers\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">");
335 out.println("<tbody><tr valign=\"middle\">");
336 out.println("<td class=\"logo\" width=\"76\"><a href=" + config.get(RequestUtil.RESOURCE_LINK) + ">");
337 out.println("<img src=\"../resource/logo.gif\" alt=" + config.get(RequestUtil.PROP_FAQ_OWNER) + " border=\"0\" height=\"41\" width=\"76\"></a></td>");
338 out.println("<td width=\"0%\"><a class=\"noline\" href=" + config.get(RequestUtil.RESOURCE_LINK) + ">");
339 out.println(bundle.getString("header_feide") + "</a></td>");
340 out.println("<td width=\"35%\"> </td>");
341
342
343 TreeMap languages = (TreeMap)RequestUtil.parseConfig(config, RequestUtil.PROP_LANGUAGE, RequestUtil.PROP_COMMON);
344 Iterator it = languages.keySet().iterator();
345 while(it.hasNext()) {
346 String longName = (String) it.next();
347 String shortName = (String) languages.get(longName);
348 if (RequestUtil.ATTR_SELECTED_LANG.equals(shortName)) {
349 out.println("[" + longName + "]");
350 } else
351 out.println("<td align=\"centre\"><small><a class=\"invers\" href ="
352 + config.get(RequestUtil.PROP_FAQ_STATUS) + "?" + RequestUtil.PARAM_LANG + "=" + shortName + ">" + longName
353 + "</a></small></td>");
354 }
355
356
357 out.println("<td class=\"dekor1\" width=\"100%\"> </td></tr></tbody></table>");
358 out.println("<div class=\"midt\">");
359 out.println("<table cellspacing=\"0\">");
360 out.println("<tbody><tr valign=\"top\">");
361 out.println("<td class=\"kropp\">");
362
363
364 Vector allerrors = new Vector();
365 allerrors.addAll(checkModules(bundle));
366 allerrors.addAll(checkConfigStatus(bundle));
367 allerrors.addAll(checkSoap(bundle));
368 allerrors.addAll(checkBackend(bundle));
369 if (allerrors.size()>0) {
370 for (int i = 0; i < allerrors.size(); i++) {
371 out.println("<font color=#FFFFFF>" + (String)(allerrors.get(i)) + "</font>" + "<br>");
372 }
373 } else if (allerrors.size() == 0) {
374 out.println("<font color=#FFFFFF>" + bundle.getString("ready_msg") + "</font>");
375 }
376
377
378 out.println("<p><table border=1><tr><th>" + bundle.getString("table_organization") + "</th><th>" + bundle.getString("table_status") + "</th></tr>");
379
380
381 for (Iterator iterator = backendDataUsers.keySet().iterator(); iterator.hasNext();) {
382 String key = (String) iterator.next();
383 BackendStatusUser userData = (BackendStatusUser) backendDataUsers.get(key);
384 out.println("<tr><td>" + userData.getOrganization() + "</td>");
385 try {
386 final Map attributes = MoriaController.directNonInteractiveAuthentication(new String[] {STATUS_ATTRIBUTE},
387 userData.getName(), userData.getPassword(), STATUS_PRINCIPAL);
388
389
390 out.println("<td>OK</td>");
391
392 } catch (AuthenticationException e) {
393 log.logWarn("Authentication failed for: " + userData.getName() + ", contact: " + userData.getContact());
394 String message = "<a href=mailto:" + userData.getContact() + "?subject=" + userData.getOrganization()
395 + ":%20" + bundle.getString("subject_authentication") + userData.getName() + ">" + bundle.getString("error_help");
396 out.println("<td>" + bundle.getString("error_authentication") + message + "</a></td></tr>");
397 } catch (DirectoryUnavailableException e) {
398 log.logWarn("The directory is unavailable for: " + userData.getName() + ", contact: " + userData.getContact());
399 String message = "<a href=mailto:" + userData.getContact() + "?subject=" + userData.getOrganization()
400 + ":%20" + bundle.getString("subject_directory") + userData.getName() + ">" + bundle.getString("error_help");
401 out.println("<td>" + bundle.getString("error_directory") + message + "</a></td></tr>");
402 } catch (AuthorizationException e) {
403 log.logWarn("Authorization failed for: " + userData.getName() + ", contact: " + userData.getContact());
404 String message = "<a href=mailto:" + userData.getContact() + "?subject=" + userData.getOrganization()
405 + ":%20" + bundle.getString("subject_authorization") + userData.getName() + ">" + bundle.getString("error_help");
406 out.println("<td>" + bundle.getString("error_authorization") + message + "</a></td></tr>");
407 } catch (IllegalInputException e) {
408 log.logWarn("Illegal input for: " + userData.getName() + ", contact: " + userData.getContact());
409 String message = "<a href=mailto:" + userData.getContact() + "?subject=" + userData.getOrganization()
410 + ":%20" + bundle.getString("subject_illegal") + userData.getName() + ">" + bundle.getString("error_help");
411 out.println("<td>" + bundle.getString("error_illegal") + message + "</a></td></tr>");
412 } catch (InoperableStateException e) {
413 log.logWarn("Inoperable state for: " + userData.getName() + ", contact: " + userData.getContact());
414
415 if (userData.getOrganization().equals("Uninett")){
416 String message = "<a href=mailto:" + userData.getContact() + "?subject=" + userData.getOrganization()
417 + ":%20" + bundle.getString("subject_inoperable") + userData.getName() + ">" + bundle.getString("error_help");
418 out.println("<td>" + bundle.getString("error_inoperable") + message + "</a></td></tr>");
419 } else {
420 out.println("<td>" + bundle.getString("error_inoperable"));
421 }
422 } finally {
423
424
425 out.println("</tr>");
426
427 }
428 }
429
430
431 out.println("</table></p>");
432
433
434 out.println("</tr>");
435 out.println("</table>");
436 out.println("</tbody>");
437 out.println("</div>");
438
439 out.println("<p>");
440 out.println("<table summary=\"Layout-tabell\" class=\"invers\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">");
441 out.println("<tbody><tr class=\"bunn\" valign=\"middle\">");
442 out.println("<td class=\"invers\" align=\"left\"><small><a class=\"invers\" href=\"mailto:"
443 + config.get(RequestUtil.RESOURCE_MAIL) + "\">" + config.get(RequestUtil.RESOURCE_MAIL) + "</a></small></td>");
444 out.println("<td class=\"invers\" align=\"right\"><small>" + config.get(RequestUtil.RESOURCE_DATE) + "</small></td>");
445 out.println("</tr></tbody></table></p>");
446
447
448 out.println("</body></html>");
449
450 }
451
452 /***
453 * Get this servlet's configuration from the web module, given by
454 * <code>RequestUtil.PROP_CONFIG</code>.
455 * @return The last valid configuration.
456 * @throws IllegalStateException
457 * If unable to read the current configuration from the servlet
458 * context, and there is no previous configuration. Also thrown
459 * if any of the required parameters (given by
460 * <code>REQUIRED_PARAMETERS</code>) are not set.
461 * @see #REQUIRED_PARAMETERS
462 * @see RequestUtil#PROP_CONFIG
463 */
464 private Properties getConfig() {
465 try {
466 return getServletConfig(getRequiredParameters(), log);
467 }
468 catch (IllegalStateException e) {
469 return null;
470 }
471 }
472
473 }
474