blob: b161b7f62cabd7be93baebe72c939020021d73cf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.security;
import java.util.ArrayList;
import org.apache.harmony.security.fortress.PolicyUtils;
/**
* {@code AccessControlContext} encapsulates the {@code ProtectionDomain}s on
* which access control decisions are based.
*/
public final class AccessControlContext {
// List of ProtectionDomains wrapped by the AccessControlContext
// It has the following characteristics:
// - 'context' can not be null
// - never contains null(s)
// - all elements are unique (no dups)
ProtectionDomain[] context;
DomainCombiner combiner;
// An AccessControlContext inherited by the current thread from its parent
private AccessControlContext inherited;
/**
* Constructs a new instance of {@code AccessControlContext} with the
* specified {@code AccessControlContext} and {@code DomainCombiner}.
* <p>
* If a {@code SecurityManager} is installed, code calling this constructor
* need the {@code SecurityPermission} {@code createAccessControlContext} to
* be granted, otherwise a {@code SecurityException} will be thrown.
*
* @param acc
* the {@code AccessControlContext} related to the given {@code
* DomainCombiner}
* @param combiner
* the {@code DomainCombiner} related to the given {@code
* AccessControlContext}
* @throws SecurityException
* if a {@code SecurityManager} is installed and the caller does
* not have permission to invoke this constructor
* @throws NullPointerException
* if {@code acc} is {@code null}
*/
public AccessControlContext(AccessControlContext acc,
DomainCombiner combiner) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SecurityPermission(
"createAccessControlContext"));
}
// no need to clone() here as ACC is immutable
this.context = acc.context;
this.combiner = combiner;
}
/**
* Constructs a new instance of {@code AccessControlContext} with the
* specified array of {@code ProtectionDomain}s.
*
* @param context
* the {@code ProtectionDomain}s that are used to perform access
* checks in the context of this {@code AccessControlContext}
* @throws NullPointerException
* if {@code context} is {@code null}
*/
public AccessControlContext(ProtectionDomain[] context) {
if (context == null) {
throw new NullPointerException("context can not be null");
}
if (context.length != 0) {
// remove dup entries
ArrayList<ProtectionDomain> a = new ArrayList<ProtectionDomain>();
for (int i = 0; i < context.length; i++) {
if (context[i] != null && !a.contains(context[i])) {
a.add(context[i]);
}
}
if (a.size() != 0) {
this.context = new ProtectionDomain[a.size()];
a.toArray(this.context);
}
}
if (this.context == null) {
// Prevent numerous checks for 'context==null'
this.context = new ProtectionDomain[0];
}
}
/**
* Package-level ctor which is used in AccessController.<br>
* ProtectionDomains passed as <code>stack</code> is then passed into
* {@link #AccessControlContext(ProtectionDomain[])}, therefore:<br>
* <il>
* <li>it must not be null
* <li>duplicates will be removed
* <li>null-s will be removed
* </li>
*
* @param stack - array of ProtectionDomains
* @param inherited - inherited context, which may be null
*/
AccessControlContext(ProtectionDomain[] stack,
AccessControlContext inherited) {
this(stack); // removes dups, removes nulls, checks for stack==null
this.inherited = inherited;
}
/**
* Package-level ctor which is used in AccessController.<br>
* ProtectionDomains passed as <code>stack</code> is then passed into
* {@link #AccessControlContext(ProtectionDomain[])}, therefore:<br>
* <il>
* <li>it must not be null
* <li>duplicates will be removed
* <li>null-s will be removed
* </li>
*
* @param stack - array of ProtectionDomains
* @param combiner - combiner
*/
AccessControlContext(ProtectionDomain[] stack,
DomainCombiner combiner) {
this(stack); // removes dups, removes nulls, checks for stack==null
this.combiner = combiner;
}
/**
* Checks the specified permission against the vm's current security policy.
* The check is based on this {@code AccessControlContext} as opposed to the
* {@link AccessController#checkPermission(Permission)} method which
* performs access checks based on the context of the current thread. This
* method returns silently if the permission is granted, otherwise an
* {@code AccessControlException} is thrown.
* <p>
* A permission is considered granted if every {@link ProtectionDomain} in
* this context has been granted the specified permission.
* <p>
* If privileged operations are on the call stack, only the {@code
* ProtectionDomain}s from the last privileged operation are taken into
* account.
* <p>
* If inherited methods are on the call stack, the protection domains of the
* declaring classes are checked, not the protection domains of the classes
* on which the method is invoked.
*
* @param perm
* the permission to check against the policy
* @throws AccessControlException
* if the specified permission is not granted
* @throws NullPointerException
* if the specified permission is {@code null}
* @see AccessController#checkPermission(Permission)
*/
public void checkPermission(Permission perm) throws AccessControlException {
if (perm == null) {
throw new NullPointerException("Permission cannot be null");
}
for (int i = 0; i < context.length; i++) {
if (!context[i].implies(perm)) {
throw new AccessControlException("Permission check failed "
+ perm, perm);
}
}
if (inherited != null) {
inherited.checkPermission(perm);
}
}
/**
* Compares the specified object with this {@code AccessControlContext} for
* equality. Returns {@code true} if the specified object is also an
* instance of {@code AccessControlContext}, and the two contexts
* encapsulate the same {@code ProtectionDomain}s. The order of the {@code
* ProtectionDomain}s is ignored by this method.
*
* @param obj
* object to be compared for equality with this {@code
* AccessControlContext}
* @return {@code true} if the specified object is equal to this {@code
* AccessControlContext}, otherwise {@code false}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof AccessControlContext) {
AccessControlContext that = (AccessControlContext) obj;
if (!(PolicyUtils.matchSubset(context, that.context) && PolicyUtils
.matchSubset(that.context, context))) {
return false;
}
// BEGIN android-changed
if (combiner != null) {
return combiner.equals(that.combiner);
}
return that.combiner == null;
// END android-changed
}
return false;
}
/**
* Returns the {@code DomainCombiner} associated with this {@code
* AccessControlContext}.
* <p>
* If a {@code SecurityManager} is installed, code calling this method needs
* the {@code SecurityPermission} {@code getDomainCombiner} to be granted,
* otherwise a {@code SecurityException} will be thrown.
*
* @return the {@code DomainCombiner} associated with this {@code
* AccessControlContext}
* @throws SecurityException
* if a {@code SecurityManager} is installed and the caller does
* not have permission to invoke this method
*/
public DomainCombiner getDomainCombiner() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new SecurityPermission("getDomainCombiner"));
}
return combiner;
}
/**
* Returns the hash code value for this {@code AccessControlContext}.
* Returns the same hash code for {@code AccessControlContext}s that are
* equal to each other as required by the general contract of
* {@link Object#hashCode}.
*
* @return the hash code value for this {@code AccessControlContext}
* @see Object#equals(Object)
* @see AccessControlContext#equals(Object)
*/
public int hashCode() {
int hash = 0;
for (int i = 0; i < context.length; i++) {
hash ^= context[i].hashCode();
}
return hash;
}
}