001    /**
002     * Copyright (c) 2000-2013 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;
016    
017    import com.liferay.portal.kernel.util.WeakValueConcurrentHashMap;
018    import com.liferay.portal.util.Portal;
019    
020    import java.lang.reflect.Field;
021    
022    import java.security.AccessController;
023    import java.security.AllPermission;
024    import java.security.CodeSource;
025    import java.security.Permission;
026    import java.security.PermissionCollection;
027    import java.security.Permissions;
028    import java.security.Policy;
029    import java.security.PrivilegedActionException;
030    import java.security.PrivilegedExceptionAction;
031    import java.security.ProtectionDomain;
032    import java.security.Provider;
033    
034    import java.util.ArrayList;
035    import java.util.Collections;
036    import java.util.Enumeration;
037    import java.util.List;
038    import java.util.Map;
039    import java.util.concurrent.ConcurrentHashMap;
040    import java.util.concurrent.ConcurrentMap;
041    
042    import javax.servlet.Servlet;
043    
044    /**
045     * @author Raymond Augé
046     */
047    public class PortalPolicy extends Policy {
048    
049            public PortalPolicy(Policy policy) {
050                    _policy = policy;
051    
052                    try {
053                            _init();
054                    }
055                    catch (PrivilegedActionException pae) {
056                            throw new IllegalStateException(
057                                    "Liferay needs to be able to change the accessibility of the " +
058                                            "'key' field in " + ProtectionDomain.class.getName() +
059                                                    " as well as get the protection domains of classes",
060                                    pae.getException());
061                    }
062            }
063    
064            @Override
065            public Parameters getParameters() {
066                    Parameters parameters = null;
067    
068                    if (_policy != null) {
069                            parameters = _policy.getParameters();
070                    }
071    
072                    return parameters;
073            }
074    
075            @Override
076            public PermissionCollection getPermissions(CodeSource codeSource) {
077                    PermissionCollection permissionCollection = null;
078    
079                    if (_policy != null) {
080                            permissionCollection = _policy.getPermissions(codeSource);
081                    }
082    
083                    if (permissionCollection == null) {
084                            permissionCollection = new Permissions();
085                    }
086    
087                    return permissionCollection;
088            }
089    
090            @Override
091            public PermissionCollection getPermissions(
092                    ProtectionDomain protectionDomain) {
093    
094                    if (protectionDomain == null) {
095                            return new Permissions();
096                    }
097    
098                    Object key = _getKey(protectionDomain);
099    
100                    PermissionCollection permissionCollection = _getPermissionCollection(
101                            key);
102    
103                    if (permissionCollection != null) {
104                            return permissionCollection;
105                    }
106    
107                    permissionCollection = getPermissions(protectionDomain.getCodeSource());
108    
109                    if (permissionCollection == null) {
110                            permissionCollection = new Permissions();
111                    }
112    
113                    if (_policy != null) {
114                            _addExtraPermissions(
115                                    permissionCollection, _policy.getPermissions(protectionDomain));
116                    }
117    
118                    _addExtraPermissions(
119                            permissionCollection, protectionDomain.getPermissions());
120    
121                    PACLPolicy paclPolicy = PACLPolicyManager.getPACLPolicy(
122                            protectionDomain.getClassLoader());
123    
124                    if (paclPolicy != null) {
125                            return new PortalPermissionCollection(
126                                    paclPolicy, permissionCollection);
127                    }
128    
129                    permissionCollection.add(_allPermission);
130    
131                    return permissionCollection;
132            }
133    
134            @Override
135            public Provider getProvider() {
136                    Provider provider = null;
137    
138                    if (_policy != null) {
139                            provider = _policy.getProvider();
140                    }
141    
142                    return provider;
143            }
144    
145            @Override
146            public String getType() {
147                    String type = null;
148    
149                    if (_policy != null) {
150                            type= _policy.getType();
151                    }
152    
153                    return type;
154            }
155    
156            @Override
157            public boolean implies(
158                    ProtectionDomain protectionDomain, Permission permission) {
159    
160                    if (!(permission instanceof PACLUtil.Permission) &&
161                            ((protectionDomain.getClassLoader() == null) ||
162                             !PACLPolicyManager.isActive() ||
163                             !_paclPolicy.isCheckablePermission(permission))) {
164    
165                            return _checkWithParentPolicy(protectionDomain, permission);
166                    }
167    
168                    Object key = _getKey(protectionDomain);
169    
170                    PermissionCollection permissionCollection = _getPermissionCollection(
171                            key);
172    
173                    if (permissionCollection != null) {
174                            if (permissionCollection.implies(permission)) {
175                                    return _checkWithParentPolicy(protectionDomain, permission);
176                            }
177                            else if (_checkWithPACLPolicyPolicy(
178                                                    protectionDomain, permission, permissionCollection)) {
179    
180                                    return _checkWithParentPolicy(protectionDomain, permission);
181                            }
182    
183                            return false;
184                    }
185    
186                    permissionCollection = getPermissions(protectionDomain);
187    
188                    _permissionCollections.putIfAbsent(key, permissionCollection);
189    
190                    if (permissionCollection.implies(permission)) {
191                            return _checkWithParentPolicy(protectionDomain, permission);
192                    }
193                    else if (_checkWithPACLPolicyPolicy(
194                                            protectionDomain, permission, permissionCollection)) {
195    
196                            return _checkWithParentPolicy(protectionDomain, permission);
197                    }
198    
199                    return false;
200            }
201    
202            @Override
203            public void refresh() {
204                    if (_policy != null) {
205                            _policy.refresh();
206                    }
207    
208                    synchronized (_permissionCollections) {
209                            _permissionCollections.clear();
210    
211                            _permissionCollections.putAll(_rootPermissionCollections);
212                    }
213            }
214    
215            private void _addExtraPermissions(
216                    PermissionCollection permissionCollection,
217                    PermissionCollection staticPermissionCollection) {
218    
219                    if (staticPermissionCollection == null) {
220                            return;
221                    }
222    
223                    synchronized (staticPermissionCollection) {
224                            Enumeration<Permission> enumeration =
225                                    staticPermissionCollection.elements();
226    
227                            while (enumeration.hasMoreElements()) {
228                                    permissionCollection.add(enumeration.nextElement());
229                            }
230                    }
231            }
232    
233            private boolean _checkWithPACLPolicyPolicy(
234                    ProtectionDomain protectionDomain, Permission permission,
235                    PermissionCollection permissionCollection) {
236    
237                    if (!(permissionCollection instanceof PortalPermissionCollection)) {
238                            return false;
239                    }
240    
241                    PortalPermissionCollection portalPermissionCollection =
242                            (PortalPermissionCollection)permissionCollection;
243    
244                    Policy policy = portalPermissionCollection.getPolicy();
245    
246                    ClassLoader classLoader = portalPermissionCollection.getClassLoader();
247    
248                    if ((policy != null) &&
249                            (classLoader == protectionDomain.getClassLoader())) {
250    
251                            return policy.implies(protectionDomain, permission);
252                    }
253    
254                    return false;
255            }
256    
257            private boolean _checkWithParentPolicy(
258                    ProtectionDomain protectionDomain, Permission permission) {
259    
260                    if (_policy != null) {
261                            return _policy.implies(protectionDomain, permission);
262                    }
263    
264                    return true;
265            }
266    
267            private Object _getKey(ProtectionDomain protectionDomain) {
268                    try {
269                            return _field.get(protectionDomain);
270                    }
271                    catch (Exception e) {
272                            String string = protectionDomain.toString();
273    
274                            return string.hashCode();
275                    }
276            }
277    
278            private PermissionCollection _getPermissionCollection(Object key) {
279                    PermissionCollection permissionCollection = _permissionCollections.get(
280                            key);
281    
282                    if (permissionCollection == null) {
283                            permissionCollection = _rootPermissionCollections.get(key);
284    
285                            if (permissionCollection != null) {
286                                    _permissionCollections.putIfAbsent(key, permissionCollection);
287                            }
288                    }
289    
290                    return permissionCollection;
291            }
292    
293            private void _init() throws PrivilegedActionException {
294                    _field = AccessController.doPrivileged(
295                            new FieldPrivilegedExceptionAction());
296    
297                    List<ProtectionDomain> protectionDomains =
298                            AccessController.doPrivileged(
299                                    new ProtectionDomainsPrivilegedExceptionAction());
300    
301                    PermissionCollection permissionCollection = new Permissions();
302    
303                    permissionCollection.add(_allPermission);
304    
305                    _rootPermissionCollections =
306                            new ConcurrentHashMap<Object, PermissionCollection>();
307    
308                    for (ProtectionDomain protectionDomain : protectionDomains) {
309                            _rootPermissionCollections.put(
310                                    _getKey(protectionDomain), permissionCollection);
311                    }
312    
313                    _rootPermissionCollections = Collections.unmodifiableMap(
314                            _rootPermissionCollections);
315            }
316    
317            private static AllPermission _allPermission = new AllPermission();
318    
319            private Field _field;
320            private PACLPolicy _paclPolicy = PACLPolicyManager.getDefaultPACLPolicy();
321            private ConcurrentMap<Object, PermissionCollection> _permissionCollections =
322                    new WeakValueConcurrentHashMap<Object, PermissionCollection>();
323            private Policy _policy;
324    
325            private Map<Object, PermissionCollection> _rootPermissionCollections;
326    
327            private class FieldPrivilegedExceptionAction
328                    implements PrivilegedExceptionAction<Field> {
329    
330                    public Field run() throws Exception {
331                            Field field = ProtectionDomain.class.getDeclaredField("key");
332    
333                            field.setAccessible(true);
334    
335                            return field;
336                    }
337    
338            }
339    
340            private class ProtectionDomainsPrivilegedExceptionAction
341                    implements PrivilegedExceptionAction<List<ProtectionDomain>> {
342    
343                    public List<ProtectionDomain> run() throws Exception {
344                            List<ProtectionDomain> protectionDomains =
345                                    new ArrayList<ProtectionDomain>();
346    
347                            Class<?> clazz = getClass();
348    
349                            protectionDomains.add(clazz.getProtectionDomain());
350                            protectionDomains.add(Object.class.getProtectionDomain());
351                            protectionDomains.add(Portal.class.getProtectionDomain());
352                            protectionDomains.add(Servlet.class.getProtectionDomain());
353    
354                            return protectionDomains;
355                    }
356    
357            }
358    
359    }