001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://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: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://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.file.CmsUser; 031import org.opencms.main.CmsLog; 032import org.opencms.ui.CmsVaadinUtils; 033import org.opencms.ui.Messages; 034import org.opencms.ui.apps.user.CmsAccountsApp; 035import org.opencms.ui.components.CmsBasicDialog; 036import org.opencms.ui.components.CmsResourceInfo; 037import org.opencms.util.CmsFileUtil; 038import org.opencms.util.CmsStringUtil; 039 040import java.nio.charset.StandardCharsets; 041import java.util.Collections; 042import java.util.HashMap; 043import java.util.function.Consumer; 044 045import org.apache.commons.logging.Log; 046 047import com.vaadin.event.ShortcutAction.KeyCode; 048import com.vaadin.event.ShortcutListener; 049import com.vaadin.ui.Button; 050import com.vaadin.ui.JavaScript; 051import com.vaadin.ui.TextField; 052 053/** 054 * Dialog used to ask the user for a verification code generated from his second factor, using an authenticator app. 055 */ 056public class CmsSecondFactorDialog extends CmsBasicDialog { 057 058 public static final String CLASS_VERIFICATION_CODE_FIELD = "o-verification-code-field"; 059 060 /** Logger instance for this class. */ 061 private static final Log LOG = CmsLog.getLog(CmsSecondFactorDialog.class); 062 063 /** Serial version id. */ 064 private static final long serialVersionUID = 1L; 065 066 /** The OK button. */ 067 private Button m_okButton; 068 069 /** The field for entering the code. */ 070 private TextField m_verification; 071 072 /** The handler to which to pass the code entered by the user. */ 073 private Consumer<String> m_verificationCodeHandler; 074 075 /** 076 * Creates a new instance. 077 * 078 * @param user the user who should be asked for the second factor 079 * @param verificationCodeHandler the handler to which to pass the code entered by the user 080 */ 081 public CmsSecondFactorDialog(CmsUser user, Consumer<String> verificationCodeHandler) { 082 083 CmsResourceInfo userInfo = CmsAccountsApp.getPrincipalInfo(user); 084 userInfo.setTopLineText(user.getFullName()); 085 displayResourceInfoDirectly(Collections.singletonList(userInfo)); 086 087 CmsVaadinUtils.readAndLocalizeDesign(this, CmsVaadinUtils.getWpMessagesForCurrentLocale(), new HashMap<>()); 088 m_verificationCodeHandler = verificationCodeHandler; 089 m_okButton.addClickListener(event -> submit()); 090 091 m_verification.addShortcutListener(new ShortcutListener(null, KeyCode.ENTER, null) { 092 093 private static final long serialVersionUID = 1L; 094 095 @Override 096 public void handleAction(Object sender, Object target) { 097 098 submit(); 099 } 100 }); 101 m_verification.addStyleName(CLASS_VERIFICATION_CODE_FIELD); 102 addAttachListener(event -> { 103 m_verification.focus(); 104 initVerificationField(); 105 }); 106 } 107 108 /** 109 * Gets the caption to use for the dialog window. 110 * 111 * @param user the user for whom 2FA should be used 112 * 113 * @return the dialog caption 114 */ 115 public static String getCaption(CmsUser user) { 116 117 return CmsVaadinUtils.getMessageText(Messages.GUI_LOGIN_2FA_VERIFICATION_0); 118 } 119 120 /** 121 * Executes Javascript code that sets additional attributes on the verification code field. 122 */ 123 public static void initVerificationField() { 124 125 try { 126 byte[] jsSnippetBytes = CmsFileUtil.readFully( 127 CmsSecondFactorDialog.class.getResourceAsStream("init-verification-field.js"), 128 true); 129 String jsSnippet = new String(jsSnippetBytes, StandardCharsets.UTF_8); 130 JavaScript.getCurrent().execute(jsSnippet); 131 } catch (Exception e) { 132 LOG.error(e.getLocalizedMessage(), e); 133 } 134 } 135 136 /** 137 * Executed when the user clicks the OK button or presses Enter. 138 */ 139 protected void submit() { 140 141 String otp = m_verification.getValue().trim(); 142 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(otp)) { 143 CmsVaadinUtils.getWindow(this).close(); 144 m_verificationCodeHandler.accept(otp); 145 } 146 } 147}