| /** |
| * |
| * Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany) |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation |
| */ |
| package org.eclipse.osbp.authentication.ui.login; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.nio.charset.Charset; |
| import java.security.Key; |
| import java.text.MessageFormat; |
| import java.time.LocalDate; |
| import java.util.ArrayList; |
| import java.util.Dictionary; |
| import java.util.List; |
| import java.util.Locale; |
| |
| import javax.annotation.PostConstruct; |
| import javax.annotation.PreDestroy; |
| import javax.crypto.spec.SecretKeySpec; |
| import javax.inject.Inject; |
| |
| import org.apache.commons.lang.SerializationUtils; |
| import org.apache.commons.lang3.RandomStringUtils; |
| import org.apache.commons.mail.Email; |
| import org.apache.shiro.codec.Hex; |
| import org.apache.shiro.crypto.AesCipherService; |
| import org.apache.shiro.crypto.CryptoException; |
| import org.apache.shiro.crypto.PaddingScheme; |
| import org.apache.shiro.util.ByteSource; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.services.events.IEventBroker; |
| import org.eclipse.equinox.app.IApplicationContext; |
| import org.eclipse.osbp.authentication.account.dtos.UserAccountDto; |
| import org.eclipse.osbp.authentication.ui.login.BrowserCookie.Callback; |
| import org.eclipse.osbp.dsl.dto.lib.impl.DtoServiceAccess; |
| import org.eclipse.osbp.preferences.ProductConfiguration; |
| import org.eclipse.osbp.runtime.common.filter.IDTOService; |
| import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService; |
| import org.eclipse.osbp.ui.api.themes.EnumCssClass; |
| import org.eclipse.osbp.ui.api.user.IUser; |
| import org.eclipse.osbp.ui.initialization.IInitializationNotification; |
| import org.eclipse.osbp.user.User; |
| import org.eclipse.osbp.vaaclipse.addons.app.VaadinE4Application; |
| import org.eclipse.osbp.vaaclipse.publicapi.authentication.AuthenticationConstants; |
| import org.eclipse.osbp.vaaclipse.publicapi.resources.BundleResource; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.FrameworkUtil; |
| import org.osgi.framework.Version; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.vaadin.event.Action; |
| import com.vaadin.event.Action.Handler; |
| import com.vaadin.event.ShortcutAction; |
| import com.vaadin.server.Page; |
| import com.vaadin.shared.ui.label.ContentMode; |
| import com.vaadin.ui.Alignment; |
| import com.vaadin.ui.Button; |
| import com.vaadin.ui.CheckBox; |
| import com.vaadin.ui.HorizontalLayout; |
| import com.vaadin.ui.Image; |
| import com.vaadin.ui.Label; |
| import com.vaadin.ui.NativeButton; |
| import com.vaadin.ui.Notification; |
| import com.vaadin.ui.Notification.Type; |
| import com.vaadin.ui.Panel; |
| import com.vaadin.ui.PasswordField; |
| import com.vaadin.ui.ProgressBar; |
| import com.vaadin.ui.TextField; |
| import com.vaadin.ui.UI; |
| import com.vaadin.ui.VerticalLayout; |
| import com.vaadin.ui.Window; |
| |
| /* to be called as login procedure this class must be registered in the product's plugin.xml as extension |
| * like this: |
| * <property |
| name="applicationAuthenticationProvider" |
| value="bundleclass://org.eclipse.osbp.utils.ui/org.eclipse.osbp.utils.vaaclipse.AuthenticationProvider"> |
| </property> |
| */ |
| /* the remember me function stores a cookie named NAME_COOKIE with 40 days validity. |
| * the contents username and password is crypted using aes encryption. |
| * the key for encryption is a static with a one time randomized static key |
| * the key inside this cookie is encrypted using aes with a one time randomized static key |
| */ |
| |
| @SuppressWarnings("serial") |
| public class AuthenticationProvider implements Callback, Handler { |
| @Inject |
| private IEventBroker eventBroker; |
| |
| @Inject |
| private IEclipseContext context; |
| |
| @Inject |
| private IDSLMetadataService dslMetadataService; |
| |
| private transient IPostAuthentication postAuthentication; |
| |
| /** The Constant LOGGER. */ |
| private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationProvider.class); |
| |
| private transient AesCipherService cipher; |
| // this cookie remembers the user credentials |
| private static final String NAME_COOKIE = "osbp_rememberme"; //$NON-NLS-1$ |
| // this cookie remembers the cipher key |
| private static final String KEY_COOKIE = "osbp_basic"; //$NON-NLS-1$ |
| // the key to cipher the key |
| private static final byte[] CIPHER_KEY = Hex.decode("CA69A6B7B1C453A1885DFA8EA5743121"); |
| private static final int KEY_SIZE = 128; |
| private Locale locale; |
| private boolean keyWasFound = false; |
| private Key key; |
| |
| private int failedLoginCnt = 0; |
| |
| private TextField userField; |
| private PasswordField passwordField; |
| private Button login; |
| private CheckBox remembermeField; |
| private Button forgotpasswordField; |
| private Button register; |
| private Image lockedImage; |
| private Image unlockedImage; |
| private String cookieValue; |
| private ProgressBar progressValue; |
| private VerticalLayout imageArea; |
| |
| // Have the unmodified Enter key cause an event |
| Action doLogin = new ShortcutAction("Default key", ShortcutAction.KeyCode.ENTER, null); |
| // Have the C key modified with Alt cause an event |
| Action actionCancel = new ShortcutAction("Alt+C", ShortcutAction.KeyCode.C, new int[] { ShortcutAction.ModifierKey.ALT }); |
| |
| public AuthenticationProvider() { |
| InitializationListener.addAuthenticationProvider(this); |
| } |
| |
| @PreDestroy |
| public void preDestroy() { |
| InitializationListener.removeAuthenticationProvider(this); |
| } |
| |
| public void setDSLMetadataService(IDSLMetadataService dslMetadataService) { |
| this.dslMetadataService = dslMetadataService; |
| } |
| |
| public IPostAuthentication getPostAuthentication() { |
| return postAuthentication; |
| } |
| |
| public void setPostAuthentication(IPostAuthentication postAuthentication) { |
| this.postAuthentication = postAuthentication; |
| } |
| |
| @SuppressWarnings("restriction") |
| @PostConstruct |
| public void init(VerticalLayout parent) { |
| LOGGER.debug("{}", "AuthenticationProvider init"); |
| if (InitializationListener.getUserAccessService() == null) { |
| LOGGER.error("{}", "userAccessService not available"); |
| return; |
| } |
| cipher = new AesCipherService(); |
| locale = UI.getCurrent().getLocale(); |
| cipher.setKeySize(KEY_SIZE); |
| |
| /* Application name, version and vendor*/ |
| IApplicationContext appcontext = VaadinE4Application.getInstance().getAppContext(); |
| Bundle bundle = appcontext.getBrandingBundle(); |
| Version brandingVersion = bundle.getVersion(); |
| String version = brandingVersion != null ? brandingVersion.toString() : "0"; |
| String brandingName = appcontext.getBrandingName(); |
| String appName = brandingName != null ? brandingName : "App"; |
| Dictionary<String, String> headers = bundle.getHeaders(); |
| String brandingVendor = headers.get("Bundle-Vendor"); |
| String vendor = brandingVendor != null ? brandingVendor : "Loetz GmbH&Co.KG Heidelberg Germany"; |
| /* --------------------------- */ |
| |
| // set polling is necessary due to possible progress bars |
| UI.getCurrent().setPollInterval(1000); |
| |
| Panel loginPanel = new Panel(); |
| loginPanel.setWidth("430px"); |
| loginPanel.setId("loginPanelArea"); |
| loginPanel.addStyleName("loginPanelArea os-login"); |
| parent.addComponent(loginPanel); |
| parent.setComponentAlignment(loginPanel, Alignment.TOP_CENTER); |
| parent.setPrimaryStyleName("osbp"); |
| loginPanel.setDescription(dslMetadataService.translate(locale.toLanguageTag(), "login_caption_tip")); |
| |
| VerticalLayout loginForm = new VerticalLayout(); |
| loginForm.setSizeFull(); |
| loginForm.setMargin(true); |
| loginForm.setId("osbpLoginForm"); |
| loginForm.addStyleName("osbpLoginForm"); |
| |
| loginPanel.setContent(loginForm); |
| |
| VerticalLayout fullArea = new VerticalLayout(); |
| fullArea.setSizeFull(); |
| fullArea.setId("loginFullArea"); |
| fullArea.addStyleName("loginFullArea"); |
| |
| HorizontalLayout titleArea = new HorizontalLayout(); |
| titleArea.setSizeFull(); |
| titleArea.setId("loginTitelArea"); |
| titleArea.addStyleName("loginTitleArea"); |
| fullArea.addComponent(titleArea); |
| fullArea.setComponentAlignment(titleArea, Alignment.TOP_CENTER); |
| Label title = new Label( appName + "<br>" + dslMetadataService.translate(locale.toLanguageTag(), "login_caption") ); |
| title.setContentMode(ContentMode.HTML); |
| title.addStyleName(EnumCssClass.VIEW_HEADER_H2.toString()); |
| titleArea.addComponent(title); |
| |
| loginForm.addComponent(fullArea); |
| |
| HorizontalLayout userArea = new HorizontalLayout(); |
| userArea.setId("loginUserArea"); |
| userArea.addStyleName("loginUserArea"); |
| userArea.setSizeFull(); |
| userArea.setMargin(true); |
| fullArea.addComponent(userArea); |
| |
| VerticalLayout textArea = new VerticalLayout(); |
| textArea.setSizeFull(); |
| userArea.addComponent(textArea); |
| userArea.setExpandRatio(textArea, 0.85f); |
| userArea.setId("loginTextArea"); |
| userArea.addStyleName("loginTextArea"); |
| |
| imageArea = new VerticalLayout(); |
| imageArea.setSizeFull(); |
| imageArea.setMargin(true); |
| userArea.addComponent(imageArea); |
| userArea.setExpandRatio(imageArea, 0.15f); |
| |
| VerticalLayout buttonArea = new VerticalLayout(); |
| buttonArea.setId("loginButtonArea"); |
| buttonArea.addStyleName("loginButtonArea"); |
| buttonArea.setSizeFull(); |
| buttonArea.setMargin(true); |
| fullArea.addComponent(buttonArea); |
| HorizontalLayout loginArea = new HorizontalLayout(); |
| loginArea.setId("loginLoginArea"); |
| loginArea.addStyleName("loginLoginArea"); |
| loginArea.setSizeFull(); |
| buttonArea.addComponent(loginArea); |
| HorizontalLayout registerArea = new HorizontalLayout(); |
| registerArea.setId("loginRegisterArea"); |
| registerArea.addStyleName("loginRegisterArea"); |
| registerArea.setSizeFull(); |
| buttonArea.addComponent(registerArea); |
| |
| VerticalLayout copyrightArea = new VerticalLayout(); |
| copyrightArea.setId("loginCopyrightArea"); |
| copyrightArea.addStyleName("loginCopyrightArea"); |
| copyrightArea.setSizeFull(); |
| loginForm.addComponent(copyrightArea); |
| if(context != null) { |
| lockedImage = new Image(null, BundleResource.valueOf("platform:/plugin/" |
| + FrameworkUtil.getBundle(AuthenticationProvider.class).getSymbolicName() + "/assets/locked.png")); |
| unlockedImage = new Image(null, BundleResource.valueOf("platform:/plugin/" |
| + FrameworkUtil.getBundle(AuthenticationProvider.class).getSymbolicName() + "/assets/unlocked.png")); |
| imageArea.addComponent(lockedImage); |
| imageArea.setComponentAlignment(lockedImage, Alignment.MIDDLE_CENTER); |
| } |
| userField = new TextField(); |
| userField.setSizeFull(); |
| textArea.addComponent(userField); |
| userField.setInputPrompt(dslMetadataService.translate(locale.toLanguageTag(), "username")); |
| userField.setDescription(dslMetadataService.translate(locale.toLanguageTag(), "username_tip")); |
| |
| passwordField = new PasswordField(); |
| passwordField.setSizeFull(); |
| textArea.addComponent(passwordField); |
| passwordField.setInputPrompt(dslMetadataService.translate(locale.toLanguageTag(), "password")); |
| passwordField.setDescription(dslMetadataService.translate(locale.toLanguageTag(), "password_tip")); |
| |
| progressValue = new ProgressBar(); |
| progressValue.setSizeFull(); |
| progressValue.addStyleName("initialization-progress"); |
| progressValue.setVisible(false); |
| textArea.addComponent(progressValue); |
| |
| if (!ProductConfiguration.hasNoRememberMe()) { |
| remembermeField = new CheckBox(); |
| remembermeField.setSizeFull(); |
| loginArea.addComponent(remembermeField); |
| remembermeField.setCaption(dslMetadataService.translate(locale.toLanguageTag(), "remember_me")); |
| remembermeField.setDescription(dslMetadataService.translate(locale.toLanguageTag(), "remember_me_tip")); |
| } |
| login = new Button(); |
| login.setSizeFull(); |
| loginArea.addComponent(login); |
| login.setCaption(dslMetadataService.translate(locale.toLanguageTag(), "sign_in")); |
| login.setDescription(dslMetadataService.translate(locale.toLanguageTag(), "sign_in_tip")); |
| login.addClickListener(e -> doLogin()); |
| |
| if(context != null) { |
| forgotpasswordField = new NativeButton(); |
| forgotpasswordField.setSizeFull(); |
| registerArea.addComponent(forgotpasswordField); |
| forgotpasswordField.setCaption(dslMetadataService.translate(locale.toLanguageTag(), "forgot_password")); |
| forgotpasswordField.setDescription(dslMetadataService.translate(locale.toLanguageTag(), "forgot_password_tip")); |
| forgotpasswordField.addClickListener(e->doResetPassword()); |
| register = new Button(); |
| register.setSizeFull(); |
| registerArea.addComponent(register); |
| register.setCaption(dslMetadataService.translate(locale.toLanguageTag(), "register_new_user")); |
| register.setDescription(dslMetadataService.translate(locale.toLanguageTag(), "register_new_user_tip")); |
| register.addClickListener(e->doRegister()); |
| } |
| |
| Label copyrightField = new Label(); |
| copyrightField.setSizeFull(); |
| copyrightArea.addComponent(copyrightField); |
| copyrightArea.setComponentAlignment(copyrightField, Alignment.MIDDLE_CENTER); |
| String actualYear = String.valueOf(LocalDate.now().getYear()); |
| String cprText = dslMetadataService.translate(locale.toLanguageTag(), "copyright") + actualYear + " " + vendor; |
| String trdmText = dslMetadataService.translate(locale.toLanguageTag(), "trademark"); |
| copyrightField.setValue(cprText + "<br>" + trdmText); |
| copyrightField.setContentMode(ContentMode.HTML); |
| Label versionField = new Label(); |
| versionField.setSizeFull(); |
| versionField.addStyleName("loginVersionField"); |
| |
| copyrightArea.addComponent(versionField); |
| copyrightArea.setComponentAlignment(copyrightField, Alignment.MIDDLE_CENTER); |
| versionField.setValue(dslMetadataService.translate(locale.toLanguageTag(), "version") + ": "+ version); |
| // Set this object as the action handler |
| loginPanel.addActionHandler(this); |
| LOGGER.debug("{}", "AuthenticationProvider layouted"); |
| |
| // eventually encrypt uncrypted passwords |
| checkCookie(); |
| |
| InitializationListener.addAuthenticationProvider(this); |
| } |
| |
| private void checkCookie() { |
| // examine the cookies |
| keyWasFound = false; |
| // keyWasFound will be set by callback function onValueDetected |
| // when the key cookie was found - onValueDetected starts a new |
| // detectCookie for the remember-me function |
| cookieValue = null; |
| // detectCookie will set this value for a hashCode check |
| if (!ProductConfiguration.hasNoRememberMe()) { |
| BrowserCookie.detectCookieValue(KEY_COOKIE, this); |
| } |
| LOGGER.debug("{}", "AuthenticationProvider cookie detecting finished"); |
| } |
| |
| private void sendResetPasswordEmail(UserAccountDto user, String resetPassword) { |
| Email email = AuthenticationUiUtil.getEmail(); |
| String emailSubject = dslMetadataService.translate(locale.toLanguageTag(), "email_password_reset_subject"); |
| if ( "email_password_reset_subject".equals(emailSubject) ){ |
| emailSubject = "Your new password"; |
| } |
| String charset=Charset.defaultCharset().name(); |
| email.setCharset(charset); |
| |
| email.setSubject(emailSubject); |
| try { |
| email.setFrom(ProductConfiguration.getAdminEmail(), ProductConfiguration.getAdminEmailUsername() ); |
| String emailBody = dslMetadataService.translate(locale.toLanguageTag(), "email_password_reset_body"); |
| if ( "email_password_reset_body".equals(emailBody) ){ |
| emailBody="Please find your new password here.\nYou have to change it with your next login!\n\nYour new password is: {0}."; |
| } |
| |
| String fmt=MessageFormat.format(emailBody,resetPassword).replace("\\n", "\n"); |
| email.setMsg(fmt); |
| email.addTo(user.getEmail()); |
| email.send(); |
| } catch (Exception e) { |
| LOGGER.error("EmailException: {}", e.getLocalizedMessage()); |
| LOGGER.error("failed to send password {} to user {} to address {}", resetPassword, user.getUserName(), user.getEmail() ); |
| } |
| } |
| |
| /** |
| * Retrieve actions for a specific component. This method will be called for |
| * each object that has a handler; in this example just for login panel. The |
| * returned action list might as well be static list. |
| */ |
| @Override |
| public Action[] getActions(Object target, Object sender) { |
| return new Action[] { doLogin }; |
| } |
| |
| /** |
| * Handle actions received from keyboard. This simply directs the actions to |
| * the same listener methods that are called with ButtonClick events. |
| */ |
| @Override |
| public void handleAction(Action action, Object sender, Object target) { |
| if (action == doLogin) { |
| doLogin(); |
| } |
| } |
| |
| public void doLogin() { |
| checkCookie(); |
| InitializationListener.getUserAccessService().logout(); |
| login.focus(); |
| String username = userField.getValue(); |
| String password = passwordField.getValue(); |
| if (username.trim().isEmpty()) |
| username = null; |
| boolean hasRememberMe = ProductConfiguration.hasNoRememberMe() ? false : remembermeField.getValue(); |
| if (check(username, password, hasRememberMe) && !checkPasswordReset(username)) { |
| if(context != null) { |
| imageArea.removeComponent(lockedImage); |
| imageArea.addComponent(unlockedImage); |
| } |
| User user = new User(username); |
| registerUser(user); |
| if(postAuthentication != null) { |
| postAuthentication.execute(user); |
| } |
| } |
| } |
| |
| private void registerUser(User user) { |
| if(context != null) { |
| if (context.get(IUser.class) == null) { |
| context.set(IUser.class, user); |
| context.set("user", user); |
| } |
| if(eventBroker != null) { |
| eventBroker.post(AuthenticationConstants.Events.Authentication.name, user); |
| } |
| } |
| } |
| |
| private boolean check(String username, String password, Boolean rememberMe) { |
| boolean cookieValid = false; |
| if (cookieValue != null) { |
| if (!InitializationListener.getUserAccessService().isCookieValid(username, cookieValue)) { |
| // fraud detected - lock user account |
| LOGGER.debug("{}", "cookie fraud detection - account is locked"); |
| InitializationListener.getUserAccessService().lockAccount(username, true); |
| } |
| cookieValid = true; |
| } |
| if (!tryToAuthenticate(AuthenticationUiUtil.PORTAL_ID, username, password)) { |
| if (InitializationListener.getUserAccessService().isAccountLocked(username)) { |
| // wait exponentially along failed logins up to a maximum of |
| // 18,2 hours to prevent "Rapid-Fire Login Attempts" |
| // http://stackoverflow.com/questions/549/the-definitive-guide-to-form-based-website-authentication |
| |
| Double sleep=(1000.0 * Math.max(5,Math.pow(2, Math.min(failedLoginCnt++, 16))-1)); |
| int sleepSeconds=sleep.intValue()/1000; |
| Notification notification = new Notification(dslMetadataService.translate(locale.toLanguageTag(), "lockedmessage"), Type.ERROR_MESSAGE); |
| notification.setDelayMsec(sleep.intValue()); |
| LOGGER.info("Login '{}' is locked for {} seconds now ... pls. wait", username, ((Integer)sleepSeconds).toString() ); //NOSONAR |
| notification.show(Page.getCurrent()); |
| |
| } else if (InitializationListener.getUserAccessService().isAccountNotRegistered(username)) { |
| Notification.show(dslMetadataService.translate(locale.toLanguageTag(), "not_registered_message"), Type.ERROR_MESSAGE); |
| } else if (!InitializationListener.getUserAccessService().isAccountEnabled(username)) { |
| Notification.show(dslMetadataService.translate(locale.toLanguageTag(), "disabledmessage"), Type.ERROR_MESSAGE); |
| } else { |
| Double sleep=(1000.0 * Math.max(5,Math.pow(2, Math.min(failedLoginCnt++, 16))-1)); |
| int sleepSeconds=sleep.intValue()/1000; |
| Notification notification = new Notification(dslMetadataService.translate(locale.toLanguageTag(), "failedmessage"), Type.ERROR_MESSAGE); |
| notification.setDelayMsec(sleep.intValue()); |
| LOGGER.info("Login '{}' is delayed for {} seconds now ... pls. wait", username, ((Integer)sleepSeconds).toString() ); //NOSONAR |
| notification.show(Page.getCurrent()); |
| failedLoginCnt++; |
| } |
| return false; |
| } |
| failedLoginCnt=0; |
| |
| if (!cookieValid) { |
| // if login is valid and remember_me is active, store a cookie |
| if (rememberMe) { |
| // create a new cipher key if not already found |
| if (!keyWasFound) { |
| key = cipher.generateNewKey(KEY_SIZE); |
| byte[] keyValue = SerializationUtils.serialize(key); |
| ByteSource encryptedData = null; |
| try { |
| cipher.setPaddingScheme(PaddingScheme.PKCS5); |
| encryptedData = cipher.encrypt(pkcs5Bytes(keyValue), generateKey().getEncoded()); |
| BrowserCookie.setCookie(KEY_COOKIE, Hex.encodeToString(encryptedData.getBytes())); |
| keyWasFound = true; |
| } catch (Exception e) { |
| Throwable cause = e.getCause(); |
| String msg = e.getLocalizedMessage(); |
| if (cause != null) { |
| msg += "->" + cause.getLocalizedMessage(); |
| } |
| LOGGER.error(msg); |
| } |
| } |
| List<String> loginCredentials = new ArrayList<>(); |
| loginCredentials.add(username); |
| loginCredentials.add(password); |
| loginCredentials.add(rememberMe.toString()); |
| String loginData = encryptData(loginCredentials); |
| if (loginData != null) { |
| // cookie stays valid up to 40 days |
| BrowserCookie.setCookie(NAME_COOKIE, loginData); |
| InitializationListener.getUserAccessService().setCookieHash(username, loginData); |
| LOGGER.debug("{}", "cookie set for remember me"); |
| } |
| } else { |
| // destroy cookie |
| BrowserCookie.deleteCookie(NAME_COOKIE); |
| InitializationListener.getUserAccessService().setCookieHash(username, null); |
| LOGGER.debug("{}", "cookie destroyed for remember me"); |
| } |
| } |
| return true; |
| } |
| |
| private String encryptData(List<String> loginCredentials) { |
| cipher.setPaddingScheme(PaddingScheme.PKCS5); |
| String data = String.join(",", loginCredentials); |
| ByteSource encryptedData = null; |
| try { |
| encryptedData = cipher.encrypt(pkcs5Bytes(data), key.getEncoded()); |
| } catch (CryptoException | UnsupportedEncodingException e) { |
| Throwable cause = e.getCause(); |
| String msg = e.getLocalizedMessage(); |
| if (cause != null) { |
| msg += "->" + cause.getLocalizedMessage(); |
| } |
| LOGGER.error(msg); |
| return null; |
| } |
| return encryptedData.toHex(); |
| } |
| |
| private List<String> decryptData(String loginData) { |
| cipher.setPaddingScheme(PaddingScheme.PKCS5); |
| List<String> credentials = new ArrayList<>(); |
| ByteSource decryptedData = null; |
| if (loginData != null) { |
| try { |
| decryptedData = cipher.decrypt(Hex.decode(loginData), key.getEncoded()); |
| } catch (CryptoException e) { |
| Throwable cause = e.getCause(); |
| String msg = e.getLocalizedMessage(); |
| if (cause != null) { |
| msg += "->" + cause.getLocalizedMessage(); |
| } |
| LOGGER.error(msg); |
| return credentials; |
| } |
| String decryptedString = new String(decryptedData.getBytes()); |
| String[] data = decryptedString.split(","); |
| for (String d : data) { |
| credentials.add(d.trim()); |
| } |
| } |
| return credentials; |
| } |
| |
| // this callback is called by javascript on detection of the desired cookie |
| @Override |
| public void onValueDetected(String cookieName, String value) { |
| if (cookieName != null && value != null) { |
| LOGGER.debug("cookie {} was found", cookieName); |
| if (KEY_COOKIE.equals(cookieName)) { |
| try { |
| // deserialize the decoded hex encoded string |
| cipher.setPaddingScheme(PaddingScheme.PKCS5); |
| key = (Key) SerializationUtils.deserialize(cipher.decrypt(Hex.decode(value), generateKey().getEncoded()).getBytes()); |
| keyWasFound = true; |
| BrowserCookie.detectCookieValue(NAME_COOKIE, this); |
| } catch (Exception e) { |
| Throwable cause = e.getCause(); |
| String msg = e.getLocalizedMessage(); |
| if (cause != null) { |
| msg += "->" + cause.getLocalizedMessage(); |
| } |
| LOGGER.error(msg); |
| } |
| } else if (NAME_COOKIE.equals(cookieName)) { |
| // store for a later cookie validation |
| cookieValue = value; |
| List<String> loginCredentials = decryptData(value); |
| if (loginCredentials.size() == 3) { |
| UI.getCurrent().getSession().lock(); |
| userField.setValue(loginCredentials.get(0)); |
| passwordField.setValue(loginCredentials.get(1)); |
| remembermeField.setValue(true); |
| UI.getCurrent().getSession().unlock(); |
| LOGGER.debug("{}", "cookie decoded"); |
| if (ProductConfiguration.hasAutoLogin()) { |
| login.click(); |
| } |
| } else { |
| LOGGER.debug("{}", "cookie for remember me has invalid item count"); |
| } |
| } |
| } else { |
| LOGGER.debug("{}", "no more valid cookies found"); |
| } |
| } |
| |
| /* |
| * helper function for the PKCS5 algorithm to create a correct padding |
| * pattern to 16 byte boundaries |
| * |
| * the space is filled with the hexadecimal value of the number of padded |
| * characters |
| * |
| * as shiro creates bytecode encoding UTF-8 by default, we must use the same |
| * for padding characters and the returning byte array |
| */ |
| private byte[] pkcs5Bytes(String data) throws UnsupportedEncodingException { |
| return pkcs5Bytes(data.getBytes("UTF-8")); |
| } |
| |
| private byte[] pkcs5Bytes(byte[] data) { |
| byte diff = (byte) (16 - data.length % 16); |
| byte[] result = new byte[data.length + diff]; |
| for (int i = 0; i < data.length + diff; i++) { |
| if (i < data.length) |
| result[i] = data[i]; |
| else |
| result[i] = diff; |
| } |
| return result; |
| } |
| |
| private static Key generateKey() { |
| return new SecretKeySpec(CIPHER_KEY, "AES"); |
| } |
| |
| /** |
| * Try to authenticate with the credentials given!<br> |
| * {@link #setAuthenticated(boolean)} will explicit be called! |
| * |
| * @param portalId |
| * @param userName |
| * @param password |
| * @return true if the user was authenticated successful |
| */ |
| boolean tryToAuthenticate(String portalId, String userName, String password) { |
| boolean authenticated = InitializationListener.getUserAccessService().isAuthenticated(); |
| if (!authenticated) { |
| try { |
| authenticated = InitializationListener.getUserAccessService().authenticate(portalId, userName, password); |
| } catch (Exception e) { |
| LOGGER.error(e.getLocalizedMessage()); |
| } |
| } |
| return authenticated; |
| } |
| |
| public void notifyInitializationStep(IInitializationNotification notification) { |
| notifiyInitializationStep(notification, true); |
| } |
| |
| public void notifyInitializationDone(IInitializationNotification notification) { |
| notifiyInitializationStep(notification, false); |
| } |
| |
| private void notifiyInitializationStep(IInitializationNotification notification, boolean blockUi) { |
| if (progressValue != null) { |
| progressValue.setValue((float) notification.getRecommendedProgressValue()); |
| progressValue.setCaption(notification.getRecommendedProgressTitle(true)); |
| progressValue.setVisible(blockUi); |
| } |
| enableLoginUI(!blockUi); |
| } |
| |
| public void enableLoginUI(boolean reEnableLoginUi) { |
| if (userField != null) { |
| userField.setEnabled(reEnableLoginUi); |
| passwordField.setEnabled(reEnableLoginUi); |
| login.setEnabled(reEnableLoginUi); |
| remembermeField.setEnabled(reEnableLoginUi); |
| if(context != null) { |
| register.setEnabled(reEnableLoginUi); |
| } |
| } |
| } |
| |
| public void doRegister() { |
| register.focus(); |
| String username = userField.getValue(); |
| if (username.trim().isEmpty()) { |
| Notification.show(dslMetadataService.translate(locale.toLanguageTag(), "register_username_is_empty_message")); |
| userField.focus(); |
| } else if (InitializationListener.getUserAccessService().checkNotLoggedInUsernameExists(username)) { |
| Notification.show(dslMetadataService.translate(locale.toLanguageTag(), "register_username_already_exists")); |
| } else { // Do a registration only if the user doesn´t already exist. |
| User user = new User(username); |
| if (context.get(User.class) == null) { |
| context.set(User.class, user); |
| context.set("user", user); |
| } |
| RegistrationDialog registerDialog = new RegistrationDialog(); |
| Window registerWindow = registerDialog.init(eventBroker, dslMetadataService, user); |
| UI.getCurrent().addWindow(registerWindow); |
| } |
| |
| } |
| |
| public void doResetPassword() { |
| forgotpasswordField.focus(); |
| String username = userField.getValue(); |
| if (username.trim().isEmpty()) { |
| username = null; |
| } |
| if (InitializationListener.getUserAccessService().checkNotLoggedInUsernameExists(username)) { |
| UserAccountDto user = (UserAccountDto) InitializationListener.getUserAccessService().findUserAccount(username); |
| String resetPassword = RandomStringUtils.randomAlphanumeric(8); |
| @SuppressWarnings("restriction") |
| IDTOService<UserAccountDto> service = (IDTOService<UserAccountDto>) DtoServiceAccess |
| .getService(UserAccountDto.class); |
| String encryptedPassword = InitializationListener.getUserAccessService().encryptPassword(resetPassword); |
| user.setPassword(encryptedPassword); |
| user.setPasswordReset(true); |
| user.setLocked(false); |
| user.setFailedAttempt(0); |
| user.setEnabled(true); |
| service.update(user); |
| Notification.show(dslMetadataService.translate(locale.toLanguageTag(), "password_reset_success_message")); |
| sendResetPasswordEmail(user, resetPassword); |
| } |
| } |
| |
| public boolean checkPasswordReset(String username) { |
| // Do a registration only if the user doesn´t already exist. |
| if(eventBroker != null) { |
| UserAccountDto userAccount = (UserAccountDto) InitializationListener.getUserAccessService().findUserAccount(username); |
| if (userAccount != null && userAccount.getPasswordReset()) { |
| NewPasswordDialog newPasswordDialog = new NewPasswordDialog(); |
| Window registerWindow = newPasswordDialog.init(eventBroker, dslMetadataService, userAccount); |
| UI.getCurrent().addWindow(registerWindow); |
| return true; |
| } |
| } |
| return false; |
| } |
| } |