/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.dispatcher;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.olat.NewControllerFactory;
import org.olat.basesecurity.AuthHelper;
import org.olat.core.CoreSpringFactory;
import org.olat.core.dispatcher.Dispatcher;
import org.olat.core.dispatcher.DispatcherModule;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.UserRequestImpl;
import org.olat.core.gui.WindowSettings;
import org.olat.core.gui.Windows;
import org.olat.core.gui.components.Window;
import org.olat.core.gui.components.form.flexible.impl.InvalidRequestParameterException;
import org.olat.core.gui.control.ChiefController;
import org.olat.core.gui.control.WindowBackOffice;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.exception.MsgFactory;
import org.olat.core.gui.media.ServletUtil;
import org.olat.core.id.context.BusinessControl;
import org.olat.core.id.context.BusinessControlFactory;
import org.olat.core.id.context.HistoryPoint;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.SessionInfo;
import org.olat.core.util.StringHelper;
import org.olat.core.util.UserSession;
import org.olat.core.util.i18n.I18nManager;
import org.olat.core.util.i18n.I18nModule;
import org.olat.core.util.session.UserSessionManager;
import org.olat.core.util.threadlog.UserBasedLogLevelManager;
import org.olat.login.LoginModule;
/**
* Initial Date: 28.11.2003
*
* @author Mike Stock
*/
public class AuthenticatedDispatcher implements Dispatcher {
private static final OLog log = Tracing.createLoggerFor(AuthenticatedDispatcher.class);
protected static final String AUTHDISPATCHER_BUSINESSPATH = "AuthDispatcher:businessPath";
protected static final String QUESTIONMARK = "?";
protected static final String GUEST = "guest";
protected static final String INVITATION = "invitation";
protected static final String TRUE = "true";
/** forces secure http connection to access olat if set to true **/
private boolean forceSecureAccessOnly = false;
private UserBasedLogLevelManager userBasedLogLevelManager = UserBasedLogLevelManager.getInstance();
public AuthenticatedDispatcher(boolean forceSecureAccessOnly) {
this.forceSecureAccessOnly = forceSecureAccessOnly;
}
/**
* Main method called by OpenOLATServlet. This processess all requests for
* authenticated users.
*
* @param request
* @param response
* @param uriPrefix
*/
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) {
String uriPrefix = DispatcherModule.getLegacyUriPrefix(request);
UserSession usess = CoreSpringFactory.getImpl(UserSessionManager.class).getUserSession(request);
UserRequest ureq = null;
try{
//upon creation URL is checked for
ureq = new UserRequestImpl(uriPrefix, request, response);
} catch(NumberFormatException nfe) {
//MODE could not be decoded
//typically if robots with wrong urls hit the system
//or user have bookmarks
//or authors copy-pasted links to the content.
//showing redscreens for non valid URL is wrong instead
//a 404 message must be shown -> e.g. robots correct their links.
if(log.isDebug()){
log.debug("Bad Request "+request.getPathInfo());
}
}
boolean auth = usess.isAuthenticated();
if (!auth) {
String guestAccess = ureq.getParameter(GUEST);
if (guestAccess == null || !CoreSpringFactory.getImpl(LoginModule.class).isGuestLoginEnabled()) {
String businessPath = extractBusinessPath( ureq, request, uriPrefix);
if(businessPath != null) {
usess.putEntryInNonClearedStore(AUTHDISPATCHER_BUSINESSPATH, businessPath);
}
redirectToDefaultDispatcher(request, response);
return;
} else if (guestAccess.equals(TRUE)) {
// try to log in as anonymous
// use the language from the lang parameter if available, otherwise use the system default locale
String guestLang = ureq.getParameter("language");
if (guestLang == null) {
// support for legacy lang parameter
guestLang = ureq.getParameter("lang");
}
Locale guestLoc;
if (guestLang == null) {
guestLoc = I18nModule.getDefaultLocale();
} else {
guestLoc = I18nManager.getInstance().getLocaleOrDefault(guestLang);
}
int loginStatus = AuthHelper.doAnonymousLogin(ureq, guestLoc);
if ( loginStatus != AuthHelper.LOGIN_OK) {
if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE) {
DispatcherModule.redirectToServiceNotAvailable(response);
}
redirectToDefaultDispatcher(request, response); // error, redirect to login screen
return;
}
// else now logged in as anonymous user, continue
}
}
// authenticated!
try {
//kill session if not secured via SSL
if (forceSecureAccessOnly && !request.isSecure()) {
SessionInfo sessionInfo = usess.getSessionInfo();
if (sessionInfo!=null) {
HttpSession session = sessionInfo.getSession();
if (session!=null) {
try{
session.invalidate();
} catch(IllegalStateException ise) {
// thrown when session already invalidated. fine. ignore.
}
}
}
redirectToDefaultDispatcher(request, response);
return;
}
SessionInfo sessionInfo = usess.getSessionInfo();
if (sessionInfo == null) {
redirectToDefaultDispatcher(request,response);
return;
}
if (userBasedLogLevelManager != null) {
userBasedLogLevelManager.activateUsernameBasedLogLevel(sessionInfo.getLogin());
}
sessionInfo.setLastClickTime();
String businessPath = (String) usess.removeEntryFromNonClearedStore(AUTHDISPATCHER_BUSINESSPATH);
if (businessPath != null) {
processBusinessPath(businessPath, ureq, usess);
} else if (ureq.isValidDispatchURI()) {
// valid uri for dispatching (has timestamp, componentid and windowid)
processValidDispatchURI(ureq, usess, request, response);
} else {
businessPath = extractBusinessPath(ureq, request, uriPrefix);
if(businessPath == null) {
processBusinessPath("", ureq, usess);
} else {
processBusinessPath(businessPath, ureq, usess);
}
}
} catch (InvalidRequestParameterException e) {
try {
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
} catch (IOException e1) {
log.error("An exception occured while handling the invalid request parameter exception...", e1);
}
} catch (Throwable th) {
// Do not log as Warn or Error here, log as ERROR in MsgFactory => ExceptionWindowController throws an OLATRuntimeException
log.debug("handleError in AuthenticatedDispatcher throwable=" + th);
DispatcherModule.handleError();
ChiefController msgcc = MsgFactory.createMessageChiefController(ureq, th);
// the controller's window must be failsafe also
msgcc.getWindow().dispatchRequest(ureq, true);
// do not dispatch (render only), since this is a new Window created as
// a result of another window's click.
} finally {
if (userBasedLogLevelManager != null) {
userBasedLogLevelManager.deactivateUsernameBasedLogLevel();
}
}
}
private String extractBusinessPath(UserRequest ureq, HttpServletRequest request, String uriPrefix) {
final String origUri = request.getRequestURI();
String restPart = origUri.substring(uriPrefix.length());
try {
restPart = URLDecoder.decode(restPart, "UTF8");
} catch (UnsupportedEncodingException e) {
log.error("Unsupported encoding", e);
}
if(restPart.startsWith("repo/go")) {
return convertJumpInURL(ureq);
}
String[] split = restPart.split("/");
if (split.length > 0 && split.length % 2 == 0) {
return BusinessControlFactory.getInstance().formatFromSplittedURI(split);
}
return null;
}
/**
* http://localhost:8080/olat/auth/repo/go?rid=819242&par=77013818723561
* @param requestPart
* @param ureq
* @return
*/
private String convertJumpInURL(UserRequest ureq) {
String repoId = ureq.getParameter("rid");
String businessPath = "[RepositoryEntry:" + repoId + "]";
String par = ureq.getParameter("par");
if(StringHelper.containsNonWhitespace(par) && StringHelper.isLong(par)) {
try {
Long parLong = Long.parseLong(par);
businessPath += "[Part:" + parLong + "]";
} catch(NumberFormatException e) {
//it can happen
}
}
return businessPath;
}
private void processValidDispatchURI(UserRequest ureq, UserSession usess, HttpServletRequest request, HttpServletResponse response) {
Windows ws = Windows.getWindows(ureq);
Window window = ws.getWindow(ureq);
if (window == null) {
//probably a
if(usess.isSavedSession() && !usess.getHistoryStack().isEmpty()) {
redirectToDefaultDispatcher(request, response);
} else {
DispatcherModule.sendNotFound(request.getRequestURI(), response);
}
} else {
window.dispatchRequest(ureq);
}
}
private void redirectToDefaultDispatcher(HttpServletRequest request, HttpServletResponse response) {
if(ServletUtil.acceptJson(request)) {
try {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e) {
log.error("", e);
}
} else {
DispatcherModule.redirectToDefaultDispatcher(response);
}
}
private void processBusinessPath(String businessPath, UserRequest ureq, UserSession usess) {
ChiefController chiefController = Windows.getWindows(usess).getChiefController();
if(chiefController == null) {
if(usess.isAuthenticated()) {
AuthHelper.createAuthHome(ureq).getWindow();
chiefController = Windows.getWindows(usess).getChiefController();
} else {
redirectToDefaultDispatcher(ureq.getHttpReq(), ureq.getHttpResp());
return;
}
}
WindowBackOffice windowBackOffice = chiefController.getWindow().getWindowBackOffice();
if(chiefController.isLoginInterceptionInProgress()) {
Window w = windowBackOffice.getWindow();
w.dispatchRequest(ureq, true); // renderOnly
} else {
String wSettings = (String) usess.removeEntryFromNonClearedStore(WINDOW_SETTINGS);
if(wSettings != null) {
WindowSettings settings = WindowSettings.parse(wSettings);
windowBackOffice.setWindowSettings(settings);
}
try {
BusinessControl bc = null;
String historyPointId = ureq.getHttpReq().getParameter("historyPointId");
if(StringHelper.containsNonWhitespace(historyPointId)) {
HistoryPoint point = ureq.getUserSession().getHistoryPoint(historyPointId);
bc = BusinessControlFactory.getInstance().createFromContextEntries(point.getEntries());
}
if(bc == null) {
bc = BusinessControlFactory.getInstance().createFromString(businessPath);
}
WindowControl wControl = windowBackOffice.getChiefController().getWindowControl();
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, wControl);
NewControllerFactory.getInstance().launch(ureq, bwControl);
// render the window
Window w = windowBackOffice.getWindow();
w.dispatchRequest(ureq, true); // renderOnly
} catch (Exception e) {
// try to render something
try {
Window w = windowBackOffice.getWindow();
w.dispatchRequest(ureq, true); // renderOnly
} catch (Exception e1) {
redirectToDefaultDispatcher(ureq.getHttpReq(), ureq.getHttpResp());
}
log.error("", e);
}
}
}
}