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