/* Copyright 2008-2015 Josh Drummond This file is part of WebPasswordSafe. WebPasswordSafe is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. WebPasswordSafe is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with WebPasswordSafe; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package net.webpasswordsafe.client; import java.util.List; import java.util.Map; import java.util.logging.Logger; import net.webpasswordsafe.client.i18n.TextMessages; import net.webpasswordsafe.client.remote.LoginService; import net.webpasswordsafe.client.remote.PasswordService; import net.webpasswordsafe.client.remote.ServiceHelper; import net.webpasswordsafe.client.remote.UserService; import net.webpasswordsafe.client.ui.*; import net.webpasswordsafe.common.dto.SystemSettings; import net.webpasswordsafe.common.model.AccessLevel; import net.webpasswordsafe.common.model.Group; import net.webpasswordsafe.common.model.Password; import net.webpasswordsafe.common.model.Permission; import net.webpasswordsafe.common.model.Template; import net.webpasswordsafe.common.model.User; import net.webpasswordsafe.common.util.Constants; import net.webpasswordsafe.common.util.Constants.Function; import com.extjs.gxt.ui.client.Style.Orientation; import com.extjs.gxt.ui.client.event.MenuEvent; import com.extjs.gxt.ui.client.event.SelectionListener; import com.extjs.gxt.ui.client.util.Format; import com.extjs.gxt.ui.client.util.Margins; import com.extjs.gxt.ui.client.util.Padding; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.Info; import com.extjs.gxt.ui.client.widget.MessageBox; import com.extjs.gxt.ui.client.widget.Text; import com.extjs.gxt.ui.client.widget.Viewport; import com.extjs.gxt.ui.client.widget.layout.FillLayout; import com.extjs.gxt.ui.client.widget.layout.HBoxLayout; import com.extjs.gxt.ui.client.widget.layout.HBoxLayoutData; import com.extjs.gxt.ui.client.widget.layout.RowData; import com.extjs.gxt.ui.client.widget.layout.RowLayout; import com.extjs.gxt.ui.client.widget.layout.HBoxLayout.HBoxLayoutAlign; import com.extjs.gxt.ui.client.widget.menu.Menu; import com.extjs.gxt.ui.client.widget.menu.MenuBar; import com.extjs.gxt.ui.client.widget.menu.MenuBarItem; import com.extjs.gxt.ui.client.widget.menu.MenuItem; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; import com.google.gwt.http.client.URL; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.ServiceDefTarget; import com.google.gwt.user.client.rpc.XsrfToken; import com.google.gwt.user.client.rpc.XsrfTokenService; import com.google.gwt.user.client.rpc.XsrfTokenServiceAsync; import com.google.gwt.user.client.ui.RootPanel; /** * Entry point classes define <code>onModuleLoad()</code>. * * @author Josh Drummond * */ public class WebPasswordSafe implements EntryPoint, MainWindow, LoginWindow { private Logger log = Logger.getLogger(WebPasswordSafe.class.getName()); private ClientSessionUtil clientSessionUtil = ClientSessionUtil.getInstance(); private final static TextMessages textMessages = GWT.create(TextMessages.class); private Viewport viewport; private ContentPanel mainPanel, topPanel, menuPanel; private PasswordSearchPanel passwordSearchPanel; public void onModuleLoad() { // menu panel menuPanel = new ContentPanel(new FillLayout()); menuPanel.setHeaderVisible(false); menuPanel.setBorders(false); refreshMenu(); viewport = new Viewport(); viewport.setLayout(new RowLayout(Orientation.VERTICAL)); // title panel topPanel = new ContentPanel(); HBoxLayout titleLayout = new HBoxLayout(); titleLayout.setPadding(new Padding(5)); titleLayout.setHBoxLayoutAlign(HBoxLayoutAlign.TOP); topPanel.setLayout(titleLayout); topPanel.setHeight(25); topPanel.setHeaderVisible(false); topPanel.setBorders(false); refreshTopPanel(); viewport.add(topPanel, new RowData(1, -1)); //menu panel viewport.add(menuPanel, new RowData(1, -1)); //main panel mainPanel = new ContentPanel(new FillLayout()); mainPanel.setHeaderVisible(false); viewport.add(mainPanel, new RowData(1, 1)); RootPanel.get().add(viewport); log.info(textMessages.webpasswordsafeTitle(Constants.VERSION)); pingServer(this); } private void refreshTopPanel() { topPanel.removeAll(); Text headerGwtLabel = new Text(textMessages.webpasswordsafeTitle(Constants.VERSION)); Text loggedInLabel = null; if (clientSessionUtil.isLoggedIn()) { loggedInLabel = new Text(textMessages.loggedInAs(Format.htmlEncode(clientSessionUtil.getLoggedInUser().getFullname()))); } else { loggedInLabel = new Text(textMessages.notLoggedIn()); } topPanel.add(headerGwtLabel, new HBoxLayoutData(0, 5, 0, 0)); HBoxLayoutData flex = new HBoxLayoutData(new Margins(0, 5, 0, 0)); flex.setFlex(1); topPanel.add(new Text(), flex); topPanel.add(loggedInLabel, new HBoxLayoutData(new Margins(0))); topPanel.layout(); } private void doChangePassword() { new ChangePasswordDialog().show(); } private void doTwoStepVerificationSettings() { new TwoStepVerificationDialog().show(); } private void refreshPasswordSearch() { mainPanel.removeAll(); if (clientSessionUtil.isLoggedIn()) { passwordSearchPanel = new PasswordSearchPanel(); passwordSearchPanel.setSize("100%", "100%"); mainPanel.add(passwordSearchPanel); } mainPanel.layout(); } private void refreshMenu() { MenuBar mainMenu = new MenuBar(); buildUserMenu(mainMenu); buildPasswordMenu(mainMenu); buildAdminMenu(mainMenu); buildReportsMenu(mainMenu); buildAboutMenu(mainMenu); menuPanel.removeAll(); menuPanel.add(mainMenu); menuPanel.layout(); } private void buildUserMenu(MenuBar mainMenu) { Menu userMenu = new Menu(); MenuItem userSettings = new MenuItem(textMessages.settings()); Menu userSettingsMenu = new Menu(); //userSettingsMenu.add(new MenuItem("General")); userSettingsMenu.add(new MenuItem(textMessages.changePassword(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doChangePassword(); } })); userSettingsMenu.add(new MenuItem(textMessages.twoStepVerification(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doTwoStepVerificationSettings(); } })); userSettings.setSubMenu(userSettingsMenu); userMenu.add(userSettings); // MenuItem userRole = new MenuItem("Role"); // Menu userRoleMenu = new Menu(); // userRoleMenu.add(new MenuItem("User")); // userRoleMenu.add(new MenuItem("Admin")); // userRole.setSubMenu(userRoleMenu); // userMenu.add(userRole); userMenu.add(new MenuItem(textMessages.logout(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doLogout(); } })); mainMenu.add(new MenuBarItem(textMessages.user(), userMenu)); } private void buildPasswordMenu(MenuBar mainMenu) { Menu passwordMenu = new Menu(); if (clientSessionUtil.isAuthorized(Function.ADD_PASSWORD)) { passwordMenu.add(new MenuItem(textMessages.add(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doNewPassword(); } })); } MenuItem passwordSearch = new MenuItem(textMessages.search()); Menu passwordSearchMenu = new Menu(); passwordSearchMenu.add(new MenuItem(textMessages.openSelectedPassword(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { if (null != passwordSearchPanel) { passwordSearchPanel.openSelectedPassword(); } } })); passwordSearchMenu.add(new MenuItem(textMessages.getSelectedPasswordValue(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { if (null != passwordSearchPanel) { passwordSearchPanel.getSelectedCurrentPasswordData(); } } })); passwordSearchMenu.add(new MenuItem(textMessages.refreshSearch(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { refreshPasswordSearch(); } })); passwordSearch.setSubMenu(passwordSearchMenu); passwordMenu.add(passwordSearch); MenuItem passwordTemplate = new MenuItem(textMessages.template()); Menu passwordTemplateMenu = new Menu(); if (clientSessionUtil.isAuthorized(Function.ADD_TEMPLATE)) { passwordTemplateMenu.add(new MenuItem(textMessages.add(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doAddTemplate(); } })); } if (clientSessionUtil.isAuthorized(Function.UPDATE_TEMPLATE)) { passwordTemplateMenu.add(new MenuItem(textMessages.edit(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doEditTemplate(); } })); } passwordTemplate.setSubMenu(passwordTemplateMenu); passwordMenu.add(passwordTemplate); mainMenu.add(new MenuBarItem(textMessages.password(), passwordMenu)); } private void buildAdminMenu(MenuBar mainMenu) { // only create admin menu item if submenus allowed if (clientSessionUtil.isAuthorized(Function.ADD_USER) || clientSessionUtil.isAuthorized(Function.UPDATE_USER) || clientSessionUtil.isAuthorized(Function.ADD_GROUP) || clientSessionUtil.isAuthorized(Function.UPDATE_GROUP) || clientSessionUtil.isAuthorized(Function.UNBLOCK_IP)) { Menu adminMenu = new Menu(); //adminMenu.add(new MenuItem("Settings")); MenuItem adminUser = new MenuItem(textMessages.users()); Menu adminUserMenu = new Menu(); if (clientSessionUtil.isAuthorized(Function.ADD_USER)) { adminUserMenu.add(new MenuItem(textMessages.add(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doAddUser(); } })); } if (clientSessionUtil.isAuthorized(Function.UPDATE_USER)) { adminUserMenu.add(new MenuItem(textMessages.edit(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doEditUser(); } })); } adminUser.setSubMenu(adminUserMenu); adminMenu.add(adminUser); MenuItem adminGroup = new MenuItem(textMessages.groups()); Menu adminGroupMenu = new Menu(); if (clientSessionUtil.isAuthorized(Function.ADD_GROUP)) { adminGroupMenu.add(new MenuItem(textMessages.add(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doAddGroup(); } })); } if (clientSessionUtil.isAuthorized(Function.UPDATE_GROUP)) { adminGroupMenu.add(new MenuItem(textMessages.edit(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doEditGroup(); } })); } adminGroup.setSubMenu(adminGroupMenu); adminMenu.add(adminGroup); MenuItem adminTools = new MenuItem(textMessages.tools()); Menu adminToolsMenu = new Menu(); if (clientSessionUtil.isAuthorized(Function.UNBLOCK_IP)) { adminToolsMenu.add(new MenuItem(textMessages.unblockIP(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doUnblockIP(); } })); } adminTools.setSubMenu(adminToolsMenu); adminMenu.add(adminTools); mainMenu.add(new MenuBarItem(textMessages.admin(), adminMenu)); } // MenuItem adminRole = new MenuItem("Roles"); // adminMenu.add(adminRole); } private void buildReportsMenu(MenuBar mainMenu) { Menu reportsMenu = new Menu(); List<Map<String, Object>> reports = getClientModel().getAvailableReports(); for (final Map<String, Object> report : reports) { MenuItem menuItem = new MenuItem((String)report.get(Constants.I18N), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { displayReportDialog(report); } }); reportsMenu.add(menuItem); } mainMenu.add(new MenuBarItem(textMessages.reports(), reportsMenu)); } private void displayReportDialog(Map<String, Object> report) { new ReportDialog(report).show(); } private void buildAboutMenu(MenuBar mainMenu) { Menu aboutMenu = new Menu(); aboutMenu.add(new MenuItem(textMessages.help(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doShowHelp(); } })); aboutMenu.add(new MenuItem(textMessages.about(), new SelectionListener<MenuEvent>() { @Override public void componentSelected(MenuEvent ce) { doShowAbout(); } })); mainMenu.add(new MenuBarItem(textMessages.about(), aboutMenu)); } private void doNewPassword() { if (clientSessionUtil.isAuthorized(Function.ADD_PASSWORD)) { Password newPassword = new Password(); newPassword.setMaxEffectiveAccessLevel(AccessLevel.GRANT); newPassword.addPermission(new Permission(clientSessionUtil.getLoggedInUser(), AccessLevel.GRANT)); new PasswordDialog(newPassword, passwordSearchPanel).show(); } else { MessageBox.alert(textMessages.error(), textMessages.notAuthorized(), null); } } private void doEditGroup() { if (clientSessionUtil.isAuthorized(Function.UPDATE_GROUP)) { AsyncCallback<List<Group>> callback = new AsyncCallback<List<Group>>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(List<Group> result) { new GroupSelectionDialog(new EditGroupListener(), result, false).show(); } }; UserService.Util.getInstance().getGroups(false, callback); } else { MessageBox.alert(textMessages.error(), textMessages.notAuthorized(), null); } } private void doAddGroup() { if (clientSessionUtil.isAuthorized(Function.ADD_GROUP)) { displayGroupDialog(new Group()); } else { MessageBox.alert(textMessages.error(), textMessages.notAuthorized(), null); } } private void doUnblockIP() { if (clientSessionUtil.isAuthorized(Function.UNBLOCK_IP)) { displayIPUnblockDialog(); } else { MessageBox.alert(textMessages.error(), textMessages.notAuthorized(), null); } } private void displayIPUnblockDialog() { new IPUnblockDialog().show(); } private void displayGroupDialog(Group group) { new GroupDialog(group).show(); } @Override public void refreshLoginStatus() { refreshTopPanel(); refreshMenu(); refreshPasswordSearch(); } private void doAddUser() { if (clientSessionUtil.isAuthorized(Function.ADD_USER)) { displayUserDialog(new User()); } else { MessageBox.alert(textMessages.error(), textMessages.notAuthorized(), null); } } private void doEditUser() { if (clientSessionUtil.isAuthorized(Function.UPDATE_USER)) { AsyncCallback<List<User>> callback = new AsyncCallback<List<User>>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(List<User> result) { new UserSelectionDialog(new EditUserListener(), result, false).show(); } }; UserService.Util.getInstance().getUsers(false, callback); } else { MessageBox.alert(textMessages.error(), textMessages.notAuthorized(), null); } } private void doAddTemplate() { if (clientSessionUtil.isAuthorized(Function.ADD_TEMPLATE)) { displayTemplateDialog(new Template()); } else { MessageBox.alert(textMessages.error(), textMessages.notAuthorized(), null); } } private void doEditTemplate() { if (clientSessionUtil.isAuthorized(Function.UPDATE_TEMPLATE)) { AsyncCallback<List<Template>> callback = new AsyncCallback<List<Template>>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(List<Template> result) { new TemplateSelectionDialog(new EditTemplateListener(), result, false).show(); } }; PasswordService.Util.getInstance().getTemplates(true, callback); } else { MessageBox.alert(textMessages.error(), textMessages.notAuthorized(), null); } } private void displayTemplateDialog(Template template) { new TemplateDialog(template).show(); } private void pingServer(final LoginWindow loginWindow) { AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(Boolean result) { initXsrfProtection(loginWindow); } }; LoginService.Util.getInstance().ping(callback); } private void initXsrfProtection(final LoginWindow loginWindow) { XsrfTokenServiceAsync xsrf = (XsrfTokenServiceAsync)GWT.create(XsrfTokenService.class); ((ServiceDefTarget)xsrf).setServiceEntryPoint(GWT.getModuleBaseURL() + "xsrf"); xsrf.getNewXsrfToken(new AsyncCallback<XsrfToken>() { @Override public void onSuccess(XsrfToken token) { ServiceHelper.setXsrfToken(token); getSystemSettings(loginWindow); } @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } }); } @Override public void doGetLoggedInUser(final LoginWindow loginWindow) { AsyncCallback<User> callback = new AsyncCallback<User>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(User result) { if (null != result) { getClientModel().setLoggedInUser(result); getClientModel().setLoggedIn(true); getLoginAuthorizations(loginWindow); } else { loginWindow.doGetLoginFailure(); } } }; LoginService.Util.getInstance().getLogin(callback); } private void getLoginAuthorizations(final LoginWindow loginWindow) { AsyncCallback<Map<Function, Boolean>> callback = new AsyncCallback<Map<Function, Boolean>>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(Map<Function, Boolean> result) { getClientModel().setAuthorizations(result); getLoginReports(loginWindow); } }; LoginService.Util.getInstance().getLoginAuthorizations(null, callback); } private void getLoginReports(final LoginWindow loginWindow) { AsyncCallback<List<Map<String, Object>>> callback = new AsyncCallback<List<Map<String, Object>>>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(List<Map<String, Object>> result) { getClientModel().setAvailableReports(result); refreshLoginStatus(); loginWindow.doGetLoginSuccess(); } }; LoginService.Util.getInstance().getLoginReports(callback); } private void getSystemSettings(final LoginWindow loginWindow) { AsyncCallback<SystemSettings> callback = new AsyncCallback<SystemSettings>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(SystemSettings result) { clientSessionUtil.setSystemSettings(result); doGetLoggedInUser(loginWindow); } }; LoginService.Util.getInstance().getSystemSettings(callback); } private void doLogout() { AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(Boolean result) { if (result) { clientSessionUtil.getLoggedInUser().setUsername(""); clientSessionUtil.setLoggedIn(false); String logoutUrl = clientSessionUtil.getSystemSettings().getLogoutUrl(); if (logoutUrl.equals("")) { Window.Location.reload(); } else { logoutUrl = logoutUrl.replace("$1", URL.encodeQueryString(GWT.getHostPageBaseURL())); Window.Location.replace(logoutUrl); } } } }; LoginService.Util.getInstance().logout(callback); } private void displayLoginDialog() { new LoginDialog(this).show(); } private void displayUserDialog(User user) { new UserDialog(user).show(); } @Override public ClientSessionUtil getClientModel() { return clientSessionUtil; } @Override public void doGetLoginSuccess() { Info.display(textMessages.status(), textMessages.loggedIn(Format.htmlEncode(getClientModel().getLoggedInUser().getUsername()))); } @Override public void doGetLoginFailure() { String bypassSSO = Window.Location.getParameter("bypassSSO"); bypassSSO = (bypassSSO == null) ? "false" : bypassSSO; if (clientSessionUtil.getSystemSettings().isSsoEnabled() && !bypassSSO.equals("true")) { // sso enabled and not bypass, redirect to sso filtered page Window.Location.replace(GWT.getHostPageBaseURL()+"sso"); } else { displayLoginDialog(); } } private void doShowAbout() { new AboutDialog().show(); } private void doShowHelp() { Window.open(GWT.getHostPageBaseURL() + Constants.URL_HELP, "_blank", ""); } private class EditUserListener implements UserListener { @Override public void doUsersChosen(List<User> users) { if (users.size() > 0) { AsyncCallback<User> callback = new AsyncCallback<User>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(User result) { displayUserDialog(result); } }; UserService.Util.getInstance().getUserWithGroups(users.get(0).getId(), callback); } } } private class EditGroupListener implements GroupListener { @Override public void doGroupsChosen(List<Group> groups) { if (groups.size() > 0) { AsyncCallback<Group> callback = new AsyncCallback<Group>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(Group result) { displayGroupDialog(result); } }; UserService.Util.getInstance().getGroupWithUsers(groups.get(0).getId(), callback); } } } private class EditTemplateListener implements TemplateListener { @Override public void doTemplatesChosen(List<Template> templates) { if (templates.size() > 0) { AsyncCallback<Template> callback = new AsyncCallback<Template>() { @Override public void onFailure(Throwable caught) { WebPasswordSafe.handleServerFailure(caught); } @Override public void onSuccess(Template result) { displayTemplateDialog(result); } }; PasswordService.Util.getInstance().getTemplateWithDetails(templates.get(0).getId(), callback); } } } //// public static methods... public static void handleServerFailure(Throwable caught) { handleServerFailure(caught, false); } public static void handleServerFailure(Throwable caught, boolean showDetails) { String message = textMessages.sessionTimeout() + (showDetails ? "<br>"+caught.getMessage() : ""); MessageBox.alert(textMessages.error(), message, new ServerErrorListener()); } }