001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.security.auth;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.security.auth.AccessControlContext;
021    import com.liferay.portal.kernel.security.auth.verifier.AuthVerifier;
022    import com.liferay.portal.kernel.security.auth.verifier.AuthVerifierConfiguration;
023    import com.liferay.portal.kernel.security.auth.verifier.AuthVerifierResult;
024    import com.liferay.portal.kernel.util.PropsKeys;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.service.UserLocalServiceUtil;
028    import com.liferay.portal.util.PortalUtil;
029    import com.liferay.registry.Filter;
030    import com.liferay.registry.Registry;
031    import com.liferay.registry.RegistryUtil;
032    import com.liferay.registry.ServiceReference;
033    import com.liferay.registry.ServiceTracker;
034    import com.liferay.registry.ServiceTrackerCustomizer;
035    
036    import java.util.ArrayList;
037    import java.util.HashMap;
038    import java.util.Iterator;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Properties;
042    import java.util.Set;
043    import java.util.concurrent.CopyOnWriteArrayList;
044    
045    import javax.servlet.http.HttpServletRequest;
046    
047    import jodd.util.Wildcard;
048    
049    /**
050     * @author Tomas Polesovsky
051     * @author Peter Fellwock
052     */
053    public class AuthVerifierPipeline {
054    
055            public static final String AUTH_TYPE = "auth.type";
056    
057            public static String getAuthVerifierPropertyName(String className) {
058                    String simpleClassName = StringUtil.extractLast(
059                            className, StringPool.PERIOD);
060    
061                    return PropsKeys.AUTH_VERIFIER.concat(simpleClassName).concat(
062                            StringPool.PERIOD);
063            }
064    
065            public static AuthVerifierResult verifyRequest(
066                            AccessControlContext accessControlContext)
067                    throws PortalException {
068    
069                    return _instance._verifyRequest(accessControlContext);
070            }
071    
072            private AuthVerifierPipeline() {
073                    Registry registry = RegistryUtil.getRegistry();
074    
075                    Filter filter = registry.getFilter(
076                            "(objectClass=" + AuthVerifier.class.getName() + ")");
077    
078                    _serviceTracker = registry.trackServices(
079                            filter, new AuthVerifierTrackerCustomizer());
080    
081                    _serviceTracker.open();
082            }
083    
084            private AuthVerifierResult _createGuestVerificationResult(
085                            AccessControlContext accessControlContext)
086                    throws PortalException {
087    
088                    AuthVerifierResult authVerifierResult = new AuthVerifierResult();
089    
090                    authVerifierResult.setState(AuthVerifierResult.State.SUCCESS);
091    
092                    HttpServletRequest request = accessControlContext.getRequest();
093    
094                    long companyId = PortalUtil.getCompanyId(request);
095    
096                    long defaultUserId = UserLocalServiceUtil.getDefaultUserId(companyId);
097    
098                    authVerifierResult.setUserId(defaultUserId);
099    
100                    return authVerifierResult;
101            }
102    
103            private List<AuthVerifierConfiguration> _getAuthVerifierConfigurations(
104                    AccessControlContext accessControlContext) {
105    
106                    HttpServletRequest request = accessControlContext.getRequest();
107    
108                    List<AuthVerifierConfiguration> authVerifierConfigurations =
109                            new ArrayList<>();
110    
111                    String requestURI = request.getRequestURI();
112    
113                    String contextPath = request.getContextPath();
114    
115                    requestURI = requestURI.substring(contextPath.length());
116    
117                    for (AuthVerifierConfiguration authVerifierConfiguration :
118                                    _authVerifierConfigurations) {
119    
120                            authVerifierConfiguration = _mergeAuthVerifierConfiguration(
121                                    authVerifierConfiguration, accessControlContext);
122    
123                            if (_isMatchingRequestURI(authVerifierConfiguration, requestURI)) {
124                                    authVerifierConfigurations.add(authVerifierConfiguration);
125                            }
126                    }
127    
128                    return authVerifierConfigurations;
129            }
130    
131            private boolean _isMatchingRequestURI(
132                    AuthVerifierConfiguration authVerifierConfiguration,
133                    String requestURI) {
134    
135                    Properties properties = authVerifierConfiguration.getProperties();
136    
137                    String[] urlsExcludes = StringUtil.split(
138                            properties.getProperty("urls.excludes"));
139    
140                    if ((urlsExcludes.length > 0) &&
141                            (Wildcard.matchOne(requestURI, urlsExcludes) > -1)) {
142    
143                            return false;
144                    }
145    
146                    String[] urlsIncludes = StringUtil.split(
147                            properties.getProperty("urls.includes"));
148    
149                    if (urlsIncludes.length == 0) {
150                            return false;
151                    }
152    
153                    return Wildcard.matchOne(requestURI, urlsIncludes) > -1;
154            }
155    
156            private AuthVerifierConfiguration _mergeAuthVerifierConfiguration(
157                    AuthVerifierConfiguration authVerifierConfiguration,
158                    AccessControlContext accessControlContext) {
159    
160                    Map<String, Object> settings = accessControlContext.getSettings();
161    
162                    String authVerifierSettingsKey = getAuthVerifierPropertyName(
163                            authVerifierConfiguration.getAuthVerifierClassName());
164    
165                    boolean merge = false;
166    
167                    Set<String> settingsKeys = settings.keySet();
168    
169                    Iterator<String> iterator = settingsKeys.iterator();
170    
171                    while (iterator.hasNext() && !merge) {
172                            String settingsKey = iterator.next();
173    
174                            if (settingsKey.startsWith(authVerifierSettingsKey)) {
175                                    if (settings.get(settingsKey) instanceof String) {
176                                            merge = true;
177                                    }
178                            }
179                    }
180    
181                    if (!merge) {
182                            return authVerifierConfiguration;
183                    }
184    
185                    AuthVerifierConfiguration mergedAuthVerifierConfiguration =
186                            new AuthVerifierConfiguration();
187    
188                    mergedAuthVerifierConfiguration.setAuthVerifier(
189                            authVerifierConfiguration.getAuthVerifier());
190    
191                    Properties mergedProperties = new Properties(
192                            authVerifierConfiguration.getProperties());
193    
194                    for (String settingsKey : settings.keySet()) {
195                            if (settingsKey.startsWith(authVerifierSettingsKey)) {
196                                    Object settingsValue = settings.get(settingsKey);
197    
198                                    if (settingsValue instanceof String) {
199                                            String propertiesKey = settingsKey.substring(
200                                                    authVerifierSettingsKey.length());
201    
202                                            mergedProperties.setProperty(
203                                                    propertiesKey, (String)settingsValue);
204                                    }
205                            }
206                    }
207    
208                    mergedAuthVerifierConfiguration.setProperties(mergedProperties);
209    
210                    return mergedAuthVerifierConfiguration;
211            }
212    
213            private Map<String, Object> _mergeSettings(
214                    Properties properties, Map<String, Object> settings) {
215    
216                    Map<String, Object> mergedSettings = new HashMap<>(settings);
217    
218                    if (properties != null) {
219                            for (Map.Entry<Object, Object> entry : properties.entrySet()) {
220                                    mergedSettings.put((String)entry.getKey(), entry.getValue());
221                            }
222                    }
223    
224                    return mergedSettings;
225            }
226    
227            private AuthVerifierResult _verifyRequest(
228                            AccessControlContext accessControlContext)
229                    throws PortalException {
230    
231                    if (accessControlContext == null) {
232                            throw new IllegalArgumentException(
233                                    "Access control context is null");
234                    }
235    
236                    List<AuthVerifierConfiguration> authVerifierConfigurations =
237                            _getAuthVerifierConfigurations(accessControlContext);
238    
239                    for (AuthVerifierConfiguration authVerifierConfiguration :
240                                    authVerifierConfigurations) {
241    
242                            AuthVerifierResult authVerifierResult = null;
243    
244                            AuthVerifier authVerifier =
245                                    authVerifierConfiguration.getAuthVerifier();
246    
247                            Properties properties = authVerifierConfiguration.getProperties();
248    
249                            try {
250                                    authVerifierResult = authVerifier.verify(
251                                            accessControlContext, properties);
252                            }
253                            catch (Exception e) {
254                                    if (_log.isDebugEnabled()) {
255                                            Class<?> authVerifierClass = authVerifier.getClass();
256    
257                                            _log.debug("Skipping " + authVerifierClass.getName(), e);
258                                    }
259    
260                                    continue;
261                            }
262    
263                            if (authVerifierResult == null) {
264                                    Class<?> authVerifierClass = authVerifier.getClass();
265    
266                                    _log.error(
267                                            "Auth verifier " + authVerifierClass.getName() +
268                                                    " did not return an auth verifier result");
269    
270                                    continue;
271                            }
272    
273                            if (authVerifierResult.getState() !=
274                                            AuthVerifierResult.State.NOT_APPLICABLE) {
275    
276                                    Map<String, Object> settings = _mergeSettings(
277                                            properties, authVerifierResult.getSettings());
278    
279                                    settings.put(AUTH_TYPE, authVerifier.getAuthType());
280    
281                                    authVerifierResult.setSettings(settings);
282    
283                                    return authVerifierResult;
284                            }
285                    }
286    
287                    return _createGuestVerificationResult(accessControlContext);
288            }
289    
290            private static final Log _log = LogFactoryUtil.getLog(
291                    AuthVerifierPipeline.class);
292    
293            private static final AuthVerifierPipeline _instance =
294                    new AuthVerifierPipeline();
295    
296            private final List<AuthVerifierConfiguration> _authVerifierConfigurations =
297                    new CopyOnWriteArrayList<>();
298            private final ServiceTracker<AuthVerifier, AuthVerifierConfiguration>
299                    _serviceTracker;
300    
301            private class AuthVerifierTrackerCustomizer
302                    implements
303                            ServiceTrackerCustomizer<AuthVerifier, AuthVerifierConfiguration> {
304    
305                    @Override
306                    public AuthVerifierConfiguration addingService(
307                            ServiceReference<AuthVerifier> serviceReference) {
308    
309                            Registry registry = RegistryUtil.getRegistry();
310    
311                            AuthVerifier authVerifier = registry.getService(serviceReference);
312    
313                            if (authVerifier == null) {
314                                    return null;
315                            }
316    
317                            Class<?> authVerifierClass = authVerifier.getClass();
318    
319                            AuthVerifierConfiguration authVerifierConfiguration =
320                                    new AuthVerifierConfiguration();
321    
322                            authVerifierConfiguration.setAuthVerifier(authVerifier);
323                            authVerifierConfiguration.setAuthVerifierClassName(
324                                    authVerifierClass.getName());
325                            authVerifierConfiguration.setProperties(
326                                    _loadProperties(serviceReference, authVerifierClass.getName()));
327    
328                            if (!_validate(authVerifierConfiguration)) {
329                                    return null;
330                            }
331    
332                            _authVerifierConfigurations.add(0, authVerifierConfiguration);
333    
334                            return authVerifierConfiguration;
335                    }
336    
337                    @Override
338                    public void modifiedService(
339                            ServiceReference<AuthVerifier> serviceReference,
340                            AuthVerifierConfiguration authVerifierConfiguration) {
341    
342                            AuthVerifierConfiguration newAuthVerifierConfiguration =
343                                    new AuthVerifierConfiguration();
344    
345                            newAuthVerifierConfiguration.setAuthVerifier(
346                                    authVerifierConfiguration.getAuthVerifier());
347                            newAuthVerifierConfiguration.setAuthVerifierClassName(
348                                    authVerifierConfiguration.getAuthVerifierClassName());
349                            newAuthVerifierConfiguration.setProperties(
350                                    _loadProperties(
351                                            serviceReference,
352                                            authVerifierConfiguration.getAuthVerifierClassName()));
353    
354                            if (_authVerifierConfigurations.remove(authVerifierConfiguration)) {
355                                    if (!_validate(authVerifierConfiguration)) {
356                                            return;
357                                    }
358    
359                                    _authVerifierConfigurations.add(
360                                            0, newAuthVerifierConfiguration);
361                            }
362                    }
363    
364                    @Override
365                    public void removedService(
366                            ServiceReference<AuthVerifier> serviceReference,
367                            AuthVerifierConfiguration authVerifierConfiguration) {
368    
369                            Registry registry = RegistryUtil.getRegistry();
370    
371                            registry.ungetService(serviceReference);
372    
373                            _authVerifierConfigurations.remove(authVerifierConfiguration);
374                    }
375    
376                    private Properties _loadProperties(
377                            ServiceReference<AuthVerifier> serviceReference,
378                            String authVerifierClassName) {
379    
380                            Properties properties = new Properties();
381    
382                            String authVerifierPropertyName = getAuthVerifierPropertyName(
383                                    authVerifierClassName);
384    
385                            Map<String, Object> serviceReferenceProperties =
386                                    serviceReference.getProperties();
387    
388                            for (String key : serviceReferenceProperties.keySet()) {
389                                    String propertiesKey = key;
390    
391                                    if (key.startsWith(authVerifierPropertyName)) {
392                                            propertiesKey = key.substring(
393                                                    authVerifierPropertyName.length());
394                                    }
395    
396                                    Object value = serviceReferenceProperties.get(key);
397    
398                                    properties.setProperty(propertiesKey, String.valueOf(value));
399                            }
400    
401                            return properties;
402                    }
403    
404                    private boolean _validate(
405                            AuthVerifierConfiguration authVerifierConfiguration) {
406    
407                            Properties properties = authVerifierConfiguration.getProperties();
408    
409                            String[] urlsIncludes = StringUtil.split(
410                                    properties.getProperty("urls.includes"));
411    
412                            if (urlsIncludes.length == 0) {
413                                    if (_log.isWarnEnabled()) {
414                                            String authVerifierClassName =
415                                                    authVerifierConfiguration.getAuthVerifierClassName();
416    
417                                            _log.warn(
418                                                    "Auth verifier " + authVerifierClassName +
419                                                            " does not have URLs configured");
420                                    }
421    
422                                    return false;
423                            }
424    
425                            return true;
426                    }
427    
428            }
429    
430    }