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