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.crypto.CmsEncryptionException;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsUser;
033import org.opencms.flex.CmsFlexController;
034import org.opencms.gwt.shared.CmsGwtConstants;
035import org.opencms.i18n.CmsEncoder;
036import org.opencms.main.CmsException;
037import org.opencms.main.CmsLog;
038import org.opencms.main.CmsRuntimeException;
039import org.opencms.main.OpenCms;
040import org.opencms.security.CmsOrganizationalUnit;
041import org.opencms.ui.A_CmsUI;
042import org.opencms.ui.CmsVaadinErrorHandler;
043import org.opencms.ui.CmsVaadinUtils;
044import org.opencms.ui.Messages;
045import org.opencms.ui.apps.CmsAppWorkplaceUi;
046import org.opencms.ui.components.CmsBasicDialog;
047import org.opencms.ui.components.CmsBasicDialog.DialogWidth;
048import org.opencms.ui.components.extensions.CmsPollServerExtension;
049import org.opencms.ui.login.CmsLoginHelper.LoginParameters;
050import org.opencms.ui.shared.CmsVaadinConstants;
051import org.opencms.util.CmsFileUtil;
052import org.opencms.util.CmsMacroResolver;
053import org.opencms.util.CmsRequestUtil;
054import org.opencms.util.CmsStringUtil;
055import org.opencms.workplace.CmsWorkplace;
056import org.opencms.workplace.CmsWorkplaceManager;
057import org.opencms.workplace.CmsWorkplaceSettings;
058
059import java.io.IOException;
060import java.io.Serializable;
061import java.util.List;
062import java.util.Locale;
063
064import javax.servlet.http.HttpServletRequest;
065import javax.servlet.http.HttpServletResponse;
066import javax.servlet.http.HttpSession;
067
068import org.apache.commons.logging.Log;
069
070import com.vaadin.annotations.Theme;
071import com.vaadin.server.VaadinRequest;
072import com.vaadin.server.VaadinService;
073import com.vaadin.server.VaadinServletRequest;
074import com.vaadin.server.VaadinSession;
075import com.vaadin.shared.Version;
076import com.vaadin.ui.Alignment;
077import com.vaadin.ui.Button;
078import com.vaadin.ui.Notification;
079import com.vaadin.ui.Notification.Type;
080import com.vaadin.ui.Window;
081import com.vaadin.ui.Window.CloseEvent;
082import com.vaadin.ui.Window.CloseListener;
083import com.vaadin.v7.ui.Label;
084import com.vaadin.v7.ui.VerticalLayout;
085
086/**
087 * The UI class for the Vaadin-based login dialog.<p>
088 */
089@Theme("opencms")
090public class CmsLoginUI extends A_CmsUI {
091
092    /**
093     * Parameters which are initialized during the initial page load of the login dialog.<p>
094     */
095    public static class Parameters implements Serializable {
096
097        /** The serial version id. */
098        private static final long serialVersionUID = -4885232184680664315L;
099
100        /** The locale. */
101        public Locale m_locale;
102
103        /** The PC type (public or private). */
104        public String m_pcType;
105
106        /** The preselected OU. */
107        public String m_preselectedOu;
108
109        /** The requested resource. */
110        public String m_requestedResource;
111
112        /** The requested workplace app path. */
113        public String m_requestedWorkplaceApp;
114
115        /**
116         * Creates a new instance.<p>
117         *
118         * @param pcType the PC type
119         * @param preselectedOu the preselected OU
120         * @param locale the locale
121         * @param requestedResource the requested resource
122         * @param requestedWorkplaceApp the requested workplace app path
123         */
124        public Parameters(
125            String pcType,
126            String preselectedOu,
127            Locale locale,
128            String requestedResource,
129            String requestedWorkplaceApp) {
130
131            m_pcType = pcType;
132            m_preselectedOu = preselectedOu;
133            m_locale = locale;
134            m_requestedResource = requestedResource;
135            m_requestedWorkplaceApp = requestedWorkplaceApp;
136        }
137
138        /**
139         * Gets the locale.<p>
140         *
141         * @return the locale
142         */
143        public Locale getLocale() {
144
145            return m_locale;
146        }
147
148        /**
149         * Gets the PC type (private or public).<p>
150         *
151         * @return the pc type
152         */
153        public String getPcType() {
154
155            return m_pcType;
156
157        }
158
159        /**
160         * Gets the preselected OU.<p>
161         *
162         * @return the preselected OU
163         */
164        public String getPreselectedOu() {
165
166            return m_preselectedOu;
167        }
168
169        /**
170         * Gets the requested resource path.<p>
171         *
172         * @return the requested resource path
173         */
174        public String getRequestedResource() {
175
176            return m_requestedResource;
177        }
178
179        /**
180         * Returns the requested workplace app path.<p>
181         *
182         * @return the requested workplace app path
183         */
184        public String getRequestedWorkplaceApp() {
185
186            return m_requestedWorkplaceApp;
187        }
188
189    }
190
191    /**
192     * Attribute used to store initialization data when the UI is first loaded.
193     */
194    public static final String INIT_DATA_SESSION_ATTR = "CmsLoginUI_initData";
195
196    /** The admin CMS context. */
197    static CmsObject m_adminCms;
198
199    /** Logger instance for this class. */
200    private static final Log LOG = CmsLog.getLog(CmsLoginUI.class);
201
202    /** Serial version id. */
203    private static final long serialVersionUID = 1L;
204
205    /** The login controller. */
206    private CmsLoginController m_controller;
207
208    /** The login form. */
209    private CmsLoginForm m_loginForm;
210
211    /** The widget used to open the login target. */
212    private CmsLoginTargetOpener m_targetOpener;
213
214    /**
215     * Returns the initial HTML for the Vaadin based login dialog.<p>
216     *
217     * @param request the request
218     * @param response the response
219     *
220     * @return the initial page HTML for the Vaadin login dialog
221     *
222     * @throws IOException in case writing to the response fails
223     * @throws CmsException in case the user has not the required role
224     */
225    public static String displayVaadinLoginDialog(HttpServletRequest request, HttpServletResponse response)
226    throws IOException, CmsException {
227
228        CmsFlexController controller = CmsFlexController.getController(request);
229        if (controller == null) {
230            // controller not found - this request was not initialized properly
231            throw new CmsRuntimeException(
232                org.opencms.jsp.Messages.get().container(
233                    org.opencms.jsp.Messages.ERR_MISSING_CMS_CONTROLLER_1,
234                    CmsLoginUI.class.getName()));
235        }
236        CmsRequestUtil.disableCrossSiteFrameEmbedding(response);
237        CmsObject cms = controller.getCmsObject();
238        if ((OpenCms.getSiteManager().getSites().size() > 1) && !OpenCms.getSiteManager().isWorkplaceRequest(request)) {
239            // do not send any redirects to the workplace site for security reasons
240            response.sendError(HttpServletResponse.SC_NOT_FOUND);
241            return null;
242        }
243        String logout = request.getParameter(CmsLoginHelper.PARAM_ACTION_LOGOUT);
244        if (Boolean.valueOf(logout).booleanValue()) {
245            CmsLoginController.logout(cms, request, response);
246            return null;
247        }
248        if (!cms.getRequestContext().getCurrentUser().isGuestUser()) {
249            if (CmsLoginHelper.shouldAutoLogout(cms)) {
250                LOG.info("Auto logout for current user");
251                // the dialog does the actual logout, so here we do nothing
252            } else {
253                String encryptedTarget = request.getParameter(CmsGwtConstants.PARAM_LOGIN_REDIRECT);
254                String target = null;
255                if (CmsStringUtil.isEmptyOrWhitespaceOnly(encryptedTarget)) {
256                    target = CmsLoginController.getLoginTarget(
257                        cms,
258                        getWorkplaceSettings(cms, request.getSession()),
259                        null);
260                } else {
261                    try {
262                        target = OpenCms.getDefaultTextEncryption().decrypt(encryptedTarget);
263                    } catch (CmsEncryptionException e) {
264                        LOG.error(e.getLocalizedMessage(), e);
265                        return null;
266                    }
267                }
268                response.sendRedirect(target);
269                return null;
270            }
271        }
272        CmsLoginHelper.LoginParameters params = CmsLoginHelper.getLoginParameters(cms, request, false);
273        request.getSession().setAttribute(CmsLoginUI.INIT_DATA_SESSION_ATTR, params);
274        try {
275            byte[] pageBytes = CmsFileUtil.readFully(
276                Thread.currentThread().getContextClassLoader().getResourceAsStream(
277                    "org/opencms/ui/login/login-page.html"));
278            String page = new String(pageBytes, "UTF-8");
279            CmsMacroResolver resolver = new CmsMacroResolver();
280            String context = OpenCms.getSystemInfo().getContextPath();
281            String vaadinDir = CmsStringUtil.joinPaths(context, "VAADIN/");
282            String vaadinVersion = Version.getFullVersion();
283            String vaadinServlet = CmsStringUtil.joinPaths(context, "workplace/dialogs/");
284            String vaadinBootstrap = CmsStringUtil.joinPaths(
285                context,
286                "VAADIN/vaadinBootstrap.js?v=" + OpenCms.getSystemInfo().getVersionNumber());
287            String autocomplete = params.isPrivatePc() ? "on" : "off";
288            StringBuffer workplaceCssBuffer = null;
289            if (!OpenCms.getWorkplaceAppManager().getWorkplaceCssUris().isEmpty()) {
290                workplaceCssBuffer = new StringBuffer();
291                workplaceCssBuffer.append("\n<style type=\"text/css\">\n");
292                for (String cssURI : OpenCms.getWorkplaceAppManager().getWorkplaceCssUris()) {
293                    workplaceCssBuffer.append("@import url(\"").append(CmsWorkplace.getResourceUri(cssURI)).append(
294                        "\");\n");
295                }
296                workplaceCssBuffer.append("</style>\n");
297            }
298            String cmsLogo = OpenCms.getSystemInfo().getContextPath()
299                + CmsWorkplace.RFS_PATH_RESOURCES
300                + "commons/login_logo.png";
301            resolver.addMacro("workplaceCss", workplaceCssBuffer != null ? workplaceCssBuffer.toString() : "");
302            resolver.addMacro("loadingHtml", CmsVaadinConstants.LOADING_INDICATOR_HTML);
303            resolver.addMacro("vaadinDir", vaadinDir);
304            resolver.addMacro("vaadinVersion", vaadinVersion);
305            resolver.addMacro("vaadinServlet", vaadinServlet);
306            resolver.addMacro("vaadinBootstrap", vaadinBootstrap);
307            resolver.addMacro("cmsLogo", cmsLogo);
308            resolver.addMacro("autocomplete", autocomplete);
309            resolver.addMacro("title", CmsLoginHelper.getTitle(params.getLocale()));
310            if (params.isPrivatePc()) {
311                resolver.addMacro(
312                    "hiddenPasswordField",
313                    "      <input type=\"password\" id=\"hidden-password\" name=\"ocPword\" autocomplete=\"%(autocomplete)\" >");
314            }
315            if (params.getUsername() != null) {
316                resolver.addMacro("predefUser", "value=\"" + CmsEncoder.escapeXml(params.getUsername()) + "\"");
317            }
318            page = resolver.resolveMacros(page);
319            return page;
320        } catch (Exception e) {
321            LOG.error("Failed to display login dialog.", e);
322            return "<!--Error-->";
323        }
324    }
325
326    /**
327     * Returns the bootstrap html fragment required to display the login dialog.<p>
328     *
329     * @param cms the cms context
330     * @param request the request
331     *
332     * @return the html fragment
333     *
334     * @throws IOException in case reading the html template fails
335     */
336    public static String generateLoginHtmlFragment(CmsObject cms, VaadinRequest request) throws IOException {
337
338        LoginParameters parameters = CmsLoginHelper.getLoginParameters(cms, (HttpServletRequest)request, true);
339        request.getWrappedSession().setAttribute(CmsLoginUI.INIT_DATA_SESSION_ATTR, parameters);
340        byte[] pageBytes;
341
342        pageBytes = CmsFileUtil.readFully(
343            Thread.currentThread().getContextClassLoader().getResourceAsStream(
344                "org/opencms/ui/login/login-fragment.html"));
345
346        String html = new String(pageBytes, "UTF-8");
347        String autocomplete = ((parameters.getPcType() == null)
348            || parameters.getPcType().equals(CmsLoginHelper.PCTYPE_PRIVATE)) ? "on" : "off";
349        CmsMacroResolver resolver = new CmsMacroResolver();
350        resolver.addMacro("autocompplete", autocomplete);
351        if ((parameters.getPcType() == null) || parameters.getPcType().equals(CmsLoginHelper.PCTYPE_PRIVATE)) {
352            resolver.addMacro(
353                "hiddenPasswordField",
354                "      <input type=\"password\" id=\"hidden-password\" name=\"ocPword\" autocomplete=\"%(autocomplete)\" >");
355        }
356        if (parameters.getUsername() != null) {
357            resolver.addMacro("predefUser", "value=\"" + CmsEncoder.escapeXml(parameters.getUsername()) + "\"");
358        }
359        html = resolver.resolveMacros(html);
360        return html;
361    }
362
363    /**
364     * Sets the admin CMS object.<p>
365     *
366     * @param cms the admin cms object
367     */
368    public static void setAdminCmsObject(CmsObject cms) {
369
370        m_adminCms = cms;
371    }
372
373    /**
374     * Returns the current users workplace settings.<p>
375     *
376     * @param cms the CMS context
377     * @param session the session
378     *
379     * @return the settings
380     */
381    private static CmsWorkplaceSettings getWorkplaceSettings(CmsObject cms, HttpSession session) {
382
383        CmsWorkplaceSettings settings = (CmsWorkplaceSettings)session.getAttribute(
384            CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS);
385        if (settings == null) {
386            settings = CmsLoginHelper.initSiteAndProject(cms);
387            if (VaadinService.getCurrentRequest() != null) {
388                VaadinService.getCurrentRequest().getWrappedSession().setAttribute(
389                    CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS,
390                    settings);
391            } else {
392                session.setAttribute(CmsWorkplaceManager.SESSION_WORKPLACE_SETTINGS, settings);
393            }
394        }
395        return settings;
396    }
397
398    public void clearError() {
399
400        m_loginForm.clearError();
401    }
402
403    /**
404     * Gets the selected org unit.<p>
405     *
406     * @return the selected org unit
407     */
408    public String getOrgUnit() {
409
410        String result = m_loginForm.getOrgUnit();
411        if (result == null) {
412            result = "";
413        }
414        return result;
415
416    }
417
418    /**
419     * Gets the password.<p>
420     *
421     * @return the password
422     */
423    public String getPassword() {
424
425        return m_loginForm.getPassword();
426    }
427
428    /**
429     * Gets the selected PC type.<p>
430     *
431     * @return the PC type
432     */
433    public String getPcType() {
434
435        String result = m_loginForm.getPcType();
436        if (result == null) {
437            result = CmsLoginForm.PC_TYPE_PUBLIC;
438        }
439        return result;
440    }
441
442    /**
443     * Gets the user name.<p>
444     *
445     * @return the user name
446     */
447    public String getUser() {
448
449        return m_loginForm.getUser();
450    }
451
452    /**
453     * Opens the login target for a logged in user.<p>
454     *
455     * @param loginTarget the login target
456     * @param isPublicPC the public PC flag
457     */
458    public void openLoginTarget(String loginTarget, boolean isPublicPC) {
459
460        // login was successful, remove login init data from session
461        VaadinService.getCurrentRequest().getWrappedSession().removeAttribute(INIT_DATA_SESSION_ATTR);
462        m_targetOpener.openTarget(loginTarget, isPublicPC);
463    }
464
465    /**
466     * Sets the org units which should be selectable by the user.<p>
467     *
468     * @param ous the selectable org units
469     */
470    public void setSelectableOrgUnits(List<CmsOrganizationalUnit> ous) {
471
472        m_loginForm.setSelectableOrgUnits(ous);
473    }
474
475    /**
476     * Show notification that the user is already loogged in.<p>
477     */
478    public void showAlreadyLoggedIn() {
479
480        // TODO: do something useful
481        Notification.show("You are already logged in");
482    }
483
484    /**
485     * Shows the 'forgot password view'.<p>
486     *
487     * @param authToken the authorization token given as a request parameter
488     */
489    public void showForgotPasswordView(String authToken) {
490
491        try {
492            CmsTokenValidator validator = new CmsTokenValidator();
493            String validationResult = validator.validateToken(
494                A_CmsUI.getCmsObject(),
495                authToken,
496                OpenCms.getLoginManager().getTokenLifetime());
497            if (validationResult == null) {
498                CmsUser user = validator.getUser();
499                if (!user.isManaged()) {
500                    CmsSetPasswordDialog dlg = new CmsSetPasswordDialog(m_adminCms, user, getLocale());
501                    A_CmsUI.get().setContentToDialog(
502                        Messages.get().getBundle(A_CmsUI.get().getLocale()).key(Messages.GUI_PWCHANGE_HEADER_0)
503                            + user.getName(),
504                        dlg);
505                } else {
506                    Notification.show(
507                        CmsVaadinUtils.getMessageText(Messages.ERR_USER_NOT_SELF_MANAGED_1, user.getName()),
508                        Type.ERROR_MESSAGE);
509                }
510            } else {
511                A_CmsUI.get().setError(
512                    Messages.get().getBundle(A_CmsUI.get().getLocale()).key(Messages.GUI_PWCHANGE_INVALID_TOKEN_0));
513                LOG.info("Invalid authorization token: " + authToken + " / " + validationResult);
514            }
515        } catch (Exception e) {
516            LOG.error(e.getLocalizedMessage(), e);
517        }
518    }
519
520    /**
521     * Shows the given login error message.<p>
522     *
523     * @param messageHtml the message HTML
524     */
525    public void showLoginError(String messageHtml) {
526
527        m_loginForm.displayError(messageHtml);
528        m_loginForm.resetPassword();
529    }
530
531    /**
532     * Initializes the login view.<p>
533     *
534     * @param preselectedOu a potential preselected OU
535     * @param isAutoLogout true if user was automatically logged out
536     */
537    public void showLoginView(String preselectedOu, boolean isAutoLogout) {
538
539        VerticalLayout content = new VerticalLayout();
540        content.setSizeFull();
541
542        m_targetOpener = new CmsLoginTargetOpener(A_CmsUI.get());
543        //content.setExpandRatio(m_targetOpener, 0f);
544        content.addComponent(m_loginForm);
545        content.setComponentAlignment(m_loginForm, Alignment.MIDDLE_CENTER);
546        content.setExpandRatio(m_loginForm, 1);
547
548        setContent(content);
549
550        m_loginForm.selectOrgUnit(preselectedOu);
551        if (isAutoLogout) {
552            String text = CmsVaadinUtils.getMessageText(Messages.GUI_AUTO_LOGOUT_0);
553            m_loginForm.displayError(text);
554        }
555
556    }
557
558    /**
559     * Shows the password reset dialog.<p>
560     *
561     * @param orgUnit the OU that should be preselected
562     */
563    public void showPasswordResetDialog(String orgUnit) {
564
565        String caption = CmsVaadinUtils.getMessageText(Messages.GUI_PWCHANGE_FORGOT_PASSWORD_0);
566        A_CmsUI r = A_CmsUI.get();
567        r.setContent(new Label());
568        Window window = CmsBasicDialog.prepareWindow(DialogWidth.narrow);
569        CmsBasicDialog dialog = new CmsBasicDialog();
570        VerticalLayout result = new VerticalLayout();
571        dialog.setContent(result);
572        window.setContent(dialog);
573        window.setCaption(caption);
574        window.setClosable(true);
575        final CmsForgotPasswordDialog forgotPassword = new CmsForgotPasswordDialog(orgUnit);
576        window.addCloseListener(new CloseListener() {
577
578            /** Serial version id. */
579            private static final long serialVersionUID = 1L;
580
581            public void windowClose(CloseEvent e) {
582
583                forgotPassword.cancel();
584            }
585
586        });
587        for (Button button : forgotPassword.getButtons()) {
588            dialog.addButton(button);
589        }
590
591        r.addWindow(window);
592        window.center();
593        VerticalLayout vl = result;
594        vl.addComponent(forgotPassword);
595    }
596
597    /**
598     * @see com.vaadin.ui.UI#init(com.vaadin.server.VaadinRequest)
599     */
600    @Override
601    protected void init(VaadinRequest request) {
602
603        addStyleName("login-dialog");
604        LoginParameters params = (LoginParameters)(request.getWrappedSession().getAttribute(INIT_DATA_SESSION_ATTR));
605        if (params == null) {
606            params = CmsLoginHelper.getLoginParameters(getCmsObject(), (HttpServletRequest)request, true);
607            request.getWrappedSession().setAttribute(CmsLoginUI.INIT_DATA_SESSION_ATTR, params);
608        }
609        VaadinSession.getCurrent().setErrorHandler(new CmsVaadinErrorHandler());
610        m_controller = new CmsLoginController(m_adminCms, params);
611        m_controller.setUi(this);
612        setLocale(params.getLocale());
613        m_loginForm = new CmsLoginForm(m_controller, params.getLocale());
614        VaadinServletRequest r1 = ((VaadinServletRequest)request);
615        r1.getLocale();
616        m_controller.onInit();
617        getPage().setTitle(
618            CmsAppWorkplaceUi.WINDOW_TITLE_PREFIX
619                + CmsVaadinUtils.getMessageText(org.opencms.workplace.Messages.GUI_LOGIN_HEADLINE_0));
620        new CmsPollServerExtension(this);
621    }
622}