4A Server -  2.0
 All Classes Namespaces Files Functions Variables Enumerator
DrupAuthServlet.java
Go to the documentation of this file.
1 /*
2  * Project: Server for annotations sharing
3  * Author: Ing. Jaroslav Dytrych idytrych@fit.vutbr.cz
4  * File: DrupAuthServlet.java
5  * Description: Servlet which allows Drupal CMS to send informations about it's
6  * users, so users can be authenticated automatically with these
7  * informations. If user haven't account yet, it will be created.
8  */
9 
10 /**
11  * @file DrupAuthServlet.java
12  *
13  * Servlet which allows Drupal CMS to send informations about it's
14  * users, so users can be authenticated automatically with these
15  * informations. If user haven't account yet, it will be created.
16  *
17  * @brief Servlet which allows Drupal CMS to send informations about it's users
18  */
19 
20 package cz.vutbr.fit.knot.annotations.comet;
21 
31 import java.io.BufferedReader;
32 import java.io.IOException;
33 import java.io.PrintWriter;
34 import java.io.StringReader;
35 import java.io.StringWriter;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.ListIterator;
41 import java.util.Map;
42 import java.util.logging.Level;
43 import java.util.logging.Logger;
44 import javax.persistence.EntityManager;
45 import javax.persistence.EntityTransaction;
46 import javax.servlet.ServletException;
47 import javax.servlet.http.HttpServlet;
48 import javax.servlet.http.HttpServletRequest;
49 import javax.servlet.http.HttpServletResponse;
50 import javax.xml.parsers.DocumentBuilder;
51 import javax.xml.parsers.DocumentBuilderFactory;
52 import javax.xml.parsers.ParserConfigurationException;
53 import org.w3c.dom.Document;
54 import org.w3c.dom.Element;
55 import org.w3c.dom.NodeList;
56 import org.xml.sax.InputSource;
57 import org.xml.sax.SAXException;
58 
59 /**
60  * Servlet which allows Drupal CMS to send informations about it's users,
61  * so users can be authenticated automatically with these informations.
62  * If user haven't account yet, it will be created.
63  *
64  * @brief Servlet which allows Drupal CMS to send informations about it's users
65  * @author idytrych
66  */
67 public class DrupAuthServlet extends HttpServlet {
68 
69  /**
70  * Handles the HTTP <code>GET</code> method.
71  *
72  * @param request servlet request
73  * @param response servlet response
74  * @throws ServletException if a servlet-specific error occurs
75  * @throws IOException if an I/O error occurs
76  */
77  @Override
78  protected void doGet(HttpServletRequest request, HttpServletResponse response)
79  throws ServletException, IOException {
80  response.setContentType("text/html;charset=UTF-8");
81  PrintWriter out = response.getWriter();
82  try {
83  out.println(getError());
85  Logger.getLogger(DrupAuthServlet.class.getName()).log(Level.ALL, "GET request on DrupAuthServlet");
86  }
87  } finally {
88  out.close();
89  }
90  } // doGet()
91 
92  /**
93  * Handles the HTTP <code>POST</code> method.
94  *
95  * @param request servlet request
96  * @param response servlet response
97  * @throws ServletException if a servlet-specific error occurs
98  * @throws IOException if an I/O error occurs
99  */
100  @Override
101  protected void doPost(HttpServletRequest request, HttpServletResponse response)
102  throws ServletException, IOException {
103  BufferedReader bufReader = request.getReader();
104 
105  String responseString = "OK"; // default reply for the client
106  try {
107 
108  // read XML from Drupal
109  StringWriter writer = new StringWriter();
110  int nextChar;
111  while ((nextChar = bufReader.read()) != -1) {
112  writer.write(nextChar);
113  }
114  String requestStr = writer.toString();
115 
116  // parse XML
117  Document doc = parseXml(requestStr);
118  if (doc == null) {
120  String msg = "Bad XML received from Drupal: " + requestStr;
121  Logger.getLogger(DrupAuthServlet.class.getName()).log(Level.ALL, msg);
122  }
123  throw new RuntimeException("Bad data sent!");
124  }
125  Element docEl = doc.getDocumentElement();
126 
127  ArrayList<User> users = new ArrayList<User>();
128  Map<String,String> authData = new HashMap<String, String>();
129 
130  String drupalAddress = "";
131 
132  NodeList drupalNL = docEl.getElementsByTagName("drupal");
133  Element drupalEl = (Element) drupalNL.item(0);
134  if (drupalEl != null) {
135  drupalAddress = drupalEl.getAttribute("address");
136  if (drupalAddress == null) {
137  drupalAddress = "";
138  }
139  }
140 
141  if (drupalAddress.isEmpty()) {
143  Logger.getLogger(DrupAuthServlet.class.getName()).log(Level.SEVERE, "Missing Drupal address.");
144  }
145  throw new RuntimeException("No drupal address included!");
146  }
147 
148  String remoteAddr = request.getRemoteAddr();
149  String remoteHost = request.getRemoteHost();
151  && !isAuthorizedByIp(remoteAddr)) {
153  String msg = "Bad IP of Drupal: " + remoteAddr;
154  Logger.getLogger(DrupAuthServlet.class.getName()).log(Level.ALL, msg);
155  }
156  throw new RuntimeException("Bad IP");
157  }
159  && !isAuthorizedByName(remoteHost)) {
161  String msg = "Bad host of Drupal: " + remoteHost;
162  Logger.getLogger(DrupAuthServlet.class.getName()).log(Level.ALL, msg);
163  }
164  throw new RuntimeException("Bad Host Name");
165  }
166 
167  NodeList userNodes = docEl.getElementsByTagName("user");
168  int uCount = userNodes.getLength();
169  for (int i = 0; i < uCount; i++) {
170  Element userEl = (Element) userNodes.item(i);
171  User u = processUser(userEl,authData,drupalAddress);
172  if (u != null) {
173  users.add(u);
174  } else {
176  String msg = "Bad entry in authentication data from Drupal " + drupalAddress
177  + " " + userEl.toString();
178  Logger.getLogger(DrupAuthServlet.class.getName()).log(Level.SEVERE, msg);
179  }
180  throw new RuntimeException("Bad entry found!");
181  }
182  }
183 
184  saveUsers(users,authData);
185 
186  } catch (Exception e) { // if any exception occurred
188  String msg = "Unknown exception:";
189  Logger.getLogger(DrupAuthServlet.class.getName()).log(Level.SEVERE, msg, e);
190  }
191  responseString = getError();
192  }
193 
194  // return reply to client
195  PrintWriter resWriter = response.getWriter();
196  resWriter.write(responseString);
197  resWriter.flush();
198 
199  } // doPost()
200 
201  /**
202  * Saves data about users to the database and authentication data to AppBean
203  *
204  * @param users List of users to save
205  * @param aData Map with authentication data
206  */
207  private void saveUsers(List users, Map<String, String> aData) {
208  EntityManager em = AppBean.getPersistenceManager().getEM();
209  EntityTransaction transaction = em.getTransaction();
210  boolean errorOccurred = false;
211  try {
212  transaction.begin();
213 
214  // existing users will be updated, new users will be saved
215  for (Iterator uIt = users.iterator(); uIt.hasNext();) { // for all users
216  User user = (User) uIt.next();
217  ArrayList<UserGroup> groups = user.getGroupsAL();
218  user.setGroups(null);
219 
220  user = em.merge(user);
221 
222  for (Iterator<UserGroup> gIt = groups.iterator(); gIt.hasNext();) {
223  UserGroup ug = gIt.next();
224  ug = em.merge(ug);
225  if (!ug.getUsers().contains(user)) {
226  ug.addUser(user);
227  }
228  user.addGroup(ug);
229  }
230  } // for all users
231 
232  em.flush();
233  transaction.commit();
234 
235  } catch (Exception e) {
237  String msg = "Saving of users failed.";
238  Logger.getLogger(DrupAuthServlet.class.getName()).log(Level.SEVERE, msg, e);
239  }
240  transaction.rollback();
241  errorOccurred = true;
242  } finally {
243  em.close();
244  }
245  if (errorOccurred) {
246  throw new RuntimeException("Database failure!");
247  }
248 
249  for (Iterator uIt = users.iterator(); uIt.hasNext();) {
250  User user = (User) uIt.next();
251 
252  if(user.getId() == null){
253  //new user
254  //find new user
255  Object[] params = {"login",user.getLogin(),"comeFrom",user.getComeFrom()};
256  @SuppressWarnings("unchecked")
257  List<User> userList = AppBean.getPersistenceManager().queryDB("User.findByLoginAndSystem",params);
258 
259  if(userList != null){
260  String resultError = loadDefaultSettings(userList.get(0));
261  if(resultError != null){
262  throw(new RuntimeException("Error occured during default user settings save procedure."));
263  }
264  }else throw(new RuntimeException("Error: Can't find user."));
265  }
266 
267  AppBean.setExtAuthData(user.getComeFrom(), user.getLogin(), aData.get(user.getLogin()));
268  }
269  } // saveUSers()
270 
271  /**
272  * Processes XML element with data about user and returns User object.
273  * If user is already in database, saved object will be returned.
274  * If user is not in database, new object will be created.
275  * It also stores authentication data into the given map.
276  *
277  * @param userEl XML element with data about user
278  * @param authData Map to which authentication data will be stored.
279  * @param drupalAddress Address from which user comes
280  * @return Returns User object of the given user.
281  */
282  private User processUser(Element userEl, Map<String,String> authData, String drupalAddress) {
283  User user = null;
284 
285  // get attributes of user from XML
286  String loginAtt = userEl.getAttribute("login");
287  String tokenAtt = userEl.getAttribute("token");
288  String idAtt = userEl.getAttribute("id");
289  String nameAtt = userEl.getAttribute("name");
290  String passwordAtt = userEl.getAttribute("password");
291  String emailAtt = userEl.getAttribute("email");
292  if (loginAtt == null) { // null is a same as not set
293  loginAtt = "";
294  }
295  if (tokenAtt == null) {
296  tokenAtt = "";
297  }
298  if (idAtt == null) {
299  idAtt = "";
300  }
301  if (nameAtt == null) {
302  nameAtt = "";
303  }
304  if (passwordAtt == null) {
305  passwordAtt = "";
306  }
307  if (emailAtt == null) {
308  emailAtt = "";
309  }
310 
311  if (loginAtt.isEmpty()) {
312  return null;
313  }
314 
315  // query database for the user (find by login)
316  Object[] params = new Object[4];
317  params[0] = "login";
318  params[1] = loginAtt;
319  params[2] = "comeFrom";
320  params[3] = drupalAddress;
321  List uList = AppBean.getPersistenceManager().queryDB("User.findByLoginAndSystem", params);
322  if (uList != null && !uList.isEmpty()) { // if user was found
323  user = (User) uList.get(0);
324  }
325 
326  if (user == null) {
327  user = new User(loginAtt, nameAtt, emailAtt, passwordAtt);
328  user.setGroups(new ArrayList<UserGroup>());
329  }
330 
331  // set Drupal to which is user logged in
332  user.setComeFrom(drupalAddress);
333  // set authentication data
334  authData.put(loginAtt, tokenAtt);
335 
336  NodeList groupsNL = userEl.getElementsByTagName("group");
337  int groupsCnt = groupsNL.getLength();
338  for (int i = 0; i < groupsCnt; i++) { // for all groups
339  Element gropEl = (Element) groupsNL.item(i);
340  String groupName = gropEl.getAttribute("name");
341  if (groupName == null) {
342  groupName = "";
343  }
344  if (groupName.isEmpty()) {
345  continue;
346  }
347 
348  // query database for group
349  params[0] = "name";
350  params[1] = groupName;
351  List gList = AppBean.getPersistenceManager().queryDB("UserGroup.findByName", params);
352  if (gList != null && !gList.isEmpty()) { // if group was found
353  UserGroup group = (UserGroup) gList.get(0);
354  if (!user.getGroupsAL().contains(group)) {
355  user.addGroup(group); // add user to group
356  }
357  }
358 
359  } // for all groups
360 
361  return user;
362  } // processUser()
363 
364  /**
365  * Gets error page (there is not much informations because it can be used
366  * by attacker)
367  *
368  * @return Returns content of the error page
369  */
370  private static String getError() {
371  String retString = "<html>"
372  + "<head>"
373  + "<title>Error</title>"
374  + "</head>"
375  + "<body>"
376  + "<h1>Access to this page is not allowed!</h1>"
377  + "</body>"
378  + "</html>";
379  return retString;
380  }
381 
382  /**
383  * Parses XML with data
384  *
385  * @param XMLString String with XML with data from Drupal
386  * @return Parsed XML in w3c.dom.Document
387  */
388  private static Document parseXml(String XMLString) throws SAXException, ParserConfigurationException, IOException {
389  Document dom;
390  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
391  dbf.setNamespaceAware(true);
392 
393  DocumentBuilder db = dbf.newDocumentBuilder();
394  InputSource is = new InputSource();
395  is.setCharacterStream(new StringReader(XMLString));
396  dom = db.parse(is);
397 
398  return dom;
399  } // parseXml()
400 
401  /**
402  * Returns a short description of the servlet.
403  *
404  * @return a String containing servlet description
405  */
406  @Override
407  public String getServletInfo() {
408  return "Servlet which allows Drupal CMS to send informations about it's users";
409  }
410 
411  private String loadDefaultSettings(User user){
412  @SuppressWarnings("unchecked")
413  List<DefaultSettings> settingsList = AppBean.getPersistenceManager().queryDB("DefaultSettings.findAll");
414  if(settingsList != null){
415  String errorMessageOut;
416  ListIterator<DefaultSettings> resultIt = settingsList.listIterator();
417  while(resultIt.hasNext()){
418  DefaultSettings actualDefaultSetting = resultIt.next();
419  Settings newUserSetting = new Settings(user,actualDefaultSetting.getName(),actualDefaultSetting.getValue());
420 
421  if (AppBean.getPersistenceManager().persistEntity(newUserSetting)) {
422  errorMessageOut = MessageProvider.getMessage("newUserSettingDBFailure");
424  String msg = "Persisting of new user settings failed.";
425  Logger.getLogger(EditUser.class.getName()).log(Level.SEVERE, msg);
426  }
427  return errorMessageOut;
428  }
429  }
430  }
431 
432  return null;
433  }
434 
435  /**
436  * Method checks if request come from authorized server.
437  *
438  * @param ipAdderess ip addres of server
439  * @return true value indicate thet server is authorized, false indicates unauthorized request
440  */
441  private boolean isAuthorizedByIp(String ipAdderess){
442  @SuppressWarnings("unchecked")
443  List<DrupAuthServer> results = AppBean.getPersistenceManager().getEntitiesByName("DrupAuthServer");
444  if(results == null || results.isEmpty()){
445  return true;
446  }
447 
448  Iterator<DrupAuthServer> resultsIt = results.iterator();
449  if(resultsIt.hasNext()){
450  if(resultsIt.next().getIpAddress().equals(ipAdderess)){
451  return true;
452  }
453  }
454 
455  return false;
456  }
457 
458  /**
459  * Method checks if request come from authorized server.
460  *
461  * @param hostName host name of server
462  * @return true value indicate thet server is authorized, false indicates unauthorized request
463  */
464  private boolean isAuthorizedByName(String hostName){
465  @SuppressWarnings("unchecked")
466  List<DrupAuthServer> results = AppBean.getPersistenceManager().getEntitiesByName("DrupAuthServer");
467  if(results == null || results.isEmpty()){
468  return true;
469  }
470 
471  Iterator<DrupAuthServer> resultsIt = results.iterator();
472  if(resultsIt.hasNext()){
473  if(resultsIt.next().getHostName().equals(hostName)){
474  return true;
475  }
476  }
477 
478  return false;
479  }
480 } // class DrupAuthServlet
User processUser(Element userEl, Map< String, String > authData, String drupalAddress)
Singleton for storing global variables.
Definition: AppBean.java:47
Class representing approved drupal authentization server.
Class for getting localized messages from message bundle.
ArrayList< UserGroup > getGroupsAL()
Definition: User.java:177
Servlet which allows Drupal CMS to send informations about it's users.
void doGet(HttpServletRequest request, HttpServletResponse response)
Class representing user group.
Definition: UserGroup.java:47
Class representing parameter of user settings.
Definition: Settings.java:45
Class representing user.
Definition: User.java:51
Backbean for adding, editing and deleting of users.
Definition: EditUser.java:45
Class representing parameter of default user settings.
void doPost(HttpServletRequest request, HttpServletResponse response)
void saveUsers(List users, Map< String, String > aData)