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 GmbH & Co. KG, 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.jsp; 029 030import org.opencms.db.CmsLoginMessage; 031import org.opencms.file.CmsRequestContext; 032import org.opencms.file.CmsUser; 033import org.opencms.i18n.CmsMessageContainer; 034import org.opencms.main.CmsBroadcast.ContentMode; 035import org.opencms.main.CmsException; 036import org.opencms.main.CmsLog; 037import org.opencms.main.OpenCms; 038import org.opencms.security.CmsAuthentificationException; 039import org.opencms.security.CmsUserLog; 040import org.opencms.security.twofactor.CmsSecondFactorInfo; 041 042import java.io.IOException; 043import java.net.URI; 044import java.util.Date; 045 046import javax.servlet.http.HttpServletRequest; 047import javax.servlet.http.HttpServletResponse; 048import javax.servlet.http.HttpSession; 049import javax.servlet.jsp.PageContext; 050 051import org.apache.commons.logging.Log; 052 053/** 054 * Provides convenient wrappers useful to create user login pages.<p> 055 * 056 * Initialize this bean at the beginning of your JSP like this: 057 * <pre> 058 * <jsp:useBean id="cmslogin" class="org.opencms.jsp.CmsJspLoginBean"> 059 * <% cmslogin.init(pageContext, request, response); %> 060 * </jsp:useBean> 061 * </pre> 062 * <p> 063 * 064 * @since 6.0.0 065 */ 066public class CmsJspLoginBean extends CmsJspActionElement { 067 068 /** The log object for this class. */ 069 private static final Log LOG = CmsLog.getLog(CmsJspLoginBean.class); 070 071 /** Flag to indicate if a login was successful. */ 072 private CmsException m_loginException; 073 074 /** The verification code for 2FA. */ 075 private String m_verificationCode; 076 077 /** 078 * Empty constructor, required for every JavaBean.<p> 079 */ 080 public CmsJspLoginBean() { 081 082 // noop, you must call the init() method after you create an instance 083 } 084 085 /** 086 * Constructor, with parameters.<p> 087 * 088 * @param context the JSP page context object 089 * @param req the JSP request 090 * @param res the JSP response 091 */ 092 public CmsJspLoginBean(PageContext context, HttpServletRequest req, HttpServletResponse res) { 093 094 super(); 095 init(context, req, res); 096 } 097 098 /** 099 * Logs any login exception.<p> 100 * 101 * @param requestContext the request context 102 * @param userName the user name 103 * @param currentLoginException the exception to log 104 */ 105 public static void logLoginException( 106 CmsRequestContext requestContext, 107 String userName, 108 CmsException currentLoginException) { 109 110 if (currentLoginException instanceof CmsAuthentificationException) { 111 112 // the authentication of the user failed 113 if (org.opencms.security.Messages.ERR_LOGIN_FAILED_DISABLED_2 == currentLoginException.getMessageContainer().getKey()) { 114 115 // the user has been disabled 116 LOG.warn( 117 Messages.get().getBundle().key( 118 Messages.LOG_LOGIN_FAILED_DISABLED_3, 119 userName, 120 requestContext.addSiteRoot(requestContext.getUri()), 121 requestContext.getRemoteAddress())); 122 123 } else if (org.opencms.security.Messages.ERR_LOGIN_FAILED_TEMP_DISABLED_4 == currentLoginException.getMessageContainer().getKey()) { 124 125 // the user has been disabled 126 LOG.warn( 127 Messages.get().getBundle().key( 128 Messages.LOG_LOGIN_FAILED_TEMP_DISABLED_5, 129 new Object[] { 130 userName, 131 requestContext.addSiteRoot(requestContext.getUri()), 132 requestContext.getRemoteAddress(), 133 currentLoginException.getMessageContainer().getArgs()[2], 134 currentLoginException.getMessageContainer().getArgs()[3]})); 135 136 } else if (org.opencms.security.Messages.ERR_LOGIN_FAILED_NO_USER_2 == currentLoginException.getMessageContainer().getKey()) { 137 138 // the requested user does not exist in the database 139 LOG.warn( 140 Messages.get().getBundle().key( 141 Messages.LOG_LOGIN_FAILED_NO_USER_3, 142 userName, 143 requestContext.addSiteRoot(requestContext.getUri()), 144 requestContext.getRemoteAddress())); 145 146 } else if (org.opencms.security.Messages.ERR_LOGIN_FAILED_WITH_MESSAGE_1 == currentLoginException.getMessageContainer().getKey()) { 147 148 // logins have been disabled by the administration 149 long endTime = CmsLoginMessage.DEFAULT_TIME_END; 150 if (OpenCms.getLoginManager().getLoginMessage() != null) { 151 endTime = OpenCms.getLoginManager().getLoginMessage().getTimeEnd(); 152 } 153 LOG.info( 154 Messages.get().getBundle().key( 155 Messages.LOG_LOGIN_FAILED_WITH_MESSAGE_4, 156 new Object[] { 157 userName, 158 requestContext.addSiteRoot(requestContext.getUri()), 159 requestContext.getRemoteAddress(), 160 new Date(endTime)})); 161 162 } else { 163 164 // the user exists, so the password must have been wrong 165 CmsMessageContainer message = Messages.get().container( 166 Messages.LOG_LOGIN_FAILED_3, 167 userName, 168 requestContext.addSiteRoot(requestContext.getUri()), 169 requestContext.getRemoteAddress()); 170 if (OpenCms.getDefaultUsers().isUserAdmin(userName)) { 171 // someone tried to log in as "Admin", log this in a higher channel 172 LOG.error(message.key()); 173 } else { 174 LOG.warn(message.key()); 175 } 176 } 177 } else { 178 // the error was database related, there may be an issue with the setup 179 // write the exception to the log as well 180 LOG.error( 181 Messages.get().getBundle().key( 182 Messages.LOG_LOGIN_FAILED_DB_REASON_3, 183 userName, 184 requestContext.addSiteRoot(requestContext.getUri()), 185 requestContext.getRemoteAddress()), 186 currentLoginException); 187 } 188 } 189 190 /** 191 * Returns the link to the form that contains the login element.<p> 192 * 193 * @return the link to the form that contains the login element 194 */ 195 public String getFormLink() { 196 197 return link(getRequestContext().getUri()); 198 } 199 200 /** 201 * Returns the exception that was thrown after login, 202 * or null if no Exception was thrown (i.e. login was successful 203 * or not attempted).<p> 204 * 205 * @return the exception thrown after login 206 */ 207 public CmsException getLoginException() { 208 209 return m_loginException; 210 } 211 212 /** 213 * Returns the currently logged in user.<p> 214 * 215 * @return the currently logged in user 216 */ 217 public CmsUser getUser() { 218 219 return getRequestContext().getCurrentUser(); 220 } 221 222 /** 223 * Returns the user name of the currently logged in user.<p> 224 * 225 * @return the user name of the currently logged in user 226 */ 227 public String getUserName() { 228 229 return getRequestContext().getCurrentUser().getName(); 230 } 231 232 /** 233 * Returns true if the current user is not the guest user, 234 * i.e. if he already has logged in with some other user account.<p> 235 * 236 * @return true if the current user is already logged in 237 */ 238 public boolean isLoggedIn() { 239 240 return !getCmsObject().getRequestContext().getCurrentUser().isGuestUser(); 241 } 242 243 /** 244 * Indicates if a login was successful or not.<p> 245 * 246 * @return true if the login was successful 247 */ 248 public boolean isLoginSuccess() { 249 250 return (m_loginException == null); 251 } 252 253 /** 254 * Logs a system user in to OpenCms.<p> 255 * 256 * @param userName the users name 257 * @param password the password 258 */ 259 public void login(String userName, String password) { 260 261 login(userName, password, null); 262 } 263 264 /** 265 * Logs a system user into OpenCms.<p> 266 * 267 * Note that if a login project name is provided, this project must exist, 268 * otherwise the login is regarded as a failure even if the user data was correct.<p> 269 * 270 * @param userName the users name 271 * @param password the password 272 * @param projectName the project to switch to after login (if null project is not switched) 273 */ 274 public void login(String userName, String password, String projectName) { 275 276 HttpSession session = null; 277 m_loginException = null; 278 try { 279 280 // login the user and create a new session 281 CmsUser user = getCmsObject().readUser(userName); 282 OpenCms.getSessionManager().checkCreateSessionForUser(user); 283 CmsSecondFactorInfo secondFactorInfo = null; 284 if (m_verificationCode != null) { 285 secondFactorInfo = new CmsSecondFactorInfo(m_verificationCode); 286 } 287 getCmsObject().loginUser(userName, password, secondFactorInfo, getRequestContext().getRemoteAddress()); 288 289 // make sure we have a new session after login for security reasons 290 session = getRequest().getSession(false); 291 if (session != null) { 292 session.invalidate(); 293 } 294 session = getRequest().getSession(true); 295 if (projectName != null) { 296 // if this fails, the login is regarded as a failure as well 297 getCmsObject().getRequestContext().setCurrentProject(getCmsObject().readProject(projectName)); 298 } 299 if (!getCmsObject().getRequestContext().getCurrentProject().isOnlineProject()) { 300 // in case the user is logged into an offline project, send any available login message 301 CmsLoginMessage loginMessage = OpenCms.getLoginManager().getLoginMessage(); 302 if ((loginMessage != null) && loginMessage.isActive()) { 303 OpenCms.getSessionManager().updateSessionInfo(getCmsObject(), getRequest()); 304 OpenCms.getSessionManager().sendBroadcast(null, loginMessage.getMessage(), user, ContentMode.html); 305 } 306 } 307 308 } catch (CmsException e) { 309 // the login has failed 310 m_loginException = e; 311 } 312 if (m_loginException == null) { 313 // login was successful 314 if (LOG.isInfoEnabled()) { 315 LOG.info( 316 Messages.get().getBundle().key( 317 Messages.LOG_LOGIN_SUCCESSFUL_3, 318 userName, 319 getRequestContext().addSiteRoot(getRequestContext().getUri()), 320 getRequestContext().getRemoteAddress())); 321 } 322 CmsUserLog.logLogin(getCmsObject(), userName); 323 } else { 324 // login was not successful 325 if (session != null) { 326 session.invalidate(); 327 } 328 CmsException currentLoginException = m_loginException; 329 logLoginException(getRequestContext(), userName, currentLoginException); 330 CmsUserLog.logLoginFailure(getCmsObject(), userName); 331 } 332 } 333 334 /** 335 * Logs a system user in to OpenCms.<p> 336 * 337 * Note that if a login project name is provided, this project must exist, 338 * otherwise the login is regarded as a failure even if the user data was correct.<p> 339 * 340 * @param userName the users name 341 * @param password the password 342 * @param projectName the project to switch to after login (if null project is not switched) 343 * @param redirectUri the URI to redirect to after login (if null the current URI is used) 344 * 345 * @throws IOException in case redirect after login was not successful 346 */ 347 public void login(String userName, String password, String projectName, String redirectUri) throws IOException { 348 349 login(userName, password, projectName); 350 if (m_loginException == null) { 351 try { 352 URI uriObj = new URI(redirectUri); 353 if (uriObj.getScheme() != null) { 354 LOG.warn("Absolute URL not allowed as redirect URI: " + redirectUri); 355 return; 356 } 357 } catch (Exception e) { 358 LOG.warn("Invalid redirect URI " + redirectUri + " in login bean: " + e.getLocalizedMessage(), e); 359 return; 360 } 361 if (redirectUri != null) { 362 getResponse().sendRedirect( 363 OpenCms.getLinkManager().substituteLink(getCmsObject(), redirectUri, null, true)); 364 } else { 365 getResponse().sendRedirect(getFormLink()); 366 } 367 } 368 } 369 370 /** 371 * Logs a user out, i.e. destroys the current users session, 372 * after that the current page will be redirected to itself one time to ensure that 373 * the users session is truly destroyed.<p> 374 * 375 * @throws IOException if redirect after logout fails 376 */ 377 public void logout() throws IOException { 378 379 String loggedInUserName = getRequestContext().getCurrentUser().getName(); 380 HttpSession session = getRequest().getSession(false); 381 if (session != null) { 382 session.invalidate(); 383 /* we need this because a new session might be created after this method, 384 but before the session info is updated in OpenCmsCore.showResource. */ 385 getCmsObject().getRequestContext().setUpdateSessionEnabled(false); 386 } 387 // logout was successful 388 if (LOG.isInfoEnabled()) { 389 LOG.info( 390 Messages.get().getBundle().key( 391 Messages.LOG_LOGOUT_SUCCESFUL_3, 392 loggedInUserName, 393 getRequestContext().addSiteRoot(getRequestContext().getUri()), 394 getRequestContext().getRemoteAddress())); 395 } 396 CmsUserLog.logLogout(getCmsObject()); 397 getResponse().sendRedirect(getFormLink()); 398 } 399 400 /** 401 * Sets the verification code for two-factor authentication. 402 * 403 * @param code the verification code 404 */ 405 public void setVerificationCode(String code) { 406 407 m_verificationCode = code; 408 } 409}