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.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
020    import com.liferay.portal.kernel.util.ArrayUtil;
021    import com.liferay.portal.kernel.util.ProxyUtil;
022    import com.liferay.portal.kernel.util.ReflectionUtil;
023    
024    import java.security.AccessController;
025    import java.security.PrivilegedAction;
026    
027    import java.util.HashSet;
028    import java.util.Set;
029    
030    import org.springframework.beans.BeansException;
031    import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
032    import org.springframework.context.ApplicationContext;
033    import org.springframework.context.ApplicationContextAware;
034    
035    /**
036     * @author Raymond Aug??
037     */
038    public class DoPrivilegedFactory
039            extends InstantiationAwareBeanPostProcessorAdapter
040            implements ApplicationContextAware {
041    
042            public static boolean isEarlyBeanReference(String beanName) {
043                    return _earlyBeanReferenceNames.contains(beanName);
044            }
045    
046            public static <T> T wrap(T bean) {
047                    Class<?> clazz = bean.getClass();
048    
049                    if (clazz.isPrimitive()) {
050                            return bean;
051                    }
052    
053                    Package pkg = clazz.getPackage();
054    
055                    if (pkg != null) {
056                            String packageName = pkg.getName();
057    
058                            if (packageName.startsWith("java.")) {
059                                    return bean;
060                            }
061                    }
062    
063                    Class<?>[] interfaces = ReflectionUtil.getInterfaces(bean);
064    
065                    if (interfaces.length <= 0) {
066                            return bean;
067                    }
068    
069                    return AccessController.doPrivileged(
070                            new BeanPrivilegedAction<T>(bean, interfaces));
071            }
072    
073            @Override
074            public Object getEarlyBeanReference(Object bean, String beanName)
075                    throws BeansException {
076    
077                    if (_isWrap(bean, beanName)) {
078                            _earlyBeanReferenceNames.add(beanName);
079                    }
080    
081                    return bean;
082            }
083    
084            @Override
085            public Object postProcessAfterInitialization(Object bean, String beanName)
086                    throws BeansException {
087    
088                    if (!SecurityManagerUtil.ENABLED) {
089                            return bean;
090                    }
091    
092                    if (!_isWrap(bean, beanName)) {
093                            return bean;
094                    }
095    
096                    if (isEarlyBeanReference(beanName)) {
097                            if (_log.isDebugEnabled()) {
098                                    _log.debug("Postpone wrapping early reference of " + beanName);
099                            }
100    
101                            return bean;
102                    }
103    
104                    if (_log.isDebugEnabled()) {
105                            Class<?> clazz = bean.getClass();
106    
107                            _log.debug(
108                                    "Wrapping calls to bean " + beanName + " of type " +
109                                            clazz + " with access controller checking");
110                    }
111    
112                    return wrap(bean);
113            }
114    
115            @Override
116            public Object postProcessBeforeInitialization(Object bean, String beanName)
117                    throws BeansException {
118    
119                    return bean;
120            }
121    
122            @Override
123            public void setApplicationContext(ApplicationContext applicationContext)
124                    throws BeansException {
125    
126                    _classLoader = applicationContext.getClassLoader();
127            }
128    
129            private boolean _isDoPrivileged(Class<?> beanClass) {
130                    DoPrivileged doPrivileged = beanClass.getAnnotation(DoPrivileged.class);
131    
132                    while ((doPrivileged == null) &&
133                               (beanClass = beanClass.getSuperclass()) != null) {
134    
135                            doPrivileged = beanClass.getAnnotation(DoPrivileged.class);
136                    }
137    
138                    if (doPrivileged != null) {
139                            return true;
140                    }
141    
142                    return false;
143            }
144    
145            private boolean _isFinderOrPersistence(String beanName) {
146                    if (beanName.endsWith(_BEAN_NAME_SUFFIX_FINDER) ||
147                            beanName.endsWith(_BEAN_NAME_SUFFIX_PERSISTENCE)) {
148    
149                            return true;
150                    }
151    
152                    return false;
153            }
154    
155            private boolean _isWrap(Object bean, String beanName) {
156                    Class<?> clazz = bean.getClass();
157    
158                    if (_isDoPrivileged(clazz) || _isFinderOrPersistence(beanName)) {
159                            return true;
160                    }
161    
162                    return false;
163            }
164    
165            private static final String _BEAN_NAME_SUFFIX_FINDER = "Finder";
166    
167            private static final String _BEAN_NAME_SUFFIX_PERSISTENCE = "Persistence";
168    
169            private static final Log _log = LogFactoryUtil.getLog(
170                    DoPrivilegedFactory.class);
171    
172            private static ClassLoader _classLoader =
173                    DoPrivilegedFactory.class.getClassLoader();
174            private static final Set<String> _earlyBeanReferenceNames = new HashSet<>();
175    
176            private static class BeanPrivilegedAction <T>
177                    implements PrivilegedAction<T> {
178    
179                    public BeanPrivilegedAction(T bean, Class<?>[] interfaces) {
180                            _bean = bean;
181                            _interfaces = ArrayUtil.append(interfaces, DoPrivilegedBean.class);
182                    }
183    
184                    @Override
185                    public T run() {
186                            try {
187                                    return (T)ProxyUtil.newProxyInstance(
188                                            _classLoader, _interfaces, new DoPrivilegedHandler(_bean));
189                            }
190                            catch (Exception e) {
191                                    if (_log.isWarnEnabled()) {
192                                            _log.warn(e, e);
193                                    }
194                            }
195    
196                            return _bean;
197                    }
198    
199                    private final T _bean;
200                    private final Class<?>[] _interfaces;
201    
202            }
203    
204    }