001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.login; 029 030import org.opencms.ade.configuration.CmsADEManager; 031import org.opencms.db.CmsLoginMessage; 032import org.opencms.db.CmsUserSettings; 033import org.opencms.file.CmsObject; 034import org.opencms.file.CmsProject; 035import org.opencms.file.CmsRequestContext; 036import org.opencms.file.CmsUser; 037import org.opencms.i18n.CmsMessageContainer; 038import org.opencms.i18n.CmsResourceBundleLoader; 039import org.opencms.jsp.CmsJspActionElement; 040import org.opencms.jsp.CmsJspLoginBean; 041import org.opencms.main.CmsBroadcast.ContentMode; 042import org.opencms.main.CmsException; 043import org.opencms.main.CmsLog; 044import org.opencms.main.OpenCms; 045import org.opencms.security.CmsCustomLoginException; 046import org.opencms.security.CmsRole; 047import org.opencms.security.CmsUserLog; 048import org.opencms.security.twofactor.CmsSecondFactorInfo; 049import org.opencms.security.twofactor.CmsTwoFactorAuthenticationHandler; 050import org.opencms.ui.A_CmsDialogContext; 051import org.opencms.ui.A_CmsUI; 052import org.opencms.ui.CmsVaadinUtils; 053import org.opencms.ui.I_CmsDialogContext; 054import org.opencms.ui.I_CmsDialogContext.ContextType; 055import org.opencms.ui.Messages; 056import org.opencms.ui.apps.CmsAppHierarchyConfiguration; 057import org.opencms.ui.apps.CmsAppWorkplaceUi; 058import org.opencms.ui.apps.CmsFileExplorerConfiguration; 059import org.opencms.ui.apps.CmsSitemapEditorConfiguration; 060import org.opencms.ui.components.CmsBasicDialog; 061import org.opencms.ui.components.CmsBasicDialog.DialogWidth; 062import org.opencms.ui.dialogs.CmsUserDataDialog; 063import org.opencms.ui.login.CmsLoginHelper.LoginParameters; 064import org.opencms.util.CmsStringUtil; 065import org.opencms.util.CmsUUID; 066import org.opencms.workplace.CmsLoginUserAgreement; 067import org.opencms.workplace.CmsWorkplace; 068import org.opencms.workplace.CmsWorkplaceLoginHandler; 069import org.opencms.workplace.CmsWorkplaceManager; 070import org.opencms.workplace.CmsWorkplaceSettings; 071 072import java.io.IOException; 073import java.text.SimpleDateFormat; 074import java.util.Collection; 075import java.util.Date; 076import java.util.List; 077import java.util.MissingResourceException; 078import java.util.ResourceBundle; 079 080import javax.servlet.http.HttpServletRequest; 081import javax.servlet.http.HttpServletResponse; 082import javax.servlet.http.HttpSession; 083 084import org.apache.commons.lang3.builder.ReflectionToStringBuilder; 085import org.apache.commons.lang3.builder.ToStringStyle; 086import org.apache.commons.logging.Log; 087 088import com.vaadin.server.Page; 089import com.vaadin.server.VaadinRequest; 090import com.vaadin.server.VaadinService; 091import com.vaadin.server.VaadinServletRequest; 092import com.vaadin.server.VaadinServletResponse; 093import com.vaadin.ui.Component; 094import com.vaadin.ui.UI; 095import com.vaadin.ui.Window; 096 097/** 098 * Controller class which actually handles the login dialog logic.<p> 099 */ 100public class CmsLoginController { 101 102 /** 103 * Represents the login target information.<p> 104 */ 105 public static class CmsLoginTargetInfo { 106 107 /** The password. */ 108 private String m_password; 109 110 /** The login target. */ 111 private String m_target; 112 113 /** The user. */ 114 private String m_user; 115 116 /** 117 * Creates a new instance.<p> 118 * 119 * @param target the login target 120 * @param user the user name 121 * @param password the password 122 */ 123 public CmsLoginTargetInfo(String target, String user, String password) { 124 125 super(); 126 m_target = target; 127 m_user = user; 128 m_password = password; 129 } 130 131 /** 132 * Returns the password.<p> 133 * 134 * @return the password 135 */ 136 public String getPassword() { 137 138 return m_password; 139 } 140 141 /** 142 * Returns the target.<p> 143 * 144 * @return the target 145 */ 146 public String getTarget() { 147 148 return m_target; 149 } 150 151 /** 152 * Returns the user.<p> 153 * 154 * @return the user 155 */ 156 public String getUser() { 157 158 return m_user; 159 } 160 } 161 162 /** 163 * The login context. 164 */ 165 public static class LoginContext { 166 167 /** The CMS context. */ 168 private CmsObject m_cms; 169 170 /** The second factor information. */ 171 private CmsSecondFactorInfo m_secondFactorInfo; 172 173 /** The user being logged in. */ 174 private CmsUser m_user; 175 176 /** 177 * Gets the CmsObject. 178 * 179 * @return the CmsObject 180 */ 181 public CmsObject getCms() { 182 183 return m_cms; 184 } 185 186 /** 187 * The second factor information for 2FA. 188 * 189 * @return the second factor information 190 */ 191 public CmsSecondFactorInfo getSecondFactorInfo() { 192 193 return m_secondFactorInfo; 194 } 195 196 /** 197 * Gets the user to be logged in. 198 * 199 * @return the user 200 */ 201 public CmsUser getUser() { 202 203 return m_user; 204 } 205 206 /** 207 * Sets the CMS context. 208 * 209 * @param cms the CMS context 210 */ 211 public void setCms(CmsObject cms) { 212 213 m_cms = cms; 214 } 215 216 /** 217 * Sets the second factor information for 2FA. 218 * 219 * @param secondFactorInfo the second factor information 220 */ 221 public void setSecondFactorInfo(CmsSecondFactorInfo secondFactorInfo) { 222 223 m_secondFactorInfo = secondFactorInfo; 224 } 225 226 /** 227 * Sets the user. 228 * 229 * @param user the user 230 */ 231 public void setUser(CmsUser user) { 232 233 m_user = user; 234 } 235 236 /** 237 * @see java.lang.Object#toString() 238 */ 239 @Override 240 public String toString() { 241 242 return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE); 243 } 244 }; 245 246 /** 247 * Helper subclass of CmsLoginUserAgreement which can be used without a page context.<p> 248 * 249 * This is only used for detecting whether we need to display the user agreement dialog, not for displaying the dialog itself.<p> 250 */ 251 protected static class UserAgreementHelper extends CmsLoginUserAgreement { 252 253 /** The replacement CMS context. */ 254 private CmsObject m_cms; 255 256 /** The replacemenet workplace settings. */ 257 private CmsWorkplaceSettings m_wpSettings; 258 259 /** 260 * Creates a new instance.<p> 261 * 262 * @param cms the replacement CMS context 263 * @param wpSettings the replacement workplace settings 264 */ 265 public UserAgreementHelper(CmsObject cms, CmsWorkplaceSettings wpSettings) { 266 267 super(null); 268 m_cms = cms; 269 m_wpSettings = wpSettings; 270 initAcceptData(); 271 } 272 273 /** 274 * @see org.opencms.workplace.CmsWorkplace#getCms() 275 */ 276 @Override 277 public CmsObject getCms() { 278 279 return m_cms; 280 } 281 282 /** 283 * @see org.opencms.workplace.CmsWorkplace#getSettings() 284 */ 285 @Override 286 public CmsWorkplaceSettings getSettings() { 287 288 return m_wpSettings; 289 } 290 291 /** 292 * @see org.opencms.workplace.CmsWorkplace#initWorkplaceMembers(org.opencms.jsp.CmsJspActionElement) 293 */ 294 @Override 295 protected void initWorkplaceMembers(CmsJspActionElement jsp) { 296 297 // do nothing 298 } 299 } 300 301 /** 302 * Helper interface for splitting the actual login off into its own object 303 * which can be called from multiple places. 304 */ 305 interface LoginContinuation { 306 307 /** 308 * Continues with the login process. 309 * 310 * @param data the login data 311 * @throws Exception if something goes wrong 312 */ 313 void continueLogin(LoginContext data) throws Exception; 314 } 315 316 /** Additional info key to mark accounts as locked due to inactivity. */ 317 public static final String KEY_ACCOUNT_LOCKED = "accountLocked"; 318 319 /** The logger for this class. */ 320 private static final Log LOG = CmsLog.getLog(CmsLoginController.class); 321 322 /** The two-factor authentication handler. */ 323 protected CmsTwoFactorAuthenticationHandler m_otpHandler = OpenCms.getTwoFactorAuthenticationHandler(); 324 325 /** The UI instance. */ 326 CmsLoginUI m_ui; 327 328 /** The administrator CMS context. */ 329 private CmsObject m_adminCms; 330 331 /** The parameters collected when the login app was opened. */ 332 private LoginParameters m_params; 333 334 /*** 335 * Creates a new instance.<p> 336 * 337 * @param adminCms the admin cms context 338 * @param params the parameters for the UI 339 */ 340 public CmsLoginController(CmsObject adminCms, LoginParameters params) { 341 342 m_params = params; 343 m_adminCms = adminCms; 344 } 345 346 /** 347 * Returns the link to the login form.<p> 348 * 349 * @param cms the current cms context 350 * 351 * @return the login form link 352 */ 353 public static String getFormLink(CmsObject cms) { 354 355 return OpenCms.getLinkManager().substituteLinkForUnknownTarget( 356 cms, 357 CmsWorkplaceLoginHandler.LOGIN_HANDLER, 358 false); 359 } 360 361 /** 362 * Gets the login target link.<p> 363 * 364 * @param currentCms the current CMS context 365 * @param settings the workplace settings 366 * @param requestedResource the requested resource parameter 367 * 368 * @return the login target 369 * 370 * @throws CmsException in case the user has insufficient permissions to access the login target 371 */ 372 public static String getLoginTarget(CmsObject currentCms, CmsWorkplaceSettings settings, String requestedResource) 373 throws CmsException { 374 375 String directEditPath = CmsLoginHelper.getDirectEditPath(currentCms, settings.getUserSettings(), false); 376 String target = ""; 377 boolean checkRole = false; 378 String fragment = UI.getCurrent() != null ? UI.getCurrent().getPage().getUriFragment() : ""; 379 boolean workplace2 = false; 380 381 if ((requestedResource == null) && (directEditPath != null)) { 382 target = directEditPath; 383 } else if ((requestedResource != null) && !CmsWorkplace.JSP_WORKPLACE_URI.equals(requestedResource)) { 384 target = requestedResource; 385 } else { 386 workplace2 = true; 387 target = CmsVaadinUtils.getWorkplaceLink(); 388 checkRole = true; 389 } 390 391 UserAgreementHelper userAgreementHelper = new UserAgreementHelper(currentCms, settings); 392 boolean showUserAgreement = userAgreementHelper.isShowUserAgreement(); 393 if (showUserAgreement) { 394 target = userAgreementHelper.getConfigurationVfsPath() 395 + "?" 396 + CmsLoginUserAgreement.PARAM_WPRES 397 + "=" 398 + target; 399 } 400 if (checkRole && !OpenCms.getRoleManager().hasRole(currentCms, CmsRole.WORKPLACE_USER)) { 401 workplace2 = false; 402 target = CmsLoginHelper.getDirectEditPath(currentCms, settings.getUserSettings(), true); 403 if (target == null) { 404 throw new CmsCustomLoginException( 405 org.opencms.workplace.Messages.get().container( 406 org.opencms.workplace.Messages.GUI_LOGIN_FAILED_NO_WORKPLACE_PERMISSIONS_0)); 407 } 408 } 409 if (!workplace2) { 410 target = OpenCms.getLinkManager().substituteLink(currentCms, target); 411 } 412 413 if (workplace2 && CmsStringUtil.isEmptyOrWhitespaceOnly(fragment)) { 414 if (settings.getUserSettings().getStartView().startsWith("/")) { 415 if (CmsWorkplace.VIEW_WORKPLACE.equals(settings.getUserSettings().getStartView())) { 416 fragment = CmsFileExplorerConfiguration.APP_ID; 417 } else if (CmsWorkplace.VIEW_ADMIN.equals(settings.getUserSettings().getStartView())) { 418 fragment = CmsAppHierarchyConfiguration.APP_ID; 419 } 420 } else { 421 fragment = settings.getUserSettings().getStartView(); 422 } 423 } 424 425 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(fragment)) { 426 if (CmsSitemapEditorConfiguration.APP_ID.equals(fragment)) { 427 target = OpenCms.getLinkManager().substituteLink(currentCms, CmsADEManager.PATH_SITEMAP_EDITOR_JSP) 428 + "?path=" 429 + settings.getUserSettings().getStartFolder(); 430 } else { 431 target += "#" + fragment; 432 } 433 } 434 return target; 435 } 436 437 /** 438 * Logs the current user out by invalidating the session an reloading the current URI.<p> 439 * Important: This works only within vaadin apps.<p> 440 */ 441 public static void logout() { 442 443 logout(false); 444 } 445 446 /** 447 * Logs the current user out by invalidating the session an reloading the current URI.<p> 448 * Important: This works only within vaadin apps.<p> 449 * 450 * @param autoLogout true if this logout is triggered as part of the auto-logout function 451 */ 452 public static void logout(boolean autoLogout) { 453 454 CmsObject cms = A_CmsUI.getCmsObject(); 455 if (UI.getCurrent() instanceof CmsAppWorkplaceUi) { 456 ((CmsAppWorkplaceUi)UI.getCurrent()).onWindowClose(); 457 } 458 String loggedInUser = cms.getRequestContext().getCurrentUser().getName(); 459 UI.getCurrent().getSession().close(); 460 String logoutUri = OpenCms.getLoginManager().getLogoutUri(); 461 if (logoutUri != null) { 462 String target = OpenCms.getLinkManager().substituteLinkForUnknownTarget(cms, logoutUri, false); 463 target = OpenCms.getAuthorizationHandler().getLogoutRedirectUri( 464 cms, 465 (VaadinServletRequest)VaadinRequest.getCurrent(), 466 target); 467 // open in top frame, so it still works when the Vaadin dialog is embedded 468 Page.getCurrent().open(target, "_top", false); 469 } else { 470 String suffix = ""; 471 if (autoLogout) { 472 suffix = "?" + CmsLoginHelper.PARAM_AUTOLOGOUT + "=true"; 473 } 474 String loginLink = OpenCms.getLinkManager().substituteLinkForUnknownTarget( 475 cms, 476 CmsWorkplaceLoginHandler.LOGIN_HANDLER + suffix, 477 false); 478 loginLink = OpenCms.getAuthorizationHandler().getLogoutRedirectUri( 479 cms, 480 (VaadinServletRequest)VaadinRequest.getCurrent(), 481 loginLink); 482 483 VaadinService.getCurrentRequest().getWrappedSession().invalidate(); 484 // open in top frame, so it still works when the Vaadin dialog is embedded 485 Page.getCurrent().open(loginLink, "_top", false); 486 // logout was successful 487 if (LOG.isInfoEnabled()) { 488 LOG.info( 489 org.opencms.jsp.Messages.get().getBundle().key( 490 org.opencms.jsp.Messages.LOG_LOGOUT_SUCCESFUL_3, 491 loggedInUser, 492 "{workplace logout option}", 493 cms.getRequestContext().getRemoteAddress())); 494 } 495 CmsUserLog.logLogout(cms); 496 } 497 } 498 499 /** 500 * Logs out the current user redirecting to the login form afterwards.<p> 501 * 502 * @param cms the cms context 503 * @param request the servlet request 504 * @param response the servlet response 505 * 506 * @throws IOException if writing to the response fails 507 */ 508 public static void logout(CmsObject cms, HttpServletRequest request, HttpServletResponse response) 509 throws IOException { 510 511 CmsUserLog.logLogout(cms); 512 String loggedInUser = cms.getRequestContext().getCurrentUser().getName(); 513 HttpSession session = request.getSession(false); 514 String logoutUri = OpenCms.getLoginManager().getLogoutUri(); 515 if (logoutUri == null) { 516 if (session != null) { 517 session.invalidate(); 518 /* we need this because a new session might be created after this method, 519 but before the session info is updated in OpenCmsCore.showResource. */ 520 cms.getRequestContext().setUpdateSessionEnabled(false); 521 } 522 // logout was successful 523 if (LOG.isInfoEnabled()) { 524 LOG.info( 525 org.opencms.jsp.Messages.get().getBundle().key( 526 org.opencms.jsp.Messages.LOG_LOGOUT_SUCCESFUL_3, 527 loggedInUser, 528 cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri()), 529 cms.getRequestContext().getRemoteAddress())); 530 } 531 String redirectUri = OpenCms.getAuthorizationHandler().getLogoutRedirectUri(cms, request, getFormLink(cms)); 532 response.sendRedirect(redirectUri); 533 } else { 534 String redirectUri = OpenCms.getAuthorizationHandler().getLogoutRedirectUri( 535 cms, 536 request, 537 OpenCms.getLinkManager().substituteLinkForUnknownTarget(cms, logoutUri, false)); 538 response.sendRedirect(redirectUri); 539 } 540 } 541 542 /** 543 * Gets the PC type.<p> 544 * 545 * @return the PC type 546 */ 547 public String getPcType() { 548 549 String result = m_params.getPcType(); 550 if (CmsStringUtil.isEmptyOrWhitespaceOnly(result)) { 551 result = "public"; 552 } 553 return result; 554 } 555 556 /** 557 * Returns the reset password link.<p> 558 * 559 * @return the reset password link 560 */ 561 public String getResetPasswordLink() { 562 563 String result = OpenCms.getLinkManager().substituteLinkForUnknownTarget( 564 CmsLoginUI.m_adminCms, 565 CmsWorkplaceLoginHandler.LOGIN_HANDLER, 566 false) + "?" + CmsLoginHelper.PARAM_RESET_PASSWORD; 567 String ou = m_ui.getOrgUnit(); 568 result += "&" + CmsLoginHelper.PARAM_OUFQN + "=" + ou; 569 return result; 570 } 571 572 /** 573 * Returns true if the security option should be displayed in the login dialog.<p> 574 * 575 * @return true if the security option should be displayed in the login dialog 576 */ 577 public boolean isShowSecure() { 578 579 return OpenCms.getLoginManager().isEnableSecurity(); 580 } 581 582 /** 583 * Called when the user clicks on the 'forgot password' button.<p> 584 */ 585 public void onClickForgotPassword() { 586 587 A_CmsUI.get().getPage().setLocation(getResetPasswordLink()); 588 } 589 590 /** 591 * Called when the user clicks on the login button.<p> 592 */ 593 public void onClickLogin() { 594 595 String user = m_ui.getUser(); 596 String password = m_ui.getPassword(); 597 String ou = m_ui.getOrgUnit(); 598 if (CmsLoginOuSelector.OU_NONE.equals(ou)) { 599 displayError( 600 CmsVaadinUtils.getMessageText(Messages.GUI_LOGIN_NO_OU_SELECTED_WARNING_0) + "\n\n", 601 false, 602 false); 603 return; 604 } 605 { 606 CmsMessageContainer message = CmsLoginHelper.validateUserAndPasswordNotEmpty(user, password); 607 if (message != null) { 608 String errorMessage = message.key(m_params.getLocale()); 609 displayError(errorMessage, true, false); 610 return; 611 } 612 } 613 614 String realUser = CmsStringUtil.joinPaths(ou, user); 615 String pcType = m_ui.getPcType(); 616 CmsObject currentCms = A_CmsUI.getCmsObject(); 617 CmsUser userObj = null; 618 619 try { 620 try { 621 userObj = currentCms.readUser(realUser); 622 } catch (CmsException e) { 623 LOG.warn(e.getLocalizedMessage(), e); 624 CmsMessageContainer message = org.opencms.workplace.Messages.get().container( 625 org.opencms.workplace.Messages.GUI_LOGIN_FAILED_0); 626 displayError(message.key(m_params.getLocale()), true, true); 627 CmsUserLog.logLoginFailure(currentCms, realUser); 628 return; 629 } 630 final CmsUser userNonNull = userObj; 631 if (OpenCms.getLoginManager().canLockBecauseOfInactivity(currentCms, userObj)) { 632 boolean locked = null != userObj.getAdditionalInfo().get(KEY_ACCOUNT_LOCKED); 633 if (locked) { 634 displayError(CmsInactiveUserMessages.getLockoutText(A_CmsUI.get().getLocale()), false, false); 635 CmsUserLog.logLoginFailure(currentCms, realUser); 636 return; 637 } 638 } 639 640 CmsObject cloneCms = OpenCms.initCmsObject(currentCms); 641 cloneCms.checkLoginUser(realUser, password); 642 643 String messageToChange = ""; 644 if (OpenCms.getLoginManager().isPasswordReset(currentCms, userObj)) { 645 messageToChange = CmsVaadinUtils.getMessageText(Messages.GUI_PWCHANGE_RESET_0); 646 } 647 if (OpenCms.getLoginManager().requiresPasswordChange(currentCms, userObj)) { 648 messageToChange = getPasswordChangeMessage(); 649 } 650 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(messageToChange)) { 651 CmsChangePasswordDialog passwordDialog = new CmsChangePasswordDialog( 652 currentCms, 653 userObj, 654 A_CmsUI.get().getLocale()); 655 passwordDialog.setAdditionalMessage(messageToChange); 656 A_CmsUI.get().setContentToDialog( 657 Messages.get().getBundle(A_CmsUI.get().getLocale()).key(Messages.GUI_PWCHANGE_HEADER_0) 658 + userObj.getSimpleName(), 659 passwordDialog); 660 return; 661 } 662 663 /* 664 * Login branches now into multiple paths: For the non-OTP case we can do it directly (synchronously), 665 * and for the OTP cases we need to show additional dialogs first (so it's asynchronous). But at the end, 666 * the same things happen. So we put these 'things that happen at the end' in a closure, so we can either 667 * execute it directly or pass it as a callback to the dialogs. 668 */ 669 LoginContinuation loginContinuation = (LoginContext details) -> { 670 671 // provisional login successful, now do for real 672 // we use another separate CmsObject so we can manually control when it is written to the session info 673 CmsObject loginCms = OpenCms.initCmsObject(currentCms); 674 CmsSecondFactorInfo secondFactorInfo = details.getSecondFactorInfo(); 675 loginCms.loginUser(realUser, password, secondFactorInfo); 676 CmsUserLog.logLogin(loginCms, realUser); 677 678 if (LOG.isInfoEnabled()) { 679 CmsRequestContext context = loginCms.getRequestContext(); 680 LOG.info( 681 org.opencms.jsp.Messages.get().getBundle().key( 682 org.opencms.jsp.Messages.LOG_LOGIN_SUCCESSFUL_3, 683 context.getCurrentUser().getName(), 684 "{workplace login dialog}", 685 context.getRemoteAddress())); 686 } 687 CmsWorkplaceSettings settings = CmsLoginHelper.initSiteAndProject(loginCms); 688 final String loginTarget = getLoginTarget(loginCms, settings, m_params.getRequestedResource()); 689 690 CmsLoginHelper.setCookieData( 691 pcType, 692 user, 693 ou, 694 (VaadinServletRequest)(VaadinService.getCurrentRequest()), 695 (VaadinServletResponse)(VaadinService.getCurrentResponse())); 696 VaadinService.getCurrentRequest().getWrappedSession().setAttribute( 697 CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS, 698 settings); 699 700 final boolean isPublicPC = CmsLoginForm.PC_TYPE_PUBLIC.equals(pcType); 701 if (OpenCms.getLoginManager().requiresUserDataCheck(loginCms, userNonNull)) { 702 I_CmsDialogContext context = new A_CmsDialogContext("", ContextType.appToolbar, null) { 703 704 @Override 705 public void finish(CmsProject project, String siteRoot) { 706 707 finish(null); 708 } 709 710 @Override 711 public void finish(Collection<CmsUUID> result) { 712 713 initSessionAndSendMessages(currentCms, loginCms); 714 m_ui.openLoginTarget(loginTarget, isPublicPC); 715 716 } 717 718 public void focus(CmsUUID structureId) { 719 720 // nothing to do 721 } 722 723 public List<CmsUUID> getAllStructureIdsInView() { 724 725 return null; 726 } 727 728 @Override 729 public CmsObject getCms() { 730 731 return loginCms; 732 } 733 734 @Override 735 public void start(String title, Component dialog, DialogWidth style) { 736 737 if (dialog != null) { 738 m_window = CmsBasicDialog.prepareWindow(style); 739 m_window.setCaption(title); 740 m_window.setContent(dialog); 741 UI.getCurrent().addWindow(m_window); 742 if (dialog instanceof CmsBasicDialog) { 743 ((CmsBasicDialog)dialog).initActionHandler(m_window); 744 } 745 } 746 } 747 748 public void updateUserInfo() { 749 750 // not supported 751 } 752 }; 753 CmsUser u = currentCms.readUser(userNonNull.getId()); 754 u.setAdditionalInfo( 755 CmsUserSettings.ADDITIONAL_INFO_LAST_USER_DATA_CHECK, 756 Long.toString(System.currentTimeMillis())); 757 loginCms.writeUser(u); 758 CmsUserDataDialog dialog = new CmsUserDataDialog(context, true); 759 context.start(dialog.getTitle(UI.getCurrent().getLocale()), dialog); 760 } else { 761 initSessionAndSendMessages(currentCms, loginCms); 762 m_ui.openLoginTarget(loginTarget, isPublicPC); 763 } 764 }; 765 LoginContext context = new LoginContext(); 766 context.setUser(userNonNull); 767 context.setCms(currentCms); 768 if (m_otpHandler.needsTwoFactorAuthentication(userNonNull)) { 769 m_ui.clearError(); 770 if (!m_otpHandler.hasSecondFactor(userObj)) { 771 showSecondFactorSetup(context, loginContinuation); 772 } else { 773 showSecondFactorDialog(context, loginContinuation); 774 } 775 } else { 776 loginContinuation.continueLogin(context); 777 } 778 } catch (Exception e) { 779 780 handleError(currentCms, realUser, e); 781 } 782 } 783 784 /** 785 * Called on initialization.<p> 786 */ 787 public void onInit() { 788 789 String authToken = m_params.getAuthToken(); 790 if (authToken != null) { 791 m_ui.showForgotPasswordView(authToken); 792 } else if (m_params.isReset()) { 793 m_ui.showPasswordResetDialog(m_params.getOufqn()); 794 } else { 795 CmsUser user = A_CmsUI.getCmsObject().getRequestContext().getCurrentUser(); 796 boolean loggedIn = !user.isGuestUser(); 797 m_ui.setSelectableOrgUnits(CmsLoginHelper.getOrgUnitsForLoginDialog(A_CmsUI.getCmsObject(), null)); 798 if (loggedIn) { 799 if (m_params.isLogout()) { 800 logout(false); 801 } else { 802 if (CmsLoginHelper.shouldAutoLogout(A_CmsUI.getCmsObject())) { 803 logout(true); 804 } else { 805 m_ui.showAlreadyLoggedIn(); 806 } 807 } 808 } else { 809 m_ui.showLoginView(m_params.getOufqn(), m_params.isAutoLogout()); 810 } 811 } 812 813 } 814 815 /** 816 * Sets the login ui reference.<p> 817 * 818 * @param ui the login ui 819 */ 820 public void setUi(CmsLoginUI ui) { 821 822 m_ui = ui; 823 } 824 825 /** 826 * Returns the message to be displayed for the user data check dialog.<p> 827 * 828 * @return the message to display 829 */ 830 protected String getPasswordChangeMessage() { 831 832 ResourceBundle bundle = null; 833 try { 834 bundle = CmsResourceBundleLoader.getBundle("org.opencms.passwordchange.custom", A_CmsUI.get().getLocale()); 835 return bundle.getString("passwordchange.text"); 836 } catch (MissingResourceException e) { 837 return CmsVaadinUtils.getMessageText(Messages.GUI_PWCHANGE_INTERVAL_EXPIRED_0); 838 } 839 } 840 841 /** 842 * Handles exceptions during the login process and displays appropriate error messages. 843 * 844 * @param currentCms the CMS context 845 * @param user the user being logged in 846 * @param e the error 847 */ 848 protected void handleError(CmsObject currentCms, String user, Exception e) { 849 850 CmsMessageContainer message = null; 851 852 // there was an error during login 853 if (e instanceof CmsException) { 854 CmsMessageContainer exceptionMessage = ((CmsException)e).getMessageContainer(); 855 if (org.opencms.security.Messages.ERR_LOGIN_FAILED_DISABLED_2 == exceptionMessage.getKey()) { 856 // the user account is disabled 857 message = org.opencms.workplace.Messages.get().container( 858 org.opencms.workplace.Messages.GUI_LOGIN_FAILED_DISABLED_0); 859 } else if (org.opencms.security.Messages.ERR_LOGIN_FAILED_TEMP_DISABLED_4 == exceptionMessage.getKey()) { 860 // the user account is temporarily disabled because of too many login failures 861 message = org.opencms.workplace.Messages.get().container( 862 org.opencms.workplace.Messages.GUI_LOGIN_FAILED_TEMP_DISABLED_0); 863 } else if (org.opencms.security.Messages.ERR_LOGIN_FAILED_WITH_MESSAGE_1 == exceptionMessage.getKey()) { 864 // all logins have been disabled be the Administration 865 CmsLoginMessage loginMessage2 = OpenCms.getLoginManager().getLoginMessage(); 866 if (loginMessage2 != null) { 867 message = org.opencms.workplace.Messages.get().container( 868 org.opencms.workplace.Messages.GUI_LOGIN_FAILED_WITH_MESSAGE_1, 869 loginMessage2.getMessage().replace("\n", "")); 870 } 871 } 872 } 873 if (message == null) { 874 if (e instanceof CmsCustomLoginException) { 875 message = ((CmsCustomLoginException)e).getMessageContainer(); 876 } else { 877 // any other error - display default message 878 message = org.opencms.workplace.Messages.get().container( 879 org.opencms.workplace.Messages.GUI_LOGIN_FAILED_0); 880 LOG.warn(e.getLocalizedMessage(), e); 881 displayError(message.key(m_params.getLocale()), true, true); 882 CmsUserLog.logLoginFailure(currentCms, user); 883 return; 884 } 885 } 886 887 if (e instanceof CmsException) { 888 CmsJspLoginBean.logLoginException(currentCms.getRequestContext(), user, (CmsException)e); 889 CmsUserLog.logLoginFailure(currentCms, user); 890 } else { 891 LOG.error(e.getLocalizedMessage(), e); 892 } 893 displayError(message.key(m_params.getLocale()), false, false); 894 } 895 896 /** 897 * Switches the session to a new one with the logged in CmsObject. 898 * 899 * <p>This needs to be called in the <em>last</em> request to the Vaadin servlet in the login process, because switching the session breaks the Vaadin session state. 900 * 901 * @param currentCms the CmsObject for the current request from the Vaadin UI 902 * @param loginCms the CmsObject which was used for the actual login operation 903 */ 904 protected void initSessionAndSendMessages(CmsObject currentCms, CmsObject loginCms) { 905 906 HttpSession session = ((HttpServletRequest)VaadinService.getCurrentRequest()).getSession(false); 907 if (session != null) { 908 session.invalidate(); 909 } 910 session = ((HttpServletRequest)VaadinService.getCurrentRequest()).getSession(true); 911 912 // we don't want currentCms to be used to automatically update the session at the end of the request... 913 currentCms.getRequestContext().setUpdateSessionEnabled(false); 914 915 // ...instead we manually update the session with loginCms 916 loginCms.getRequestContext().setUpdateSessionEnabled(true); 917 OpenCms.getSessionManager().updateSessionInfo(loginCms, (HttpServletRequest)VaadinService.getCurrentRequest()); 918 919 String storedMessage = null; 920 CmsLoginMessage loginMessage = OpenCms.getLoginManager().getLoginMessage(); 921 if (loginMessage != null) { 922 // forbidden implies active 923 if (loginMessage.isLoginCurrentlyForbidden()) { 924 // we are an administrator, otherwise login would have failed 925 if (loginMessage.getTimeEnd() == CmsLoginMessage.DEFAULT_TIME_END) { 926 storedMessage = org.opencms.workplace.Messages.get().container( 927 org.opencms.workplace.Messages.GUI_LOGIN_SUCCESS_WITH_MESSAGE_WITHOUT_TIME_1, 928 loginMessage.getMessage(), 929 new Date(loginMessage.getTimeEnd())).key(A_CmsUI.get().getLocale()); 930 } else { 931 storedMessage = org.opencms.workplace.Messages.get().container( 932 org.opencms.workplace.Messages.GUI_LOGIN_SUCCESS_WITH_MESSAGE_2, 933 loginMessage.getMessage(), 934 new Date(loginMessage.getTimeEnd())).key(A_CmsUI.get().getLocale()); 935 } 936 } else if (loginMessage.isActive()) { 937 storedMessage = loginMessage.getMessage(); 938 } 939 } 940 941 if (storedMessage != null) { 942 OpenCms.getSessionManager().sendBroadcast( 943 null, 944 storedMessage, 945 loginCms.getRequestContext().getCurrentUser(), 946 ContentMode.html); 947 } 948 } 949 950 /** 951 * Gets the CMS context.<p> 952 * 953 * @return the CMS context 954 */ 955 CmsObject getCms() { 956 957 return m_adminCms; 958 } 959 960 /** 961 * Displays the given error message.<p> 962 * 963 * @param message the message 964 * @param showForgotPassword in case the forgot password link should be shown 965 * @param showTime show the time 966 */ 967 private void displayError(String message, boolean showForgotPassword, boolean showTime) { 968 969 message = message.replace("\n", "<br />"); 970 if (showForgotPassword) { 971 message += "<br /><br /><a href=\"" 972 + getResetPasswordLink() 973 + "\">" 974 + CmsVaadinUtils.getMessageText(Messages.GUI_FORGOT_PASSWORD_0) 975 + "</a>"; 976 } 977 if (showTime) { 978 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); 979 message += "<div style=\"position:absolute;right:6px;bottom:5px;\">" 980 + CmsVaadinUtils.getMessageText(Messages.GUI_TIME_1, sdf.format(new Date())) 981 + "</div>"; 982 } 983 m_ui.showLoginError(message); 984 } 985 986 /** 987 * Shows the verification dialog for 2FA. 988 * 989 * @param context the login context 990 * @param loginContinuation the handler to which we pass the verification code to continue with the login 991 */ 992 private void showSecondFactorDialog(LoginContext context, LoginContinuation loginContinuation) { 993 994 Window window = CmsBasicDialog.prepareWindow(DialogWidth.narrow); 995 window.setClosable(false); 996 window.setResizable(false); 997 window.setCaption(CmsSecondFactorDialog.getCaption(context.getUser())); 998 CmsSecondFactorDialog dialog = new CmsSecondFactorDialog(context.getUser(), verificationCode -> { 999 context.setSecondFactorInfo(new CmsSecondFactorInfo(verificationCode)); 1000 try { 1001 loginContinuation.continueLogin(context); 1002 } catch (Exception e) { 1003 handleError(context.getCms(), context.getUser().getName(), e); 1004 } 1005 }); 1006 A_CmsUI.get().addWindow(window); 1007 window.setContent(dialog); 1008 1009 } 1010 1011 /** 1012 * Shows a dialog for setting up 2FA. 1013 * 1014 * @param context the login context 1015 * @param loginContinuation the handler we call with the secret and verification code to set up 2FA and proceed with the loin. 1016 */ 1017 private void showSecondFactorSetup(LoginContext context, LoginContinuation loginContinuation) { 1018 1019 Window window = CmsBasicDialog.prepareWindow(DialogWidth.wide); 1020 window.setClosable(false); 1021 window.setResizable(false); 1022 CmsSecondFactorSetupDialog dialog = new CmsSecondFactorSetupDialog(context, context2 -> { 1023 try { 1024 loginContinuation.continueLogin(context); 1025 } catch (Exception e) { 1026 handleError(context.getCms(), context.getUser().getName(), e); 1027 } 1028 }); 1029 window.setCaption(CmsVaadinUtils.getMessageText(Messages.GUI_LOGIN_2FA_SETUP_0)); 1030 A_CmsUI.get().addWindow(window); 1031 window.setContent(dialog); 1032 1033 } 1034}