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.concurrent.ConcurrentHashSet;
018    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
019    import com.liferay.portal.kernel.util.CharPool;
020    import com.liferay.portal.kernel.util.PropsKeys;
021    import com.liferay.portal.kernel.util.SetUtil;
022    import com.liferay.portal.kernel.util.Validator;
023    import com.liferay.portal.model.Portlet;
024    import com.liferay.portal.model.PortletConstants;
025    import com.liferay.portal.service.PortletLocalServiceUtil;
026    import com.liferay.portal.util.PropsValues;
027    import com.liferay.registry.Registry;
028    import com.liferay.registry.RegistryUtil;
029    import com.liferay.registry.ServiceReference;
030    import com.liferay.registry.ServiceRegistration;
031    import com.liferay.registry.ServiceTracker;
032    import com.liferay.registry.ServiceTrackerCustomizer;
033    import com.liferay.registry.collections.StringServiceRegistrationMap;
034    import com.liferay.registry.collections.StringServiceRegistrationMapImpl;
035    import com.liferay.registry.util.StringPlus;
036    import com.liferay.util.Encryptor;
037    
038    import java.util.Collections;
039    import java.util.HashMap;
040    import java.util.List;
041    import java.util.Map;
042    import java.util.Set;
043    
044    /**
045     * @author Raymond Aug??
046     * @author Tomas Polesovsky
047     */
048    @DoPrivileged
049    public class AuthTokenWhitelistImpl implements AuthTokenWhitelist {
050    
051            public AuthTokenWhitelistImpl() {
052                    resetOriginCSRFWhitelist();
053                    resetPortletCSRFWhitelist();
054                    resetPortletInvocationWhitelist();
055                    resetPortletInvocationWhitelistActions();
056    
057                    Registry registry = RegistryUtil.getRegistry();
058    
059                    _serviceTracker = registry.trackServices(
060                            registry.getFilter(
061                                    "(&(" + PropsKeys.AUTH_TOKEN_IGNORE_ACTIONS+"=*)" +
062                                            "(objectClass=java.lang.Object))"),
063                            new AuthTokenIgnoreActionsServiceTrackerCustomizer());
064    
065                    _serviceTracker.open();
066    
067                    registerPortalProperty();
068            }
069    
070            public void destroy() {
071                    for (ServiceRegistration<Object> serviceRegistration :
072                                    _serviceRegistrations.values()) {
073    
074                            serviceRegistration.unregister();
075                    }
076    
077                    _serviceTracker.close();
078            }
079    
080            @Override
081            public Set<String> getOriginCSRFWhitelist() {
082                    return _originCSRFWhitelist;
083            }
084    
085            @Override
086            public Set<String> getPortletCSRFWhitelist() {
087                    return _portletCSRFWhitelist;
088            }
089    
090            @Override
091            public Set<String> getPortletCSRFWhitelistActions() {
092                    return _portletCSRFWhitelistActions;
093            }
094    
095            @Override
096            public Set<String> getPortletInvocationWhitelist() {
097                    return _portletInvocationWhitelist;
098            }
099    
100            @Override
101            public Set<String> getPortletInvocationWhitelistActions() {
102                    return _portletInvocationWhitelistActions;
103            }
104    
105            @Override
106            public boolean isOriginCSRFWhitelisted(long companyId, String origin) {
107                    Set<String> whitelist = getOriginCSRFWhitelist();
108    
109                    for (String whitelistedOrigins : whitelist) {
110                            if (origin.startsWith(whitelistedOrigins)) {
111                                    return true;
112                            }
113                    }
114    
115                    return false;
116            }
117    
118            @Override
119            public boolean isPortletCSRFWhitelisted(
120                    long companyId, String portletId, String strutsAction) {
121    
122                    String rootPortletId = PortletConstants.getRootPortletId(portletId);
123    
124                    Set<String> whitelist = getPortletCSRFWhitelist();
125    
126                    if (whitelist.contains(rootPortletId)) {
127                            return true;
128                    }
129    
130                    if (Validator.isNotNull(strutsAction)) {
131                            Set<String> whitelistActions = getPortletCSRFWhitelistActions();
132    
133                            if (whitelistActions.contains(strutsAction) &&
134                                    isValidStrutsAction(companyId, rootPortletId, strutsAction)) {
135    
136                                    return true;
137                            }
138                    }
139    
140                    return false;
141            }
142    
143            @Override
144            public boolean isPortletInvocationWhitelisted(
145                    long companyId, String portletId, String strutsAction) {
146    
147                    Set<String> whitelist = getPortletInvocationWhitelist();
148    
149                    if (whitelist.contains(portletId)) {
150                            return true;
151                    }
152    
153                    if (Validator.isNotNull(strutsAction)) {
154                            Set<String> whitelistActions =
155                                    getPortletInvocationWhitelistActions();
156    
157                            if (whitelistActions.contains(strutsAction) &&
158                                    isValidStrutsAction(companyId, portletId, strutsAction)) {
159    
160                                    return true;
161                            }
162                    }
163    
164                    return false;
165            }
166    
167            @Override
168            public boolean isValidSharedSecret(String sharedSecret) {
169                    if (Validator.isNull(sharedSecret)) {
170                            return false;
171                    }
172    
173                    if (Validator.isNull(PropsValues.AUTH_TOKEN_SHARED_SECRET)) {
174                            return false;
175                    }
176    
177                    return sharedSecret.equals(
178                            Encryptor.digest(PropsValues.AUTH_TOKEN_SHARED_SECRET));
179            }
180    
181            @Override
182            public Set<String> resetOriginCSRFWhitelist() {
183                    _originCSRFWhitelist = SetUtil.fromArray(
184                            PropsValues.AUTH_TOKEN_IGNORE_ORIGINS);
185                    _originCSRFWhitelist = Collections.unmodifiableSet(
186                            _originCSRFWhitelist);
187    
188                    return _originCSRFWhitelist;
189            }
190    
191            @Override
192            public Set<String> resetPortletCSRFWhitelist() {
193                    _portletCSRFWhitelist = SetUtil.fromArray(
194                            PropsValues.AUTH_TOKEN_IGNORE_PORTLETS);
195                    _portletCSRFWhitelist = Collections.unmodifiableSet(
196                            _portletCSRFWhitelist);
197    
198                    return _portletCSRFWhitelist;
199            }
200    
201            @Override
202            public Set<String> resetPortletInvocationWhitelist() {
203                    _portletInvocationWhitelist = SetUtil.fromArray(
204                            PropsValues.PORTLET_ADD_DEFAULT_RESOURCE_CHECK_WHITELIST);
205                    _portletInvocationWhitelist = Collections.unmodifiableSet(
206                            _portletInvocationWhitelist);
207    
208                    return _portletInvocationWhitelist;
209            }
210    
211            @Override
212            public Set<String> resetPortletInvocationWhitelistActions() {
213                    _portletInvocationWhitelistActions = SetUtil.fromArray(
214                            PropsValues.PORTLET_ADD_DEFAULT_RESOURCE_CHECK_WHITELIST_ACTIONS);
215                    _portletInvocationWhitelistActions = Collections.unmodifiableSet(
216                            _portletInvocationWhitelistActions);
217    
218                    return _portletInvocationWhitelistActions;
219            }
220    
221            protected boolean isValidStrutsAction(
222                    long companyId, String portletId, String strutsAction) {
223    
224                    try {
225                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
226                                    companyId, portletId);
227    
228                            if (portlet == null) {
229                                    return false;
230                            }
231    
232                            String strutsPath = strutsAction.substring(
233                                    1, strutsAction.lastIndexOf(CharPool.SLASH));
234    
235                            if (strutsPath.equals(portlet.getStrutsPath()) ||
236                                    strutsPath.equals(portlet.getParentStrutsPath())) {
237    
238                                    return true;
239                            }
240                    }
241                    catch (Exception e) {
242                    }
243    
244                    return false;
245            }
246    
247            protected void registerPortalProperty() {
248                    Registry registry = RegistryUtil.getRegistry();
249    
250                    for (String authTokenIgnoreAction :
251                                    PropsValues.AUTH_TOKEN_IGNORE_ACTIONS) {
252    
253                            Map<String, Object> properties = new HashMap<>();
254    
255                            properties.put(
256                                    PropsKeys.AUTH_TOKEN_IGNORE_ACTIONS, authTokenIgnoreAction);
257                            properties.put("objectClass", Object.class.getName());
258    
259                            ServiceRegistration<Object> serviceRegistration =
260                                    registry.registerService(
261                                            Object.class, new Object(), properties);
262    
263                            _serviceRegistrations.put(
264                                    authTokenIgnoreAction, serviceRegistration);
265                    }
266            }
267    
268            private Set<String> _originCSRFWhitelist;
269            private Set<String> _portletCSRFWhitelist;
270            private final Set<String> _portletCSRFWhitelistActions =
271                    new ConcurrentHashSet<>();
272            private Set<String> _portletInvocationWhitelist;
273            private Set<String> _portletInvocationWhitelistActions;
274            private final StringServiceRegistrationMap<Object> _serviceRegistrations =
275                    new StringServiceRegistrationMapImpl<>();
276            private final ServiceTracker<Object, Object> _serviceTracker;
277    
278            private class AuthTokenIgnoreActionsServiceTrackerCustomizer
279                    implements ServiceTrackerCustomizer<Object, Object> {
280    
281                    @Override
282                    public Object addingService(ServiceReference<Object> serviceReference) {
283                            List<String> authTokenIgnoreActions = StringPlus.asList(
284                                    serviceReference.getProperty(
285                                            PropsKeys.AUTH_TOKEN_IGNORE_ACTIONS));
286    
287                            _portletCSRFWhitelistActions.addAll(authTokenIgnoreActions);
288    
289                            Registry registry = RegistryUtil.getRegistry();
290    
291                            return registry.getService(serviceReference);
292                    }
293    
294                    @Override
295                    public void modifiedService(
296                            ServiceReference<Object> serviceReference, Object object) {
297    
298                            removedService(serviceReference, object);
299    
300                            addingService(serviceReference);
301                    }
302    
303                    @Override
304                    public void removedService(
305                            ServiceReference<Object> serviceReference, Object object) {
306    
307                            List<String> authTokenIgnoreActions = StringPlus.asList(
308                                    serviceReference.getProperty(
309                                            PropsKeys.AUTH_TOKEN_IGNORE_ACTIONS));
310    
311                            _portletCSRFWhitelistActions.removeAll(authTokenIgnoreActions);
312    
313                            Registry registry = RegistryUtil.getRegistry();
314    
315                            registry.ungetService(serviceReference);
316                    }
317    
318            }
319    
320    }