/**
* 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.auth;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.security.auth.AccessControlContext;
import com.liferay.portal.kernel.security.auth.verifier.AuthVerifier;
import com.liferay.portal.kernel.security.auth.verifier.AuthVerifierConfiguration;
import com.liferay.portal.kernel.security.auth.verifier.AuthVerifierResult;
import com.liferay.portal.kernel.service.UserLocalServiceUtil;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.registry.Filter;
import com.liferay.registry.Registry;
import com.liferay.registry.RegistryUtil;
import com.liferay.registry.ServiceReference;
import com.liferay.registry.ServiceTracker;
import com.liferay.registry.ServiceTrackerCustomizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.http.HttpServletRequest;
import jodd.util.Wildcard;
/**
* @author Tomas Polesovsky
* @author Peter Fellwock
*/
public class AuthVerifierPipeline {
public static final String AUTH_TYPE = "auth.type";
public static String getAuthVerifierPropertyName(String className) {
String simpleClassName = StringUtil.extractLast(
className, StringPool.PERIOD);
return PropsKeys.AUTH_VERIFIER.concat(simpleClassName).concat(
StringPool.PERIOD);
}
public static AuthVerifierResult verifyRequest(
AccessControlContext accessControlContext)
throws PortalException {
return _instance._verifyRequest(accessControlContext);
}
private AuthVerifierPipeline() {
Registry registry = RegistryUtil.getRegistry();
Filter filter = registry.getFilter(
"(objectClass=" + AuthVerifier.class.getName() + ")");
_serviceTracker = registry.trackServices(
filter, new AuthVerifierTrackerCustomizer());
_serviceTracker.open();
}
private AuthVerifierResult _createGuestVerificationResult(
AccessControlContext accessControlContext)
throws PortalException {
AuthVerifierResult authVerifierResult = new AuthVerifierResult();
authVerifierResult.setState(AuthVerifierResult.State.SUCCESS);
HttpServletRequest request = accessControlContext.getRequest();
long companyId = PortalUtil.getCompanyId(request);
long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
authVerifierResult.setUserId(defaultUserId);
return authVerifierResult;
}
private List<AuthVerifierConfiguration> _getAuthVerifierConfigurations(
AccessControlContext accessControlContext) {
HttpServletRequest request = accessControlContext.getRequest();
List<AuthVerifierConfiguration> authVerifierConfigurations =
new ArrayList<>();
String requestURI = request.getRequestURI();
String contextPath = request.getContextPath();
requestURI = requestURI.substring(contextPath.length());
for (AuthVerifierConfiguration authVerifierConfiguration :
_authVerifierConfigurations) {
authVerifierConfiguration = _mergeAuthVerifierConfiguration(
authVerifierConfiguration, accessControlContext);
if (_isMatchingRequestURI(authVerifierConfiguration, requestURI)) {
authVerifierConfigurations.add(authVerifierConfiguration);
}
}
return authVerifierConfigurations;
}
private boolean _isMatchingRequestURI(
AuthVerifierConfiguration authVerifierConfiguration,
String requestURI) {
Properties properties = authVerifierConfiguration.getProperties();
String[] urlsExcludes = StringUtil.split(
properties.getProperty("urls.excludes"));
if ((urlsExcludes.length > 0) &&
(Wildcard.matchOne(requestURI, urlsExcludes) > -1)) {
return false;
}
String[] urlsIncludes = StringUtil.split(
properties.getProperty("urls.includes"));
if (urlsIncludes.length == 0) {
return false;
}
if (Wildcard.matchOne(requestURI, urlsIncludes) > -1) {
return true;
}
return false;
}
private AuthVerifierConfiguration _mergeAuthVerifierConfiguration(
AuthVerifierConfiguration authVerifierConfiguration,
AccessControlContext accessControlContext) {
Map<String, Object> settings = accessControlContext.getSettings();
String authVerifierSettingsKey = getAuthVerifierPropertyName(
authVerifierConfiguration.getAuthVerifierClassName());
boolean merge = false;
Set<String> settingsKeys = settings.keySet();
Iterator<String> iterator = settingsKeys.iterator();
while (iterator.hasNext() && !merge) {
String settingsKey = iterator.next();
if (settingsKey.startsWith(authVerifierSettingsKey)) {
if (settings.get(settingsKey) instanceof String) {
merge = true;
}
}
}
if (!merge) {
return authVerifierConfiguration;
}
AuthVerifierConfiguration mergedAuthVerifierConfiguration =
new AuthVerifierConfiguration();
mergedAuthVerifierConfiguration.setAuthVerifier(
authVerifierConfiguration.getAuthVerifier());
Properties mergedProperties = new Properties(
authVerifierConfiguration.getProperties());
for (Map.Entry<String, Object> entry : settings.entrySet()) {
String settingsKey = entry.getKey();
if (settingsKey.startsWith(authVerifierSettingsKey)) {
Object settingsValue = entry.getValue();
if (settingsValue instanceof String) {
String propertiesKey = settingsKey.substring(
authVerifierSettingsKey.length());
mergedProperties.setProperty(
propertiesKey, (String)settingsValue);
}
}
}
mergedAuthVerifierConfiguration.setProperties(mergedProperties);
return mergedAuthVerifierConfiguration;
}
private Map<String, Object> _mergeSettings(
Properties properties, Map<String, Object> settings) {
Map<String, Object> mergedSettings = new HashMap<>(settings);
if (properties != null) {
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
mergedSettings.put((String)entry.getKey(), entry.getValue());
}
}
return mergedSettings;
}
private AuthVerifierResult _verifyRequest(
AccessControlContext accessControlContext)
throws PortalException {
if (accessControlContext == null) {
throw new IllegalArgumentException(
"Access control context is null");
}
List<AuthVerifierConfiguration> authVerifierConfigurations =
_getAuthVerifierConfigurations(accessControlContext);
for (AuthVerifierConfiguration authVerifierConfiguration :
authVerifierConfigurations) {
AuthVerifierResult authVerifierResult = null;
AuthVerifier authVerifier =
authVerifierConfiguration.getAuthVerifier();
Properties properties = authVerifierConfiguration.getProperties();
try {
authVerifierResult = authVerifier.verify(
accessControlContext, properties);
}
catch (Exception e) {
if (_log.isDebugEnabled()) {
Class<?> authVerifierClass = authVerifier.getClass();
_log.debug("Skipping " + authVerifierClass.getName(), e);
}
continue;
}
if (authVerifierResult == null) {
Class<?> authVerifierClass = authVerifier.getClass();
_log.error(
"Auth verifier " + authVerifierClass.getName() +
" did not return an auth verifier result");
continue;
}
if (authVerifierResult.getState() !=
AuthVerifierResult.State.NOT_APPLICABLE) {
Map<String, Object> settings = _mergeSettings(
properties, authVerifierResult.getSettings());
settings.put(AUTH_TYPE, authVerifier.getAuthType());
authVerifierResult.setSettings(settings);
return authVerifierResult;
}
}
return _createGuestVerificationResult(accessControlContext);
}
private static final Log _log = LogFactoryUtil.getLog(
AuthVerifierPipeline.class);
private static final AuthVerifierPipeline _instance =
new AuthVerifierPipeline();
private final List<AuthVerifierConfiguration> _authVerifierConfigurations =
new CopyOnWriteArrayList<>();
private final ServiceTracker<AuthVerifier, AuthVerifierConfiguration>
_serviceTracker;
private class AuthVerifierTrackerCustomizer
implements
ServiceTrackerCustomizer<AuthVerifier, AuthVerifierConfiguration> {
@Override
public AuthVerifierConfiguration addingService(
ServiceReference<AuthVerifier> serviceReference) {
Registry registry = RegistryUtil.getRegistry();
AuthVerifier authVerifier = registry.getService(serviceReference);
if (authVerifier == null) {
return null;
}
Class<?> authVerifierClass = authVerifier.getClass();
AuthVerifierConfiguration authVerifierConfiguration =
new AuthVerifierConfiguration();
authVerifierConfiguration.setAuthVerifier(authVerifier);
authVerifierConfiguration.setAuthVerifierClassName(
authVerifierClass.getName());
authVerifierConfiguration.setProperties(
_loadProperties(serviceReference, authVerifierClass.getName()));
if (!_validate(authVerifierConfiguration)) {
return null;
}
_authVerifierConfigurations.add(0, authVerifierConfiguration);
return authVerifierConfiguration;
}
@Override
public void modifiedService(
ServiceReference<AuthVerifier> serviceReference,
AuthVerifierConfiguration authVerifierConfiguration) {
_authVerifierConfigurations.remove(authVerifierConfiguration);
authVerifierConfiguration.setProperties(
_loadProperties(
serviceReference,
authVerifierConfiguration.getAuthVerifierClassName()));
if (_validate(authVerifierConfiguration)) {
_authVerifierConfigurations.add(0, authVerifierConfiguration);
}
}
@Override
public void removedService(
ServiceReference<AuthVerifier> serviceReference,
AuthVerifierConfiguration authVerifierConfiguration) {
Registry registry = RegistryUtil.getRegistry();
registry.ungetService(serviceReference);
_authVerifierConfigurations.remove(authVerifierConfiguration);
}
private Properties _loadProperties(
ServiceReference<AuthVerifier> serviceReference,
String authVerifierClassName) {
Properties properties = new Properties();
String authVerifierPropertyName = getAuthVerifierPropertyName(
authVerifierClassName);
Map<String, Object> serviceReferenceProperties =
serviceReference.getProperties();
for (Map.Entry<String, Object> entry :
serviceReferenceProperties.entrySet()) {
String key = entry.getKey();
if (key.startsWith(authVerifierPropertyName)) {
key = key.substring(authVerifierPropertyName.length());
}
Object value = entry.getValue();
properties.setProperty(key, String.valueOf(value));
}
return properties;
}
private boolean _validate(
AuthVerifierConfiguration authVerifierConfiguration) {
Properties properties = authVerifierConfiguration.getProperties();
String[] urlsIncludes = StringUtil.split(
properties.getProperty("urls.includes"));
if (urlsIncludes.length == 0) {
if (_log.isWarnEnabled()) {
String authVerifierClassName =
authVerifierConfiguration.getAuthVerifierClassName();
_log.warn(
"Auth verifier " + authVerifierClassName +
" does not have URLs configured");
}
return false;
}
return true;
}
}
}