/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.security.sso.cas.internal.servlet.filter; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.module.configuration.ConfigurationProvider; import com.liferay.portal.kernel.servlet.BaseFilter; import com.liferay.portal.kernel.settings.CompanyServiceSettingsLocator; import com.liferay.portal.kernel.util.Http; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.Portal; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.security.sso.cas.configuration.CASConfiguration; import com.liferay.portal.security.sso.cas.constants.CASConstants; import com.liferay.portal.security.sso.cas.constants.CASWebKeys; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.validation.Assertion; import org.jasig.cas.client.validation.Cas20ProxyTicketValidator; import org.jasig.cas.client.validation.TicketValidator; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; /** * Participates in every login and logout that triggers an HTTP request to * Liferay Portal. * * <p> * This class checks if the HTTP session attribute <code>CAS_FORCE_LOGOUT</code> * is set by CASAutoLogin and, if so, redirects the browser to the configured * CAS Logout URL. * </p> * * <p> * Next, if the session attribute <code>CAS_LOGIN</code> has not already been * set and no ticket parameter is received via the HTTP servlet request, the CAS * server login URL is constructed based on the configuration of the Login URL, * the Server name, and the Service URL and the browser is redirected to this * URL. If a ticket parameter was received, it will be validated. * </p> * * <p> * Validation includes sending a SAML request containing the ticket to the CAS * server, and in return receiving an assertion of user attributes. However, * only the principal attribute is used and it is set as the session attribute * <code>CAS_LOGIN</code> as mentioned earlier. It is important that the CAS * server issues a principal of the same type that the portal instance is * configured to use (e.g., screen name versus email address). * </p> * * @author Michael Young * @author Brian Wing Shun Chan * @author Raymond Augé * @author Tina Tian * @author Zsolt Balogh */ @Component( configurationPid = "com.liferay.portal.security.sso.cas.configuration.CASConfiguration", immediate = true, property = { "before-filter=Auto Login Filter", "dispatcher=FORWARD", "dispatcher=REQUEST", "servlet-context-name=", "servlet-filter-name=SSO CAS Filter", "url-pattern=/c/portal/login", "url-pattern=/c/portal/logout" }, service = Filter.class ) public class CASFilter extends BaseFilter { public static void reload(long companyId) { _ticketValidators.remove(companyId); } @Override public boolean isFilterEnabled( HttpServletRequest request, HttpServletResponse response) { try { long companyId = _portal.getCompanyId(request); CASConfiguration casConfiguration = _configurationProvider.getConfiguration( CASConfiguration.class, new CompanyServiceSettingsLocator( companyId, CASConstants.SERVICE_NAME)); if (casConfiguration.enabled()) { return true; } } catch (Exception e) { _log.error(e, e); } return false; } @Override protected Log getLog() { return _log; } protected TicketValidator getTicketValidator(long companyId) throws Exception { TicketValidator ticketValidator = _ticketValidators.get(companyId); if (ticketValidator != null) { return ticketValidator; } CASConfiguration casConfiguration = _configurationProvider.getConfiguration( CASConfiguration.class, new CompanyServiceSettingsLocator( companyId, CASConstants.SERVICE_NAME)); String serverName = casConfiguration.serverName(); String serverUrl = casConfiguration.serverURL(); String loginUrl = casConfiguration.loginURL(); Cas20ProxyTicketValidator cas20ProxyTicketValidator = new Cas20ProxyTicketValidator(serverUrl); Map<String, String> parameters = new HashMap<>(); parameters.put("casServerLoginUrl", loginUrl); parameters.put("casServerUrlPrefix", serverUrl); parameters.put("redirectAfterValidation", "false"); parameters.put("serverName", serverName); cas20ProxyTicketValidator.setCustomParameters(parameters); _ticketValidators.put(companyId, cas20ProxyTicketValidator); return cas20ProxyTicketValidator; } @Override protected void processFilter( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws Exception { HttpSession session = request.getSession(); long companyId = _portal.getCompanyId(request); CASConfiguration casConfiguration = _configurationProvider.getConfiguration( CASConfiguration.class, new CompanyServiceSettingsLocator( companyId, CASConstants.SERVICE_NAME)); String pathInfo = request.getPathInfo(); Object forceLogout = session.getAttribute(CASWebKeys.CAS_FORCE_LOGOUT); if (forceLogout != null) { session.removeAttribute(CASWebKeys.CAS_FORCE_LOGOUT); String logoutUrl = casConfiguration.logoutURL(); response.sendRedirect(logoutUrl); return; } if (Validator.isNotNull(pathInfo) && pathInfo.contains("/portal/logout")) { session.invalidate(); String logoutUrl = casConfiguration.logoutURL(); response.sendRedirect(logoutUrl); return; } else { String login = (String)session.getAttribute(CASWebKeys.CAS_LOGIN); if (Validator.isNotNull(login)) { processFilter( CASFilter.class.getName(), request, response, filterChain); return; } String serverName = casConfiguration.serverName(); String serviceUrl = casConfiguration.serviceURL(); if (Validator.isNull(serviceUrl)) { serviceUrl = CommonUtils.constructServiceUrl( request, response, serviceUrl, serverName, "ticket", false); } String ticket = ParamUtil.getString(request, "ticket"); if (Validator.isNull(ticket)) { String loginUrl = casConfiguration.loginURL(); loginUrl = _http.addParameter(loginUrl, "service", serviceUrl); response.sendRedirect(loginUrl); return; } TicketValidator ticketValidator = getTicketValidator(companyId); Assertion assertion = ticketValidator.validate(ticket, serviceUrl); if (assertion != null) { AttributePrincipal attributePrincipal = assertion.getPrincipal(); login = attributePrincipal.getName(); session.setAttribute(CASWebKeys.CAS_LOGIN, login); } } processFilter( CASFilter.class.getName(), request, response, filterChain); } @Reference(unbind = "-") protected void setConfigurationProvider( ConfigurationProvider configurationProvider) { _configurationProvider = configurationProvider; } private static final Log _log = LogFactoryUtil.getLog(CASFilter.class); private static final Map<Long, TicketValidator> _ticketValidators = new ConcurrentHashMap<>(); private ConfigurationProvider _configurationProvider; @Reference private Http _http; @Reference private Portal _portal; }