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.lang;
016    
017    import com.liferay.portal.kernel.security.pacl.NotPrivileged;
018    import com.liferay.portal.kernel.security.pacl.permission.PortalServicePermission;
019    import com.liferay.portal.kernel.util.HashUtil;
020    import com.liferay.portal.kernel.util.Validator;
021    
022    import java.lang.reflect.InvocationHandler;
023    import java.lang.reflect.InvocationTargetException;
024    import java.lang.reflect.Method;
025    
026    import java.security.AccessController;
027    import java.security.PrivilegedActionException;
028    import java.security.PrivilegedExceptionAction;
029    
030    import java.util.ArrayList;
031    import java.util.Arrays;
032    import java.util.Collections;
033    import java.util.List;
034    
035    /**
036     * @author Raymond Aug??
037     */
038    public class DoPrivilegedHandler
039            implements DoPrivilegedBean, InvocationHandler {
040    
041            public DoPrivilegedHandler(Object bean) {
042                    _bean = bean;
043    
044                    _initNotPrivilegedMethods();
045            }
046    
047            @Override
048            public Object getActualBean() {
049                    return _bean;
050            }
051    
052            @Override
053            public Object invoke(Object proxy, Method method, Object[] arguments)
054                    throws Throwable {
055    
056                    try {
057                            return doInvoke(proxy, method, arguments);
058                    }
059                    catch (InvocationTargetException ite) {
060                            throw ite.getTargetException();
061                    }
062            }
063    
064            protected Object doInvoke(Object proxy, Method method, Object[] arguments)
065                    throws Throwable {
066    
067                    Class<?> methodDeclaringClass = method.getDeclaringClass();
068                    String methodName = method.getName();
069    
070                    if (methodDeclaringClass.equals(DoPrivilegedBean.class) &&
071                            methodName.equals("getActualBean")) {
072    
073                            return _bean;
074                    }
075                    else if (methodDeclaringClass.equals(Object.class) &&
076                                     methodName.equals("equals")) {
077    
078                            Object object = arguments[0];
079    
080                            if (object instanceof DoPrivilegedBean) {
081                                    DoPrivilegedBean doPrivilegedBean = (DoPrivilegedBean)object;
082    
083                                    object = doPrivilegedBean.getActualBean();
084                            }
085    
086                            return _bean.equals(object);
087                    }
088                    else if (_isNotPrivileged(method)) {
089                            return method.invoke(_bean, arguments);
090                    }
091    
092                    String declaringClassName = methodDeclaringClass.getName();
093    
094                    if (declaringClassName.endsWith(_BEAN_NAME_SUFFIX_FINDER) ||
095                            declaringClassName.endsWith(_BEAN_NAME_SUFFIX_PERSISTENCE)) {
096    
097                            PortalServicePermission.checkService(_bean, method, arguments);
098                    }
099    
100                    try {
101                            return AccessController.doPrivileged(
102                                    new InvokePrivilegedExceptionAction(_bean, method, arguments));
103                    }
104                    catch (PrivilegedActionException pae) {
105                            Exception e = pae.getException();
106    
107                            throw e.getCause();
108                    }
109            }
110    
111            private void _initNotPrivilegedMethods() {
112                    _notPrivilegedMethods = new ArrayList<>();
113    
114                    Class<?> beanClass = _bean.getClass();
115    
116                    Method[] methods = beanClass.getMethods();
117    
118                    for (Method method : methods) {
119                            NotPrivileged notPrivileged = method.getAnnotation(
120                                    NotPrivileged.class);
121    
122                            if (notPrivileged == null) {
123                                    continue;
124                            }
125    
126                            _notPrivilegedMethods.add(new MethodKey(method));
127                    }
128    
129                    _notPrivilegedMethods = Collections.unmodifiableList(
130                            _notPrivilegedMethods);
131    
132                    if (!_notPrivilegedMethods.isEmpty()) {
133                            _hasNotPrivilegedMethods = true;
134                    }
135            }
136    
137            private boolean _isNotPrivileged(Method method) {
138                    if (_hasNotPrivilegedMethods &&
139                            _notPrivilegedMethods.contains(new MethodKey(method))) {
140    
141                            return true;
142                    }
143    
144                    return false;
145            }
146    
147            private static final String _BEAN_NAME_SUFFIX_FINDER = "Finder";
148    
149            private static final String _BEAN_NAME_SUFFIX_PERSISTENCE = "Persistence";
150    
151            private Object _bean;
152            private boolean _hasNotPrivilegedMethods = false;
153            private List<MethodKey> _notPrivilegedMethods;
154    
155            private class InvokePrivilegedExceptionAction
156                    implements PrivilegedExceptionAction<Object> {
157    
158                    public InvokePrivilegedExceptionAction(
159                            Object bean, Method method, Object[] arguments) {
160    
161                            _bean = bean;
162                            _method = method;
163                            _arguments = arguments;
164                    }
165    
166                    @Override
167                    public Object run() throws Exception {
168                            return _method.invoke(_bean, _arguments);
169                    }
170    
171                    private final Object[] _arguments;
172                    private Object _bean;
173                    private final Method _method;
174    
175            }
176    
177            /**
178             * This is not the typical MethodKey. It matches on overload conditions
179             * rather than on equality. The key in the cache should always be an
180             * implementation, while the method being checked will be from an interface,
181             * therefore the <code>equals</code> check is not symmetrical.
182             */
183            private class MethodKey {
184    
185                    public MethodKey(Method method) {
186                            _declaringClass = method.getDeclaringClass();
187                            _methodName = method.getName();
188                            _parameterTypes = method.getParameterTypes();
189                    }
190    
191                    @Override
192                    public boolean equals(Object obj) {
193                            MethodKey methodKey = (MethodKey)obj;
194    
195                            // Note again that this check is not symmetrical. This method key's
196                            // class must be assignable from the cached method key's class
197    
198                            if (_declaringClass.isAssignableFrom(methodKey._declaringClass) &&
199                                    Validator.equals(_methodName, methodKey._methodName) &&
200                                    Arrays.equals(_parameterTypes, methodKey._parameterTypes)) {
201    
202                                    return true;
203                            }
204    
205                            return false;
206                    }
207    
208                    @Override
209                    public int hashCode() {
210                            int hash = HashUtil.hash(0, _declaringClass);
211    
212                            hash = HashUtil.hash(hash, _methodName);
213    
214                            return HashUtil.hash(hash, _parameterTypes);
215                    }
216    
217                    private final Class<?> _declaringClass;
218                    private final String _methodName;
219                    private final Class<?>[] _parameterTypes;
220    
221            }
222    
223    }