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