001    /**
002     * Copyright (c) 2000-2012 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.pacl.checker;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.security.pacl.permission.PortalServicePermission;
020    import com.liferay.portal.kernel.util.ProxyUtil;
021    import com.liferay.portal.kernel.util.ServerDetector;
022    import com.liferay.portal.kernel.util.SetUtil;
023    import com.liferay.portal.kernel.util.StringPool;
024    import com.liferay.portal.kernel.util.StringUtil;
025    import com.liferay.portal.security.pacl.PACLPolicy;
026    import com.liferay.portal.security.pacl.PACLPolicyManager;
027    import com.liferay.portal.util.ClassLoaderUtil;
028    
029    import java.lang.reflect.Method;
030    
031    import java.security.Permission;
032    
033    import java.util.Collections;
034    import java.util.HashMap;
035    import java.util.Map;
036    import java.util.Properties;
037    import java.util.Set;
038    
039    /**
040     * @author Brian Wing Shun Chan
041     * @author Raymond Augé
042     */
043    public class PortalServiceChecker extends BaseChecker {
044    
045            public void afterPropertiesSet() {
046                    initServices();
047            }
048    
049            public void checkPermission(Permission permission) {
050                    PortalServicePermission portalServicePermission =
051                            (PortalServicePermission)permission;
052    
053                    String name = portalServicePermission.getName();
054                    Object object = portalServicePermission.getObject();
055    
056                    if (name.equals(PORTAL_SERVICE_PERMISSION_DYNAMIC_QUERY)) {
057                            Class<?> implClass = (Class<?>)object;
058    
059                            if (!hasDynamicQuery(implClass)) {
060                                    throwSecurityException(
061                                            _log,
062                                            "Attempted to create a dynamic query for " + implClass);
063                            }
064                    }
065            }
066    
067            @Override
068            public AuthorizationProperty generateAuthorizationProperty(
069                    Object... arguments) {
070    
071                    if ((arguments == null) || (arguments.length == 0)) {
072                            return null;
073                    }
074    
075                    Object object = null;
076                    Method method = null;
077    
078                    if (arguments[0] instanceof Permission) {
079                            PortalServicePermission portalServicePermission =
080                                    (PortalServicePermission)arguments[0];
081    
082                            object = portalServicePermission.getObject();
083                            method = portalServicePermission.getMethod();
084                    }
085                    else {
086                            object = arguments[0];
087                            method = (Method)arguments[1];
088                    }
089    
090                    Class<?> clazz = getClass(object);
091    
092                    if (clazz == null) {
093                            return null;
094                    }
095    
096                    ClassLoader classLoader = ClassLoaderUtil.getClassLoader(clazz);
097    
098                    PACLPolicy paclPolicy = PACLPolicyManager.getPACLPolicy(classLoader);
099    
100                    String filter = "[portal]";
101    
102                    if (paclPolicy != null) {
103                            filter =
104                                    StringPool.OPEN_BRACKET + paclPolicy.getServletContextName() +
105                                            StringPool.CLOSE_BRACKET;
106                    }
107    
108                    String className = getInterfaceName(clazz.getName());
109    
110                    String methodName = method.getName();
111    
112                    if (methodName.equals("invokeMethod")) {
113                            methodName = (String)arguments[0];
114                    }
115    
116                    AuthorizationProperty authorizationProperty =
117                            new AuthorizationProperty();
118    
119                    authorizationProperty.setKey("security-manager-services" + filter);
120                    authorizationProperty.setValue(
121                            className + StringPool.POUND + methodName);
122    
123                    return authorizationProperty;
124            }
125    
126            public boolean hasService(
127                    Object object, Method method, Object[] arguments) {
128    
129                    Class<?> clazz = getClass(object);
130    
131                    if (clazz == null) {
132                            return false;
133                    }
134    
135                    ClassLoader classLoader = ClassLoaderUtil.getClassLoader(clazz);
136    
137                    PACLPolicy paclPolicy = PACLPolicyManager.getPACLPolicy(classLoader);
138    
139                    if (paclPolicy == getPACLPolicy()) {
140                            return true;
141                    }
142    
143                    Set<String> services = getServices(paclPolicy);
144    
145                    String className = getInterfaceName(clazz.getName());
146    
147                    if (services.contains(className)) {
148                            return true;
149                    }
150    
151                    String methodName = method.getName();
152    
153                    if (methodName.equals("invokeMethod")) {
154                            methodName = (String)arguments[0];
155                    }
156    
157                    if (services.contains(
158                                    className.concat(StringPool.POUND).concat(methodName))) {
159    
160                            return true;
161                    }
162    
163                    return false;
164            }
165    
166            protected Class<?> getClass(Object object) {
167                    Class<?> clazz = object.getClass();
168    
169                    if (ProxyUtil.isProxyClass(clazz)) {
170                            Class<?>[] interfaces = clazz.getInterfaces();
171    
172                            if (interfaces.length == 0) {
173                                    return null;
174                            }
175    
176                            clazz = interfaces[0];
177                    }
178    
179                    return clazz;
180            }
181    
182            protected String getInterfaceName(String className) {
183                    int pos = className.indexOf(".impl.");
184    
185                    if (pos != -1) {
186                            className =
187                                    className.substring(0, pos + 1) + className.substring(pos + 6);
188                    }
189    
190                    if (className.endsWith("Impl")) {
191                            className = className.substring(0, className.length() - 4);
192                    }
193    
194                    return className;
195            }
196    
197            protected Set<String> getServices(PACLPolicy paclPolicy) {
198                    Set<String> services = null;
199    
200                    if (paclPolicy == null) {
201                            services = _portalServices;
202                    }
203                    else {
204                            services = _pluginServices.get(paclPolicy.getServletContextName());
205                    }
206    
207                    return services;
208            }
209    
210            protected boolean hasDynamicQuery(Class<?> clazz) {
211                    ClassLoader classLoader = ClassLoaderUtil.getClassLoader(clazz);
212    
213                    PACLPolicy paclPolicy = PACLPolicyManager.getPACLPolicy(classLoader);
214    
215                    if (paclPolicy == getPACLPolicy()) {
216                            return true;
217                    }
218    
219                    /*Set<String> services = getServices(paclPolicy);
220    
221                    String className = getInterfaceName(clazz.getName());
222    
223                    if (services.contains(className)) {
224                            return true;
225                    }*/
226    
227                    return false;
228            }
229    
230            protected void initServices() {
231                    Properties properties = getProperties();
232    
233                    for (Map.Entry<Object, Object> entry : properties.entrySet()) {
234                            String key = (String)entry.getKey();
235                            String value = (String)entry.getValue();
236    
237                            if (!key.startsWith("security-manager-services[")) {
238                                    continue;
239                            }
240    
241                            int x = key.indexOf("[");
242                            int y = key.indexOf("]", x);
243    
244                            String servicesServletContextName = key.substring(x + 1, y);
245    
246                            Set<String> services = SetUtil.fromArray(StringUtil.split(value));
247    
248                            if (servicesServletContextName.equals(
249                                            _PORTAL_SERVLET_CONTEXT_NAME)) {
250    
251                                    _portalServices = services;
252    
253                                    touchServices(_portalServices);
254                            }
255                            else {
256                                    _pluginServices.put(servicesServletContextName, services);
257                            }
258                    }
259            }
260    
261            protected void touchService(String service) {
262                    String className = service;
263    
264                    if (!className.contains(".service.")) {
265                            return;
266                    }
267    
268                    String methodName = null;
269    
270                    if (className.contains(".service.persistence.") &&
271                            (className.endsWith("Persistence") ||
272                             className.contains("Persistence#"))) {
273    
274                            methodName = "getPersistence";
275                    }
276                    else if (className.endsWith("Service") ||
277                                     className.contains("Service#")) {
278    
279                            methodName = "getService";
280                    }
281                    else {
282                            _log.error("Invalid service " + service);
283    
284                            return;
285                    }
286    
287                    int pos = className.indexOf(StringPool.POUND);
288    
289                    if (pos != -1) {
290                            className = className.substring(0, pos);
291                    }
292    
293                    if (className.endsWith("Persistence")) {
294                            className = className.substring(0, className.length() - 11);
295                    }
296    
297                    className += "Util";
298    
299                    if (_log.isDebugEnabled()) {
300                            _log.debug("Invoking " + className + "#" + methodName);
301                    }
302    
303                    // Invoke method since it will attempt to access declared members
304    
305                    ClassLoader classLoader = getCommonClassLoader();
306    
307                    if (ServerDetector.isGeronimo() || ServerDetector.isJBoss()) {
308                            classLoader = getPortalClassLoader();
309                    }
310    
311                    try {
312                            Class<?> clazz = classLoader.loadClass(className);
313    
314                            Method method = clazz.getMethod(methodName);
315    
316                            method.invoke(clazz);
317                    }
318                    catch (Exception e) {
319                            _log.error(e, e);
320                    }
321            }
322    
323            protected void touchServices(Set<String> services) {
324                    for (String service : services) {
325                            touchService(service);
326                    }
327            }
328    
329            private static final String _PORTAL_SERVLET_CONTEXT_NAME = "portal";
330    
331            private static Log _log = LogFactoryUtil.getLog(PortalServiceChecker.class);
332    
333            private Map<String, Set<String>> _pluginServices =
334                    new HashMap<String, Set<String>>();
335            private Set<String> _portalServices = Collections.emptySet();
336    
337    }