AccessControlContext.java revision f6c387128427e121477c1b32ad35cdcaa5101ba3
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17/*
18 * Copyright (C) 2008 The Android Open Source Project
19 *
20 * Licensed under the Apache License, Version 2.0 (the "License");
21 * you may not use this file except in compliance with the License.
22 * You may obtain a copy of the License at
23 *
24 *      http://www.apache.org/licenses/LICENSE-2.0
25 *
26 * Unless required by applicable law or agreed to in writing, software
27 * distributed under the License is distributed on an "AS IS" BASIS,
28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 * See the License for the specific language governing permissions and
30 * limitations under the License.
31 */
32
33package java.security;
34
35import org.apache.harmony.security.fortress.PolicyUtils;
36
37import java.util.ArrayList;
38
39/**
40 * {@code AccessControlContext} encapsulates the {@code ProtectionDomain}s on
41 * which access control decisions are based.
42 */
43public final class AccessControlContext {
44
45    // List of ProtectionDomains wrapped by the AccessControlContext
46    // It has the following characteristics:
47    //     - 'context' can not be null
48    //     - never contains null(s)
49    //     - all elements are unique (no dups)
50    ProtectionDomain[] context;
51
52    DomainCombiner combiner;
53
54    // An AccessControlContext inherited by the current thread from its parent
55    private AccessControlContext inherited;
56
57    /**
58     * Constructs a new instance of {@code AccessControlContext} with the
59     * specified {@code AccessControlContext} and {@code DomainCombiner}.
60     * <p>
61     * If a {@code SecurityManager} is installed, code calling this constructor
62     * need the {@code SecurityPermission} {@code createAccessControlContext} to
63     * be granted, otherwise a {@code SecurityException} will be thrown.
64     *
65     * @param acc
66     *            the {@code AccessControlContext} related to the given {@code
67     *            DomainCombiner}
68     * @param combiner
69     *            the {@code DomainCombiner} related to the given {@code
70     *            AccessControlContext}
71     * @throws SecurityException
72     *             if a {@code SecurityManager} is installed and the caller does
73     *             not have permission to invoke this constructor
74     * @throws NullPointerException
75     *             if {@code acc} is {@code null}
76     * @since Android 1.0
77     */
78    public AccessControlContext(AccessControlContext acc,
79            DomainCombiner combiner) {
80        SecurityManager sm = System.getSecurityManager();
81        if (sm != null) {
82            sm.checkPermission(new SecurityPermission(
83                    "createAccessControlContext"));
84        }
85        // no need to clone() here as ACC is immutable
86        this.context = acc.context;
87        this.combiner = combiner;
88    }
89
90    /**
91     * Constructs a new instance of {@code AccessControlContext} with the
92     * specified array of {@code ProtectionDomain}s.
93     *
94     * @param context
95     *            the {@code ProtectionDomain}s that are used to perform access
96     *            checks in the context of this {@code AccessControlContext}
97     * @throws NullPointerException
98     *             if {@code context} is {@code null}
99     * @since Android 1.0
100     */
101    public AccessControlContext(ProtectionDomain[] context) {
102        if (context == null) {
103            throw new NullPointerException("context can not be null");
104        }
105        if (context.length != 0) {
106            // remove dup entries
107            ArrayList<ProtectionDomain> a = new ArrayList<ProtectionDomain>();
108            for (int i = 0; i < context.length; i++) {
109                if (context[i] != null && !a.contains(context[i])) {
110                    a.add(context[i]);
111                }
112            }
113            if (a.size() != 0) {
114                this.context = new ProtectionDomain[a.size()];
115                a.toArray(this.context);
116            }
117        }
118        if (this.context == null) {
119            // Prevent numerous checks for 'context==null'
120            this.context = new ProtectionDomain[0];
121        }
122    }
123
124    /**
125     * Package-level ctor which is used in AccessController.<br>
126     * ProtectionDomains passed as <code>stack</code> is then passed into
127     * {@link #AccessControlContext(ProtectionDomain[])}, therefore:<br>
128     * <il>
129     * <li>it must not be null
130     * <li>duplicates will be removed
131     * <li>null-s will be removed
132     * </li>
133     *
134     * @param stack - array of ProtectionDomains
135     * @param inherited - inherited context, which may be null
136     */
137    AccessControlContext(ProtectionDomain[] stack,
138            AccessControlContext inherited) {
139        this(stack); // removes dups, removes nulls, checks for stack==null
140        this.inherited = inherited;
141    }
142
143    /**
144     * Package-level ctor which is used in AccessController.<br>
145     * ProtectionDomains passed as <code>stack</code> is then passed into
146     * {@link #AccessControlContext(ProtectionDomain[])}, therefore:<br>
147     * <il>
148     * <li>it must not be null
149     * <li>duplicates will be removed
150     * <li>null-s will be removed
151     * </li>
152     *
153     * @param stack - array of ProtectionDomains
154     * @param combiner - combiner
155     */
156    AccessControlContext(ProtectionDomain[] stack,
157            DomainCombiner combiner) {
158        this(stack); // removes dups, removes nulls, checks for stack==null
159        this.combiner = combiner;
160    }
161
162    /**
163     * Checks the specified permission against the vm's current security policy.
164     * The check is based on this {@code AccessControlContext} as opposed to the
165     * {@link AccessController#checkPermission(Permission)} method which
166     * performs access checks based on the context of the current thread. This
167     * method returns silently if the permission is granted, otherwise an
168     * {@code AccessControlException} is thrown.
169     * <p>
170     * A permission is considered granted if every {@link ProtectionDomain} in
171     * this context has been granted the specified permission.
172     * <p>
173     * If privileged operations are on the call stack, only the {@code
174     * ProtectionDomain}s from the last privileged operation are taken into
175     * account.
176     * <p>
177     * If inherited methods are on the call stack, the protection domains of the
178     * declaring classes are checked, not the protection domains of the classes
179     * on which the method is invoked.
180     *
181     * @param perm
182     *            the permission to check against the policy
183     * @throws AccessControlException
184     *             if the specified permission is not granted
185     * @throws NullPointerException
186     *             if the specified permission is {@code null}
187     * @see AccessController#checkPermission(Permission)
188     * @since Android 1.0
189     */
190    public void checkPermission(Permission perm) throws AccessControlException {
191        if (perm == null) {
192            throw new NullPointerException("Permission cannot be null");
193        }
194        for (int i = 0; i < context.length; i++) {
195            if (!context[i].implies(perm)) {
196                throw new AccessControlException("Permission check failed "
197                        + perm, perm);
198            }
199        }
200        if (inherited != null) {
201            inherited.checkPermission(perm);
202        }
203    }
204
205
206    /**
207     * Compares the specified object with this {@code AccessControlContext} for
208     * equality. Returns {@code true} if the specified object is also an
209     * instance of {@code AccessControlContext}, and the two contexts
210     * encapsulate the same {@code ProtectionDomain}s. The order of the {@code
211     * ProtectionDomain}s is ignored by this method.
212     *
213     * @param obj
214     *            object to be compared for equality with this {@code
215     *            AccessControlContext}
216     * @return {@code true} if the specified object is equal to this {@code
217     *         AccessControlContext}, otherwise {@code false}
218     * @since Android 1.0
219     */
220    @Override
221    public boolean equals(Object obj) {
222        if (this == obj) {
223            return true;
224        }
225        if (obj instanceof AccessControlContext) {
226            AccessControlContext that = (AccessControlContext) obj;
227            if (!(PolicyUtils.matchSubset(context, that.context) && PolicyUtils
228                    .matchSubset(that.context, context))) {
229                return false;
230            }
231            // BEGIN android-changed
232            if(combiner != null) {
233                return combiner.equals(that.combiner);
234            }
235            return that.combiner == null;
236            // END android-changed
237        }
238        return false;
239    }
240
241    /**
242     * Returns the {@code DomainCombiner} associated with this {@code
243     * AccessControlContext}.
244     * <p>
245     * If a {@code SecurityManager} is installed, code calling this method needs
246     * the {@code SecurityPermission} {@code getDomainCombiner} to be granted,
247     * otherwise a {@code SecurityException} will be thrown.
248     *
249     * @return the {@code DomainCombiner} associated with this {@code
250     *         AccessControlContext}
251     * @throws SecurityException
252     *             if a {@code SecurityManager} is installed and the caller does
253     *             not have permission to invoke this method
254     * @since Android 1.0
255     */
256    public DomainCombiner getDomainCombiner() {
257        SecurityManager sm = System.getSecurityManager();
258        if (sm != null) {
259            sm.checkPermission(new SecurityPermission("getDomainCombiner"));
260        }
261        return combiner;
262    }
263
264
265    /**
266     * Returns the hash code value for this {@code AccessControlContext}.
267     * Returns the same hash code for {@code AccessControlContext}s that are
268     * equal to each other as required by the general contract of
269     * {@link Object#hashCode}.
270     *
271     * @return the hash code value for this {@code AccessControlContext}
272     * @see Object#equals(Object)
273     * @see AccessControlContext#equals(Object)
274     * @since Android 1.0
275     */
276    public int hashCode() {
277        int hash = 0;
278        for (int i = 0; i < context.length; i++) {
279            hash ^= context[i].hashCode();
280        }
281        return hash;
282    }
283
284}
285