JAXPPolicyManager.java revision 1035:149559dd882d
1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package jaxp.library;
24
25
26import java.net.URL;
27import java.security.CodeSource;
28import java.security.Permission;
29import java.security.PermissionCollection;
30import java.security.Permissions;
31import java.security.Policy;
32import java.security.ProtectionDomain;
33import java.security.SecurityPermission;
34import java.util.Enumeration;
35import java.util.HashMap;
36import java.util.Map;
37import java.util.PropertyPermission;
38import java.util.Set;
39import java.util.StringJoiner;
40
41
42/*
43 * This is a base class that every test class must extend if it needs to be run
44 * with security mode.
45 */
46public class JAXPPolicyManager {
47    /*
48     * Backing up policy.
49     */
50    private Policy policyBackup;
51
52    /*
53     * Backing up security manager.
54     */
55    private SecurityManager smBackup;
56
57    /*
58     * Current policy.
59     */
60    private TestPolicy policy = new TestPolicy();
61
62    /*
63     * JAXPPolicyManager singleton.
64     */
65    private static JAXPPolicyManager policyManager = null;
66
67    /*
68     * Install a SecurityManager along with a default Policy to allow testNG to
69     * run when there is a security manager.
70     */
71    private JAXPPolicyManager() {
72        // Backing up policy and security manager for restore
73        policyBackup = Policy.getPolicy();
74        smBackup = System.getSecurityManager();
75
76        // Set customized policy
77        setDefaultPermissions();
78        Policy.setPolicy(policy);
79        System.setSecurityManager(new SecurityManager());
80    }
81
82    static synchronized JAXPPolicyManager getJAXPPolicyManager(boolean createIfNone) {
83        if (policyManager == null & createIfNone)
84            policyManager = new JAXPPolicyManager();
85        return policyManager;
86    }
87
88    private void teardown() throws Exception {
89        System.setSecurityManager(smBackup);
90        Policy.setPolicy(policyBackup);
91    }
92
93    /*
94     * Restore the original Policy and SecurityManager.
95     */
96    static synchronized void teardownPolicyManager() throws Exception {
97        if (policyManager != null) {
98            policyManager.teardown();
99            policyManager = null;
100        }
101    }
102
103    /*
104     * Set default permissions, sub-class of JAXPBaseTest should override this
105     * method.
106     */
107    private void setDefaultPermissions() {
108        //Permissions to set security manager and policy
109        addPermission(new SecurityPermission("getPolicy"));
110        addPermission(new SecurityPermission("setPolicy"));
111        addPermission(new RuntimePermission("setSecurityManager"));
112        addPermission(new PropertyPermission("test.src", "read"));
113    }
114
115    /*
116     * Add permission to the TestPolicy.
117     *
118     * @param permission to be added.
119     */
120    void addPermission(Permission p) {
121        policy.addPermission(p);
122    }
123
124    /*
125     * Add a temporary permission in current thread context. This won't impact
126     * global policy and doesn't support permission combination.
127     *
128     * @param permission
129     *            to add.
130     * @return index of the added permission.
131     */
132    int addTmpPermission(Permission p) {
133        return policy.addTmpPermission(p);
134    }
135
136    /*
137     * set allowAll in current thread context.
138     */
139    void setAllowAll(boolean allow) {
140        policy.setAllowAll(allow);
141    }
142
143    /*
144     * Remove a temporary permission from current thread context.
145     *
146     * @param index to remove.
147     *
148     * @throws RuntimeException if no temporary permission list in current
149     *             thread context or no permission correlated to the index.
150     */
151    void removeTmpPermission(int index) {
152        policy.removeTmpPermission(index);
153    }
154
155
156}
157
158/*
159 * Simple Policy class that supports the required Permissions to validate the
160 * JAXP concrete classes.
161 */
162class TestPolicy extends Policy {
163    private final static Set<String> TEST_JARS =
164         Set.of("jtreg.jar", "javatest.jar", "testng.jar", "jcommander.jar");
165    private final PermissionCollection permissions = new Permissions();
166
167    private ThreadLocal<Map<Integer, Permission>> transientPermissions = new ThreadLocal<>();
168    private ThreadLocal<Boolean> allowAll = new ThreadLocal<>();
169
170    private static Policy defaultPolicy = Policy.getPolicy();
171
172    /*
173     * Add permission to this policy.
174     *
175     * @param permission to be added.
176     */
177    void addPermission(Permission p) {
178        permissions.add(p);
179    }
180
181    /*
182     * Set all permissions. Caution: this should not called carefully unless
183     * it's really needed.
184     *
185     * private void setAllPermissions() { permissions.add(new AllPermission());
186     * }
187     */
188
189    /*
190     * Overloaded methods from the Policy class.
191     */
192    @Override
193    public String toString() {
194        StringJoiner sj = new StringJoiner("\n", "policy: ", "");
195        Enumeration<Permission> perms = permissions.elements();
196        while (perms.hasMoreElements()) {
197            sj.add(perms.nextElement().toString());
198        }
199        return sj.toString();
200
201    }
202
203    @Override
204    public PermissionCollection getPermissions(ProtectionDomain domain) {
205        return permissions;
206    }
207
208    @Override
209    public PermissionCollection getPermissions(CodeSource codesource) {
210        return permissions;
211    }
212
213    private boolean isTestMachineryDomain(ProtectionDomain domain) {
214        CodeSource cs = (domain == null) ? null : domain.getCodeSource();
215        URL loc = (cs == null) ? null : cs.getLocation();
216        String path = (loc == null) ? null : loc.getPath();
217        return path != null && TEST_JARS.stream()
218                                .filter(path::endsWith)
219                                .findAny()
220                                .isPresent();
221    }
222
223    @Override
224    public boolean implies(ProtectionDomain domain, Permission perm) {
225        if (allowAll())
226            return true;
227
228        if (defaultPolicy.implies(domain, perm))
229            return true;
230
231        if (permissions.implies(perm))
232            return true;
233
234        if (isTestMachineryDomain(domain))
235            return true;
236
237        return tmpImplies(perm);
238    }
239
240    /*
241     * Add a temporary permission in current thread context. This won't impact
242     * global policy and doesn't support permission combination.
243     *
244     * @param permission to add.
245     * @return index of the added permission.
246     */
247    int addTmpPermission(Permission p) {
248        Map<Integer, Permission> tmpPermissions = transientPermissions.get();
249        if (tmpPermissions == null)
250            tmpPermissions = new HashMap<>();
251
252        int id = tmpPermissions.size();
253        tmpPermissions.put(id, p);
254        transientPermissions.set(tmpPermissions);
255        return id;
256    }
257
258    /*
259     * Remove a temporary permission from current thread context.
260     *
261     * @param index to remove.
262     *
263     * @throws RuntimeException if no temporary permission list in current
264     *             thread context or no permission correlated to the index.
265     */
266    void removeTmpPermission(int index) {
267        try {
268            Map<Integer, Permission> tmpPermissions = transientPermissions.get();
269            tmpPermissions.remove(index);
270        } catch (NullPointerException | IndexOutOfBoundsException e) {
271            throw new RuntimeException("Tried to delete a non-existent temporary permission", e);
272        }
273    }
274
275    /*
276     * Checks to see if the specified permission is implied by temporary
277     * permission list in current thread context.
278     *
279     * @param permission the Permission object to compare.
280     *
281     * @return true if "permission" is implied by any permission in the
282     *         temporary permission list, false if not.
283     */
284    private boolean tmpImplies(Permission perm) {
285        Map<Integer, Permission> tmpPermissions = transientPermissions.get();
286        if (tmpPermissions != null) {
287            for (Permission p : tmpPermissions.values()) {
288                if (p.implies(perm))
289                    return true;
290            }
291        }
292        return false;
293    }
294
295    /*
296     * Checks to see if allow all permission requests in current thread context.
297     */
298    private boolean allowAll() {
299        Boolean allow = allowAll.get();
300        if (allow != null) {
301            return allow;
302        }
303        return false;
304    }
305
306    /*
307     * set allowAll in current thread context.
308     */
309    void setAllowAll(boolean allow) {
310        allowAll.set(allow);
311    }
312}
313