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.service.UserLocalServiceUtil;
025    import com.liferay.portal.kernel.util.PortalUtil;
026    import com.liferay.portal.kernel.util.PropsKeys;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
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                    if (Wildcard.matchOne(requestURI, urlsIncludes) > -1) {
154                            return true;
155                    }
156    
157                    return false;
158            }
159    
160            private AuthVerifierConfiguration _mergeAuthVerifierConfiguration(
161                    AuthVerifierConfiguration authVerifierConfiguration,
162                    AccessControlContext accessControlContext) {
163    
164                    Map<String, Object> settings = accessControlContext.getSettings();
165    
166                    String authVerifierSettingsKey = getAuthVerifierPropertyName(
167                            authVerifierConfiguration.getAuthVerifierClassName());
168    
169                    boolean merge = false;
170    
171                    Set<String> settingsKeys = settings.keySet();
172    
173                    Iterator<String> iterator = settingsKeys.iterator();
174    
175                    while (iterator.hasNext() && !merge) {
176                            String settingsKey = iterator.next();
177    
178                            if (settingsKey.startsWith(authVerifierSettingsKey)) {
179                                    if (settings.get(settingsKey) instanceof String) {
180                                            merge = true;
181                                    }
182                            }
183                    }
184    
185                    if (!merge) {
186                            return authVerifierConfiguration;
187                    }
188    
189                    AuthVerifierConfiguration mergedAuthVerifierConfiguration =
190                            new AuthVerifierConfiguration();
191    
192                    mergedAuthVerifierConfiguration.setAuthVerifier(
193                            authVerifierConfiguration.getAuthVerifier());
194    
195                    Properties mergedProperties = new Properties(
196                            authVerifierConfiguration.getProperties());
197    
198                    for (Map.Entry<String, Object> entry : settings.entrySet()) {
199                            String settingsKey = entry.getKey();
200    
201                            if (settingsKey.startsWith(authVerifierSettingsKey)) {
202                                    Object settingsValue = entry.getValue();
203    
204                                    if (settingsValue instanceof String) {
205                                            String propertiesKey = settingsKey.substring(
206                                                    authVerifierSettingsKey.length());
207    
208                                            mergedProperties.setProperty(
209                                                    propertiesKey, (String)settingsValue);
210                                    }
211                            }
212                    }
213    
214                    mergedAuthVerifierConfiguration.setProperties(mergedProperties);
215    
216                    return mergedAuthVerifierConfiguration;
217            }
218    
219            private Map<String, Object> _mergeSettings(
220                    Properties properties, Map<String, Object> settings) {
221    
222                    Map<String, Object> mergedSettings = new HashMap<>(settings);
223    
224                    if (properties != null) {
225                            for (Map.Entry<Object, Object> entry : properties.entrySet()) {
226                                    mergedSettings.put((String)entry.getKey(), entry.getValue());
227                            }
228                    }
229    
230                    return mergedSettings;
231            }
232    
233            private AuthVerifierResult _verifyRequest(
234                            AccessControlContext accessControlContext)
235                    throws PortalException {
236    
237                    if (accessControlContext == null) {
238                            throw new IllegalArgumentException(
239                                    "Access control context is null");
240                    }
241    
242                    List<AuthVerifierConfiguration> authVerifierConfigurations =
243                            _getAuthVerifierConfigurations(accessControlContext);
244    
245                    for (AuthVerifierConfiguration authVerifierConfiguration :
246                                    authVerifierConfigurations) {
247    
248                            AuthVerifierResult authVerifierResult = null;
249    
250                            AuthVerifier authVerifier =
251                                    authVerifierConfiguration.getAuthVerifier();
252    
253                            Properties properties = authVerifierConfiguration.getProperties();
254    
255                            try {
256                                    authVerifierResult = authVerifier.verify(
257                                            accessControlContext, properties);
258                            }
259                            catch (Exception e) {
260                                    if (_log.isDebugEnabled()) {
261                                            Class<?> authVerifierClass = authVerifier.getClass();
262    
263                                            _log.debug("Skipping " + authVerifierClass.getName(), e);
264                                    }
265    
266                                    continue;
267                            }
268    
269                            if (authVerifierResult == null) {
270                                    Class<?> authVerifierClass = authVerifier.getClass();
271    
272                                    _log.error(
273                                            "Auth verifier " + authVerifierClass.getName() +
274                                                    " did not return an auth verifier result");
275    
276                                    continue;
277                            }
278    
279                            if (authVerifierResult.getState() !=
280                                            AuthVerifierResult.State.NOT_APPLICABLE) {
281    
282                                    Map<String, Object> settings = _mergeSettings(
283                                            properties, authVerifierResult.getSettings());
284    
285                                    settings.put(AUTH_TYPE, authVerifier.getAuthType());
286    
287                                    authVerifierResult.setSettings(settings);
288    
289                                    return authVerifierResult;
290                            }
291                    }
292    
293                    return _createGuestVerificationResult(accessControlContext);
294            }
295    
296            private static final Log _log = LogFactoryUtil.getLog(
297                    AuthVerifierPipeline.class);
298    
299            private static final AuthVerifierPipeline _instance =
300                    new AuthVerifierPipeline();
301    
302            private final List<AuthVerifierConfiguration> _authVerifierConfigurations =
303                    new CopyOnWriteArrayList<>();
304            private final ServiceTracker<AuthVerifier, AuthVerifierConfiguration>
305                    _serviceTracker;
306    
307            private class AuthVerifierTrackerCustomizer
308                    implements
309                            ServiceTrackerCustomizer<AuthVerifier, AuthVerifierConfiguration> {
310    
311                    @Override
312                    public AuthVerifierConfiguration addingService(
313                            ServiceReference<AuthVerifier> serviceReference) {
314    
315                            Registry registry = RegistryUtil.getRegistry();
316    
317                            AuthVerifier authVerifier = registry.getService(serviceReference);
318    
319                            if (authVerifier == null) {
320                                    return null;
321                            }
322    
323                            Class<?> authVerifierClass = authVerifier.getClass();
324    
325                            AuthVerifierConfiguration authVerifierConfiguration =
326                                    new AuthVerifierConfiguration();
327    
328                            authVerifierConfiguration.setAuthVerifier(authVerifier);
329                            authVerifierConfiguration.setAuthVerifierClassName(
330                                    authVerifierClass.getName());
331                            authVerifierConfiguration.setProperties(
332                                    _loadProperties(serviceReference, authVerifierClass.getName()));
333    
334                            if (!_validate(authVerifierConfiguration)) {
335                                    return null;
336                            }
337    
338                            _authVerifierConfigurations.add(0, authVerifierConfiguration);
339    
340                            return authVerifierConfiguration;
341                    }
342    
343                    @Override
344                    public void modifiedService(
345                            ServiceReference<AuthVerifier> serviceReference,
346                            AuthVerifierConfiguration authVerifierConfiguration) {
347    
348                            AuthVerifierConfiguration newAuthVerifierConfiguration =
349                                    new AuthVerifierConfiguration();
350    
351                            newAuthVerifierConfiguration.setAuthVerifier(
352                                    authVerifierConfiguration.getAuthVerifier());
353                            newAuthVerifierConfiguration.setAuthVerifierClassName(
354                                    authVerifierConfiguration.getAuthVerifierClassName());
355                            newAuthVerifierConfiguration.setProperties(
356                                    _loadProperties(
357                                            serviceReference,
358                                            authVerifierConfiguration.getAuthVerifierClassName()));
359    
360                            if (_authVerifierConfigurations.remove(authVerifierConfiguration)) {
361                                    if (!_validate(authVerifierConfiguration)) {
362                                            return;
363                                    }
364    
365                                    _authVerifierConfigurations.add(
366                                            0, newAuthVerifierConfiguration);
367                            }
368                    }
369    
370                    @Override
371                    public void removedService(
372                            ServiceReference<AuthVerifier> serviceReference,
373                            AuthVerifierConfiguration authVerifierConfiguration) {
374    
375                            Registry registry = RegistryUtil.getRegistry();
376    
377                            registry.ungetService(serviceReference);
378    
379                            _authVerifierConfigurations.remove(authVerifierConfiguration);
380                    }
381    
382                    private Properties _loadProperties(
383                            ServiceReference<AuthVerifier> serviceReference,
384                            String authVerifierClassName) {
385    
386                            Properties properties = new Properties();
387    
388                            String authVerifierPropertyName = getAuthVerifierPropertyName(
389                                    authVerifierClassName);
390    
391                            Map<String, Object> serviceReferenceProperties =
392                                    serviceReference.getProperties();
393    
394                            for (Map.Entry<String, Object> entry :
395                                            serviceReferenceProperties.entrySet()) {
396    
397                                    String key = entry.getKey();
398    
399                                    if (key.startsWith(authVerifierPropertyName)) {
400                                            key = key.substring(authVerifierPropertyName.length());
401                                    }
402    
403                                    Object value = entry.getValue();
404    
405                                    properties.setProperty(key, String.valueOf(value));
406                            }
407    
408                            return properties;
409                    }
410    
411                    private boolean _validate(
412                            AuthVerifierConfiguration authVerifierConfiguration) {
413    
414                            Properties properties = authVerifierConfiguration.getProperties();
415    
416                            String[] urlsIncludes = StringUtil.split(
417                                    properties.getProperty("urls.includes"));
418    
419                            if (urlsIncludes.length == 0) {
420                                    if (_log.isWarnEnabled()) {
421                                            String authVerifierClassName =
422                                                    authVerifierConfiguration.getAuthVerifierClassName();
423    
424                                            _log.warn(
425                                                    "Auth verifier " + authVerifierClassName +
426                                                            " does not have URLs configured");
427                                    }
428    
429                                    return false;
430                            }
431    
432                            return true;
433                    }
434    
435            }
436    
437    }