/********************************************************************************* * The contents of this file are subject to the Common Public Attribution * License Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.openemm.org/cpal1.html. The License is based on the Mozilla * Public License Version 1.1 but Sections 14 and 15 have been added to cover * use of software over a computer network and provide for limited attribution * for the Original Developer. In addition, Exhibit A has been modified to be * consistent with Exhibit B. * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License. * * The Original Code is OpenEMM. * The Original Developer is the Initial Developer. * The Initial Developer of the Original Code is AGNITAS AG. All portions of * the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights * Reserved. * * Contributor(s): AGNITAS AG. ********************************************************************************/ package org.agnitas.web; import java.io.IOException; import java.util.Date; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledThreadPoolExecutor; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.agnitas.beans.Admin; import org.agnitas.beans.AdminEntry; import org.agnitas.beans.AdminGroup; import org.agnitas.beans.impl.AdminImpl; import org.agnitas.beans.impl.PaginatedListImpl; import org.agnitas.dao.AdminDao; import org.agnitas.dao.AdminGroupDao; import org.agnitas.dao.CompanyDao; import org.agnitas.service.AdminListQueryWorker; import org.agnitas.util.AgnUtils; import org.agnitas.web.forms.AdminForm; import org.agnitas.web.forms.StrutsFormBase; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; /** * Implementation of <strong>Action</strong> that handles Account Admins * * @author Andreas Rehak, Martin Helff */ public class AdminAction extends StrutsActionBase { private static final transient Logger logger = Logger.getLogger(AdminAction.class); public static final int ACTION_VIEW_RIGHTS = ACTION_LAST + 1; public static final int ACTION_SAVE_RIGHTS = ACTION_LAST + 2; public static final int ACTION_VIEW_WITHOUT_LOAD = ACTION_LAST + 3; private static final String FUTURE_TASK = "GET_ADMIN_LIST"; protected AdminDao adminDao; protected AdminGroupDao adminGroupDao; protected CompanyDao companyDao; protected ConcurrentHashMap<String, Future<PaginatedListImpl<AdminEntry>>> futureHolder; protected ScheduledThreadPoolExecutor executorService; // ---------------------------------------- Public Methods /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). * Return an <code>ActionForward</code> instance describing where and how * control should be forwarded, or <code>null</code> if the response has * already been completed. * <br> * ACTION_LIST: calls a FutureHolder to get the list of entries.<br> * While FutureHolder is running, destination is "loading". <br> * After FutureHolder is finished destination is "list". * <br><br> * ACTION_SAVE: checks, if admin username was changed to one that is used for some another admin of the same company<br> * If the username is ok, saves admin data * <br><br> * ACTION_VIEW: loads data of chosen admin into form and forwards to admin view page * <br><br> * ACTION_VIEW_RIGHTS: loads list of permissions for given admin and forwards to user right list page * <br><br> * ACTION_SAVE_RIGHTS: saves permissions for certain admin and forwards to user right list page * <br><br> * ACTION_NEW: creates new admin db entry if the password field is not empty (after trimming the password string)<br> * and there is no another admin with the same username;<br> * saves permissions for the new admin;<br> * forwards to admin list page * <br><br> * ACTION_VIEW_WITHOUT_LOAD: is used after failing form validation<br> * for loading essential data into request before returning to the view page;<br> * does not reload form data * <br><br> * ACTION_CONFIRM_DELETE: only forwards to jsp with question to confirm deletion. * <br><br> * ACITON_DELETE: deletes the entry of certain admin, deletes userrights of given admin, forwards to admin list page. * <br><br> * Any other ACTION_* would cause a forward to "list" * <br><br> * @param mapping * The ActionMapping used to select this instance * @param form ActionForm object, data for the action filled by the jsp * @param req HTTP request from jsp * @param res HTTP response * @exception IOException * if an input/output error occurs * @exception ServletException * if a servlet exception occurs * @return destination specified in struts-config.xml to forward to next jsp */ public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { AdminForm aForm = null; ActionMessages errors = new ActionMessages(); ActionMessages messages = new ActionMessages(); ActionForward destination = null; if (!AgnUtils.isUserLoggedIn(req)) { return mapping.findForward("logon"); } if (form != null) { aForm = (AdminForm) form; } else { aForm = new AdminForm(); } if (logger.isInfoEnabled()) logger.info("Action: " + aForm.getAction()); if (req.getParameter("delete") != null && req.getParameter("delete").equals("delete")) { aForm.setAction(ACTION_CONFIRM_DELETE); } try { switch (aForm.getAction()) { case AdminAction.ACTION_LIST: if (allowed("admin.show", req)) { destination = prepareList(mapping, req, errors, destination, aForm); } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } break; case AdminAction.ACTION_VIEW: if (allowed("admin.show", req)) { if (aForm.getAdminID() != 0) { aForm.setAction(AdminAction.ACTION_SAVE); loadAdmin(aForm, req); } else { aForm.setAction(AdminAction.ACTION_NEW); } destination = mapping.findForward("view"); } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } break; case AdminAction.ACTION_SAVE: if (allowed("admin.change", req)) { if (AgnUtils.parameterNotEmpty(req, "save")) { if (!adminUsernameChangedToExisting(aForm)) { saveAdmin(aForm, req); // Show "changes saved" messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("default.changes_saved")); } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.username.duplicate")); } } } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } destination = mapping.findForward("view"); break; case AdminAction.ACTION_VIEW_RIGHTS: loadAdmin(aForm, req); aForm.setAction(AdminAction.ACTION_SAVE_RIGHTS); destination = mapping.findForward("rights"); break; case AdminAction.ACTION_SAVE_RIGHTS: saveAdminRights(aForm, req); loadAdmin(aForm, req); aForm.setAction(AdminAction.ACTION_SAVE_RIGHTS); destination = mapping.findForward("rights"); // Show "changes saved" messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("default.changes_saved")); break; case AdminAction.ACTION_NEW: if (allowed("admin.new", req)) { if (AgnUtils.parameterNotEmpty(req, "save")) { aForm.setAdminID(0); if (aForm.getPassword().length() > 0) { if (!adminExists(aForm)) { try { saveAdmin(aForm, req); // Show "changes saved" messages.add(ActionMessages.GLOBAL_MESSAGE,new ActionMessage("default.changes_saved")); destination = prepareList(mapping, req, errors, destination, aForm); aForm.setAction(AdminAction.ACTION_LIST); } catch (Exception e) { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.admin.save")); destination = mapping.findForward("view"); aForm.setAction(AdminAction.ACTION_NEW); } } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.username.duplicate")); destination = mapping.findForward("view"); aForm.setAction(ACTION_NEW); } } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.admin.no_password")); destination = mapping.findForward("view"); aForm.setAction(AdminAction.ACTION_NEW); } } } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } break; case AdminAction.ACTION_CONFIRM_DELETE: loadAdmin(aForm, req); aForm.setAction(AdminAction.ACTION_DELETE); destination = mapping.findForward("delete"); break; case AdminAction.ACTION_DELETE: if (req.getParameter("kill") != null) { if (allowed("admin.delete", req)) { deleteAdmin(aForm, req); // Show "changes saved" messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("default.changes_saved")); } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } aForm.setAction(AdminAction.ACTION_LIST); destination = prepareList(mapping, req, errors, destination, aForm); } break; case AdminAction.ACTION_VIEW_WITHOUT_LOAD: if (allowed("admin.show", req)) { req.setAttribute("adminGroups", adminGroupDao.getAdminGroupsByCompanyId(AgnUtils.getAdmin(req).getCompanyID())); if (aForm.getAdminID() != 0) { aForm.setAction(AdminAction.ACTION_SAVE); } else { aForm.setAction(AdminAction.ACTION_NEW); } destination = mapping.findForward("view"); } else { errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.permissionDenied")); } break; default: aForm.setAction(AdminAction.ACTION_LIST); destination = prepareList(mapping, req, errors, destination, aForm); } } catch (Exception e) { logger.error("execute: " + e + "\n" + AgnUtils.getStackTrace(e)); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.exception")); throw new ServletException(e); } if (destination != null && "view".equals(destination.getName())) { req.setAttribute("adminGroups", adminGroupDao.getAdminGroupsByCompanyId(AgnUtils.getAdmin(req).getCompanyID())); } // Report any errors we have discovered back to the original form if (!errors.isEmpty()) { saveErrors(req, errors); } // Report any message (non-errors) we have discovered if (!messages.isEmpty()) { saveMessages(req, messages); } return destination; } /** * Load an admin account. Loads the data of the admin from the database and * stores it in the form. * * @param aForm * the formular passed from the jsp * @param req * the Servlet Request (needed to get the company id) */ protected void loadAdmin(AdminForm aForm, HttpServletRequest req) { int adminID = aForm.getAdminID(); int compID = AgnUtils.getAdmin(req).getCompany().getId(); Admin admin = adminDao.getAdmin(adminID, compID); if (admin != null) { aForm.setUsername(admin.getUsername()); aForm.setPassword(""); aForm.setPasswordConfirm(""); aForm.setCompanyID(admin.getCompany().getId()); aForm.setFullname(admin.getFullname()); aForm.setAdminLocale(new Locale(admin.getAdminLang(), admin.getAdminCountry())); aForm.setAdminTimezone(admin.getAdminTimezone()); aForm.setLayoutID(admin.getLayoutID()); aForm.setGroupID(admin.getGroup().getGroupID()); aForm.setUserRights(admin.getAdminPermissions()); aForm.setGroupRights(admin.getGroup().getGroupPermissions()); aForm.setNumberofRows(admin.getPreferredListSize()); if (logger.isInfoEnabled()) logger.info("loadAdmin: admin " + aForm.getAdminID()+ " loaded"); } else { aForm.setAdminID(0); aForm.setCompanyID(AgnUtils.getAdmin(req).getCompany().getId()); logger.warn("loadAdmin: admin " + aForm.getAdminID() + " could not be loaded"); } } /** * Save an admin account. Gets the admin data from a form and stores it in * the database. * * @param aForm * the formular passed from the jsp * @param req * the Servlet Request (needed to get the company id) */ protected void saveAdmin(AdminForm aForm, HttpServletRequest req) { int adminID = aForm.getAdminID(); int compID = aForm.getCompanyID(); int groupID = aForm.getGroupID(); Admin admin = adminDao.getAdmin(adminID, compID); boolean isNew = false; if (admin == null) { admin = new AdminImpl(); admin.setCompanyID(compID); admin.setCompany(companyDao.getCompany(compID)); admin.setLayoutID(0); isNew = true; } AdminGroup group = (AdminGroup) adminGroupDao.getAdminGroup(groupID); admin.setAdminID(aForm.getAdminID()); if (!isNew && passwordChanged(admin.getUsername(), aForm.getPassword())) { admin.setLastPasswordChange(new Date()); } admin.setUsername(aForm.getUsername()); if (aForm.getPassword() != null && aForm.getPassword().trim().length() != 0) { admin.setPassword(aForm.getPassword()); } if (aForm.getPassword().length() > 0) { logger.error("Username: " + aForm.getUsername() + " PasswordLength: " + aForm.getPassword().length()); } else { logger.error("Username: " + aForm.getUsername()); } admin.setFullname(aForm.getFullname()); admin.setAdminCountry(aForm.getAdminLocale().getCountry()); admin.setAdminLang(aForm.getAdminLocale().getLanguage()); admin.setAdminTimezone(aForm.getAdminTimezone()); admin.setGroup(group); admin.setPreferredListSize(aForm.getNumberofRows()); adminDao.save(admin); if (isNew) { AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": create user " + admin.getUsername()); } else { AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": edit user " + aForm.getUsername()); } if (logger.isInfoEnabled()) logger.info("saveAdmin: admin " + aForm.getAdminID()); } protected boolean passwordChanged(String username, String password) { Admin admin = adminDao.getAdminByLogin(username, password); if (StringUtils.isEmpty(password) || (admin != null && admin.getAdminID() > 0)) { return false; } else { return true; } } /** * Save the permission for an admin. Gets the permissions for the admin from * the form and stores it in the database. * * @param aForm * the formular passed from the jsp * @param req * the Servlet Request (needed to get the company id) */ protected void saveAdminRights(AdminForm aForm, HttpServletRequest req) { adminDao.saveAdminRights(aForm.getAdminID(), aForm.getUserRights()); if (logger.isInfoEnabled()) logger.info("saveAdminRights: permissions changed"); } /** * Deletes an admin from the database. Also deletes all the admin permissions. * * @param aForm * the formular passed from the jsp * @param req * the Servlet Request (needed to get the company id) */ protected void deleteAdmin(AdminForm aForm, HttpServletRequest req) { int adminID = aForm.getAdminID(); int companyID = AgnUtils.getAdmin(req).getCompany().getId(); Admin admin = adminDao.getAdmin(adminID, companyID); String username = admin != null ? admin.getUsername() : aForm.getUsername(); adminDao.delete(admin); if (logger.isInfoEnabled()) logger.info("Admin " + adminID + " deleted"); AgnUtils.userlogger().info(AgnUtils.getAdmin(req).getUsername() + ": delete user " + username); } /** * Creates Future object contains list of admins. * @param mapping the ActionMapping used to select this instance * @param req HTTP request * @param errors ActionMessages object contains error messages, could be changed inside the method * @param destination specified in struts-config.xml to forward to next jsp * @param adminForm AdminForm object * @return action forward for displaying page with admin list or loading admin page, if the list is not prepared yet */ protected ActionForward prepareList(ActionMapping mapping, HttpServletRequest req, ActionMessages errors, ActionForward destination, AdminForm adminForm) { ActionMessages messages = null; try { setNumberOfRows(req, adminForm); destination = mapping.findForward("loading"); String key = FUTURE_TASK + "@" + req.getSession(false).getId(); if (!futureHolder.containsKey(key)) { Future<PaginatedListImpl<AdminEntry>> adminFuture = getAdminlistFuture(adminDao, req, adminForm); futureHolder.put(key, adminFuture); } if (futureHolder.containsKey(key) && futureHolder.get(key).isDone()) { req.setAttribute("adminEntries", futureHolder.get(key).get()); destination = mapping.findForward("list"); futureHolder.remove(key); adminForm.setRefreshMillis(RecipientForm.DEFAULT_REFRESH_MILLIS); messages = adminForm.getMessages(); if (messages != null && !messages.isEmpty()) { saveMessages(req, messages); adminForm.setMessages(null); } } else { // raise the refresh time if (adminForm.getRefreshMillis() < 1000) { adminForm.setRefreshMillis(adminForm.getRefreshMillis() + 50); } adminForm.setError(false); } } catch (Exception e) { logger.error("admin: " + e + "\n" + AgnUtils.getStackTrace(e)); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.exception")); // do not refresh when an error has been occurred adminForm.setError(true); } return destination; } /** * Gets list of admins from database according to given sorting parameters. * @param adminDao AdminDao object * @param request HTTP request * @param aForm AdminForm object * @return Future object contains admin list * @throws NumberFormatException * @throws IllegalAccessException * @throws InstantiationException * @throws InterruptedException * @throws ExecutionException */ protected Future<PaginatedListImpl<AdminEntry>> getAdminlistFuture(AdminDao adminDao, HttpServletRequest request, StrutsFormBase aForm) throws NumberFormatException, IllegalAccessException, InstantiationException, InterruptedException, ExecutionException { int rownums = aForm.getNumberofRows(); String direction = request.getParameter("dir"); if (direction == null) { direction = request.getSession().getAttribute("admin_dir") == null ? "" : (String) request.getSession().getAttribute("admin_dir"); } else { request.getSession().setAttribute("admin_dir", direction); } String sort = request.getParameter("sort"); if (sort == null) { sort = request.getSession().getAttribute("admin_sort") == null ? "" : (String) request.getSession().getAttribute("admin_sort"); } else { request.getSession().setAttribute("admin_sort", sort); } String pageStr = request.getParameter("page"); if (pageStr == null || "".equals(pageStr.trim())) { pageStr = request.getSession().getAttribute("admin_page") == null ? "1" : (String) request.getSession().getAttribute("admin_page"); } else { request.getSession().setAttribute("admin_page", pageStr); } if (aForm.isNumberOfRowsChanged()) { aForm.setPage("1"); request.getSession().setAttribute("admin_page", "1"); aForm.setNumberOfRowsChanged(false); pageStr = "1"; } int companyID = AgnUtils.getCompanyID(request); Future<PaginatedListImpl<AdminEntry>> future = executorService.submit(new AdminListQueryWorker(adminDao, companyID, sort, direction, Integer.parseInt(pageStr), rownums)); return future; } /** * Method checks if admin with entered username already exists in system. * * @param aForm * form * @return true if admin already exists, false otherwise */ protected boolean adminExists(AdminForm aForm) { return adminDao.adminExists(aForm.getCompanyID(), aForm.getUsername()); } /** * Method checks if username was changed to existing one. * * @param aForm * the form * @return true if username was changed to existing one; false - if the * username was changed to none-existing or if the username was not * changed at all */ protected boolean adminUsernameChangedToExisting(AdminForm aForm) { Admin currentAdmin = adminDao.getAdmin(aForm.getAdminID(), aForm.getCompanyID()); if (currentAdmin.getUsername().equals(aForm.getUsername())) { return false; } else { return adminDao.adminExists(aForm.getCompanyID(), aForm.getUsername()); } } public void setAdminDao(AdminDao adminDao) { this.adminDao = adminDao; } public void setAdminGroupDao(AdminGroupDao adminGroupDao) { this.adminGroupDao = adminGroupDao; } public void setCompanyDao(CompanyDao companyDao) { this.companyDao = companyDao; } public void setFutureHolder(ConcurrentHashMap<String, Future<PaginatedListImpl<AdminEntry>>> futureHolder) { this.futureHolder = futureHolder; } public void setExecutorService(ScheduledThreadPoolExecutor executorService) { this.executorService = executorService; } }