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