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.CmsObject;
031import org.opencms.file.CmsUser;
032import org.opencms.main.CmsException;
033import org.opencms.main.CmsLog;
034import org.opencms.main.CmsRuntimeException;
035import org.opencms.main.OpenCms;
036import org.opencms.security.CmsSecurityException;
037import org.opencms.security.I_CmsPasswordHandler;
038import org.opencms.security.I_CmsPasswordSecurityEvaluator;
039import org.opencms.security.I_CmsPasswordSecurityEvaluator.SecurityLevel;
040import org.opencms.ui.A_CmsUI;
041import org.opencms.ui.CmsVaadinUtils;
042import org.opencms.ui.I_CmsDialogContext;
043import org.opencms.ui.Messages;
044import org.opencms.ui.components.CmsBasicDialog;
045import org.opencms.ui.components.CmsOkCancelActionHandler;
046import org.opencms.ui.components.OpenCmsTheme;
047import org.opencms.util.CmsStringUtil;
048import org.opencms.util.CmsUUID;
049import org.opencms.workplace.CmsWorkplaceLoginHandler;
050
051import java.util.Collections;
052import java.util.Locale;
053
054import org.apache.commons.logging.Log;
055
056import com.vaadin.server.UserError;
057import com.vaadin.ui.Button;
058import com.vaadin.ui.Button.ClickEvent;
059import com.vaadin.ui.Button.ClickListener;
060import com.vaadin.ui.UI;
061import com.vaadin.v7.data.Property.ValueChangeEvent;
062import com.vaadin.v7.data.Property.ValueChangeListener;
063import com.vaadin.v7.event.FieldEvents.TextChangeEvent;
064import com.vaadin.v7.event.FieldEvents.TextChangeListener;
065
066/**
067 * Dialog used to change the password.<p>
068 */
069public class CmsChangePasswordDialog extends CmsBasicDialog {
070
071    /** Logger instance for this class. */
072    private static final Log LOG = CmsLog.getLog(CmsChangePasswordDialog.class);
073
074    /** Serial version id. */
075    private static final long serialVersionUID = 1L;
076
077    /** The password form. */
078    protected CmsPasswordForm m_form;
079
080    /** The CMS context. */
081    CmsObject m_cms;
082
083    /** The locale. */
084    Locale m_locale;
085
086    /** The user. */
087    CmsUser m_user;
088
089    /** The cancel button. */
090    private Button m_cancelButton;
091
092    /** The dialog context. */
093    private I_CmsDialogContext m_context;
094
095    /** The button to change the password. */
096    private Button m_passwordChangeButton;
097
098    /**
099     * Creates a new instance.<p>
100     *
101     * @param cms the CMS context
102     * @param user the user
103     * @param locale the locale
104     */
105    public CmsChangePasswordDialog(CmsObject cms, CmsUser user, Locale locale) {
106
107        super();
108        m_locale = locale;
109        m_cms = cms;
110        m_user = user;
111        if (m_user.isManaged()) {
112            throw new CmsRuntimeException(
113                Messages.get().container(Messages.ERR_USER_NOT_SELF_MANAGED_1, m_user.getName()));
114        }
115
116        m_form = new CmsPasswordForm(m_locale);
117        setContent(m_form);
118        if (OpenCms.getPasswordHandler() instanceof I_CmsPasswordSecurityEvaluator) {
119            m_form.setSecurityHint(
120                ((I_CmsPasswordSecurityEvaluator)OpenCms.getPasswordHandler()).getPasswordSecurityHint(m_locale));
121        }
122        m_passwordChangeButton = new Button(CmsVaadinUtils.getMessageText(Messages.GUI_CHANGE_PASSWORD_BUTTON_0));
123        addButton(m_passwordChangeButton);
124        m_passwordChangeButton.addClickListener(new ClickListener() {
125
126            /** Serial versino id. */
127            private static final long serialVersionUID = 1L;
128
129            public void buttonClick(ClickEvent event) {
130
131                submit();
132            }
133        });
134
135        m_cancelButton = new Button(CmsVaadinUtils.messageCancel());
136        addButton(m_cancelButton);
137        m_cancelButton.addClickListener(new ClickListener() {
138
139            /** Serial versino id. */
140            private static final long serialVersionUID = 1L;
141
142            public void buttonClick(ClickEvent event) {
143
144                close();
145            }
146        });
147        m_cancelButton.setVisible(false);
148        m_form.getOldPasswordField().setImmediate(true);
149        m_form.getPassword1Field().setImmediate(true);
150        m_form.getPassword2Field().setImmediate(true);
151
152        m_form.getOldPasswordField().addValueChangeListener(new ValueChangeListener() {
153
154            private static final long serialVersionUID = 1L;
155
156            public void valueChange(ValueChangeEvent event) {
157
158                clearOldPasswordError();
159            }
160        });
161
162        m_form.getPassword1Field().addTextChangeListener(new TextChangeListener() {
163
164            private static final long serialVersionUID = 1L;
165
166            public void textChange(TextChangeEvent event) {
167
168                checkSecurity(event.getText());
169            }
170        });
171        m_form.getPassword2Field().addTextChangeListener(new TextChangeListener() {
172
173            private static final long serialVersionUID = 1L;
174
175            public void textChange(TextChangeEvent event) {
176
177                checkPasswordMatch(event.getText());
178            }
179        });
180    }
181
182    /**
183     * Creates a new instance.<p>
184     *
185     * @param context the dialog context
186     */
187    public CmsChangePasswordDialog(I_CmsDialogContext context) {
188
189        this(context.getCms(), context.getCms().getRequestContext().getCurrentUser(), UI.getCurrent().getLocale());
190        m_context = context;
191        m_cancelButton.setVisible(true);
192        setActionHandler(new CmsOkCancelActionHandler() {
193
194            private static final long serialVersionUID = 1L;
195
196            @Override
197            protected void cancel() {
198
199                close();
200            }
201
202            @Override
203            protected void ok() {
204
205                submit();
206            }
207        });
208    }
209
210    /**
211     * Displays some additional text.<p>
212     *
213     * @param text the text to display
214     */
215    public void setAdditionalMessage(String text) {
216
217        m_form.setAdditionalText(text);
218    }
219
220    /**
221     * Checks whether the passwords match.<p>
222     *
223     * @param password2 the password 2 value
224     */
225    void checkPasswordMatch(String password2) {
226
227        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(password2)) {
228            showPasswordMatchError(!password2.equals(m_form.getPassword1()));
229        }
230    }
231
232    /**
233     * Checks the security level of the given password.<p>
234     *
235     * @param password the password
236     */
237    void checkSecurity(String password) {
238
239        I_CmsPasswordHandler handler = OpenCms.getPasswordHandler();
240        try {
241            handler.validatePassword(password);
242            if (handler instanceof I_CmsPasswordSecurityEvaluator) {
243                SecurityLevel level = ((I_CmsPasswordSecurityEvaluator)handler).evaluatePasswordSecurity(password);
244                m_form.setErrorPassword1(null, OpenCmsTheme.SECURITY + "-" + level.name());
245            } else {
246                m_form.setErrorPassword1(null, OpenCmsTheme.SECURITY_STRONG);
247            }
248        } catch (CmsSecurityException e) {
249            m_form.setErrorPassword1(new UserError(e.getLocalizedMessage(m_locale)), OpenCmsTheme.SECURITY_INVALID);
250        }
251        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_form.getPassword2())) {
252            showPasswordMatchError(!password.equals(m_form.getPassword2()));
253        }
254    }
255
256    /**
257     * Clears the wrong old password error.<p>
258     */
259    void clearOldPasswordError() {
260
261        m_form.setErrorOldPassword(null, null);
262    }
263
264    /**
265     * Closes the dialog.<p>
266     */
267    void close() {
268
269        if (m_context != null) {
270            m_context.finish(Collections.<CmsUUID> emptyList());
271        }
272    }
273
274    /**
275     * Shows or hides the not matching passwords error.<p>
276     *
277     * @param show <code>true</code> to show the error
278     */
279    void showPasswordMatchError(boolean show) {
280
281        if (show) {
282            m_form.setErrorPassword2(
283                new UserError(Messages.get().getBundle(m_locale).key(Messages.GUI_PWCHANGE_PASSWORD_MISMATCH_0)),
284                OpenCmsTheme.SECURITY_INVALID);
285        } else {
286            m_form.setErrorPassword2(null, OpenCmsTheme.SECURITY_STRONG);
287        }
288    }
289
290    /**
291     * Submits the password change.<p>
292     */
293    void submit() {
294
295        String password1 = m_form.getPassword1();
296        String password2 = m_form.getPassword2();
297
298        if (validatePasswords(password1, password2)) {
299            String oldPassword = m_form.getOldPassword();
300            boolean error = false;
301
302            if (oldPassword.equals(password1)) {
303                m_form.setErrorPassword1(
304                    new UserError(
305                        Messages.get().getBundle(m_locale).key(Messages.GUI_PWCHANGE_DIFFERENT_PASSWORD_REQUIRED_0)),
306                    OpenCmsTheme.SECURITY_INVALID);
307                error = true;
308            } else {
309                try {
310                    m_cms.setPassword(m_user.getName(), oldPassword, password1);
311                } catch (CmsException e) {
312                    m_form.setErrorOldPassword(
313                        new UserError(e.getLocalizedMessage(m_locale)),
314                        OpenCmsTheme.SECURITY_INVALID);
315                    error = true;
316                    LOG.debug(e.getLocalizedMessage(), e);
317                }
318            }
319
320            if (!error) {
321                if (m_context != null) {
322                    close();
323                } else {
324                    // this will be the case for forced password changes after login
325                    CmsVaadinUtils.showAlert(
326                        Messages.get().getBundle(m_locale).key(Messages.GUI_PWCHANGE_SUCCESS_HEADER_0),
327                        Messages.get().getBundle(m_locale).key(Messages.GUI_PWCHANGE_GUI_PWCHANGE_SUCCESS_CONTENT_0),
328                        new Runnable() {
329
330                            public void run() {
331
332                                A_CmsUI.get().getPage().setLocation(
333                                    OpenCms.getLinkManager().substituteLinkForUnknownTarget(
334                                        CmsLoginUI.m_adminCms,
335                                        CmsWorkplaceLoginHandler.LOGIN_HANDLER
336                                            + "?ocUname="
337                                            + m_user.getSimpleName()
338                                            + "&ocOuFqn="
339                                            + m_user.getOuFqn(),
340                                        false));
341                            }
342
343                        });
344                }
345            }
346        }
347    }
348
349    /**
350     * Validates the passwords, checking if they match and fulfill the requirements of the password handler.<p>
351     * Will show the appropriate errors if necessary.<p>
352     *
353     * @param password1 password 1
354     * @param password2 password 2
355     *
356     * @return <code>true</code> if valid
357     */
358    boolean validatePasswords(String password1, String password2) {
359
360        if (!password1.equals(password2)) {
361            showPasswordMatchError(true);
362            return false;
363        }
364        showPasswordMatchError(false);
365        try {
366            OpenCms.getPasswordHandler().validatePassword(password1);
367            m_form.getPassword1Field().setComponentError(null);
368            return true;
369        } catch (CmsException e) {
370            m_form.setErrorPassword1(new UserError(e.getLocalizedMessage(m_locale)), OpenCmsTheme.SECURITY_INVALID);
371            return false;
372        }
373    }
374}