jakarta.security.jacc.EJBMethodPermission Maven / Gradle / Ivy
/*
* Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package jakarta.security.jacc;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.lang.reflect.Method;
import java.security.Permission;
import java.util.HashMap;
/**
* Class for Jakarta Enterprise Beans method permissions.
*
*
* The name of an EJBMethodPermission contains the value of the ejb-name element in the application's deployment
* descriptor that identifies the target Jakarta Enterprise Bean.
*
*
* The actions of an EJBMethodPermission identifies the methods of the Jakarta Enterprise Bean to which the permission applies.
*
*
* Implementations of this class MAY implement newPermissionCollection or inherit its implementation from the super
* class.
*
* @see java.security.Permission
*
* @author Ron Monzillo
* @author Gary Ellison
*
*/
public final class EJBMethodPermission extends Permission {
private static final long serialVersionUID = 1L;
/**
* The serialized fields of this permission are defined below. Whether or not the serialized fields correspond to actual
* (private) fields is an implementation decision.
*
* @serialField actions String the canonicalized actions string (as returned by getActions).
*/
private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("actions", String.class) };
private static final String interfaceKeys[] = { "Local", "LocalHome", "Remote", "Home", "ServiceEndpoint" };
private static HashMap interfaceHash = new HashMap();
static {
for (int i = 0; i < interfaceKeys.length; i++) {
interfaceHash.put(interfaceKeys[i], i);
}
}
private transient int methodInterface;
private transient String otherMethodInterface;
private transient String methodName;
private transient String methodParams;
private transient String actions;
private transient int hashCodeValue;
/**
* Creates a new EJBMethodPermission with the specified name and actions.
*
*
* The name contains the value of the ejb-name element corresponding to an Jakarta Enterprise Bean in the application's deployment
* descriptor.
*
*
* The actions contains a methodSpec. The syntax of the actions parameter is defined as follows:
*
*
* methodNameSpec ::= methodName | emptyString
*
* methodInterfaceName ::= String
*
* methodInterfaceSpec ::= methodInterfaceName | emptyString
*
* typeName ::= typeName | typeName []
*
* methodParams ::= typeName | methodParams comma typeName
*
* methodParamsSpec ::= emptyString | methodParams
*
* methodSpec ::= null |
* methodNameSpec |
* methodNameSpec comma methodInterfaceName |
* methodNameSpec comma methodInterfaceSpec comma methodParamsSpec
*
*
*
* A MethodInterfaceName is a non-empty String and should contain a method-intf value as defined for use in Jakarta Enterprise Beans
* deployment descriptors. An implementation must be flexible such that it supports additional interface names
* especially if they are standardized by the Jakarta Enterprise Beans Specification. The Jakarta Enterprise Beans Specification
* currently defines the following method-intf values:
*
*
* { "Home", "LocalHome", "Remote", "Local", "ServiceEndpoint" }
*
*
*
* A null or empty string methodSpec indicates that the permission applies to all methods of the Jakarta Enterprise Bean.
* A methodSpec with a methodNameSpec of the empty string matches all methods of the Jakarta Enterprise Bean that match the
* methodInterface and methodParams elements of the methodSpec.
*
*
* A methodSpec with a methodInterfaceSpec of the empty string matches all methods of the Jakarta Enterprise Bean that match the
* methodNameSpec and methodParamsSpec elements of the methodSpec.
*
*
* A methodSpec without a methodParamsSpec matches all methods of the Jakarta Enterprise Bean that match the methodNameSpec and
* methodInterface elements of the methodSpec.
*
*
* The order of the typeNames in methodParams array must match the order of occurence of the corresponding parameters in
* the method signature of the target method(s). Each typeName in the methodParams must contain the canonical form of
* the corresponding parameter's typeName as defined by the getActions method. A methodSpec with an empty
* methodParamsSpec matches all 0 argument methods of the Jakarta Enterprise Bean that match the methodNameSpec and
* methodInterfaceSpec elements of the methodSpec.
*
* @param name of the Jakarta Enterprise Bean to which the permission pertains.
* @param actions identifies the methods of the Jakarta Enterprise Bean to which the permission pertains.
*/
public EJBMethodPermission(String name, String actions) {
super(name);
setMethodSpec(actions);
}
/**
* Creates a new EJBMethodPermission with name corresponding to the EJBName and actions composed from methodName,
* methodInterface, and methodParams.
*
* @param EJBName The string representation of the name of the Jakarta Enterprise Bean as it appears in the corresponding
* ejb-name element
* in the deployment descriptor.
* @param methodName A string that may be used to indicate the method of the Jakarta Enterprise Bean to which the permission
* pertains. A value of null or "" indicates that the permission pertains to all methods that match the other parameters of
* the permission specification without consideration of method name.
* @param methodInterface A string that may be used to specify the Jakarta Enterprise Bean interface to which the permission
* pertains. A value of null or "", indicates that the permission pertains to all methods that match the other parameters of
* the permission specification without consideration of the interface they occur on.
* @param methodParams An array of strings that may be used to specify (by typeNames) the parameter signature of the
* target methods. The order of the typeNames in methodParams array must match the order of occurrence of the
* corresponding parameters in the method signature of the target method(s). Each typeName in the methodParams array
* must contain the canonical form of the corresponding parameter's typeName as defined by the getActions method. An
* empty methodParams array is used to represent a method signature with no arguments. A value of null indicates that
* the permission pertains to all methods that match the other parameters of the permission specification without
* consideration of method signature.
*/
public EJBMethodPermission(String EJBName, String methodName, String methodInterface, String[] methodParams) {
super(EJBName);
setMethodSpec(methodName, methodInterface, methodParams);
}
/**
* Creates a new EJBMethodPermission with name corresponding to the EJBName and actions composed from methodInterface,
* and the Method object.
*
*
* A container uses this constructor prior to checking if a caller has permission to call the method of an Jakarta
* Enterprise Bean.
*
* @param EJBName The string representation of the name of the Jakarta Enterprise Bean as it appears in the
* corresponding ejb-name element in the deployment descriptor.
* @param methodInterface A string that may be used to specify the Jakarta Enterprise Bean interface to which the
* permission pertains. A value of null or "", indicates that the permission pertains to all methods that match
* the other parameters of the permission specification without consideration of the interface they occur on.
* @param method an instance of the Java.lang.reflect.Method class corresponding to the method that the container is
* trying to determine whether the caller has permission to access. This value must not be null.
*/
public EJBMethodPermission(String EJBName, String methodInterface, Method method) {
super(EJBName);
setMethodSpec(methodInterface, method);
}
/**
* Checks two EJBMethodPermission objects for equality. EJBMethodPermission objects are equivalent if they have case
* sensitive equivalent name and actions values.
*
*
* Two Permission objects, P1 and P2, are equivalent if and only if P1.implies(P2) AND P2.implies(P1).
*
* @param o the EJBMethodPermission object being tested for equality with this EJBMethodPermission
*
* @return true if the argument EJBMethodPermission object is equivalent to this EJBMethodPermission.
*/
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof EJBMethodPermission)) {
return false;
}
EJBMethodPermission that = (EJBMethodPermission) o;
if (!this.getName().equals(that.getName())) {
return false;
}
if (this.methodName != null) {
if (that.methodName == null || !this.methodName.equals(that.methodName)) {
return false;
}
} else if (that.methodName != null) {
return false;
}
if (this.methodInterface != that.methodInterface) {
return false;
}
if (this.methodInterface == -2 && !this.otherMethodInterface.equals(that.otherMethodInterface)) {
return false;
}
if (this.methodParams != null) {
if (that.methodParams == null || !this.methodParams.equals(that.methodParams)) {
return false;
}
} else if (that.methodParams != null) {
return false;
}
return true;
}
/**
* Returns a String containing a canonical representation of the actions of this EJBMethodPermission. The Canonical form
* of the actions of an EJBMethodPermission is described by the following syntax description.
*
*
* methodNameSpec ::= methodName | emptyString
*
* methodInterfaceName ::= String
*
* methodInterfaceSpec ::= methodInterfaceName | emptyString
*
* typeName ::= typeName | typeName []
*
* methodParams ::= typeName | methodParams comma typeName
*
* methodParamsSpec ::= emptyString | methodParams
*
* methodSpec ::= null |
* methodName |
* methodNameSpec comma methodInterfaceName |
* methodNameSpec comma methodInterfaceSpec comma methodParamsSpec
*
*
*
* The canonical form of each typeName must begin with the fully qualified Java name of the corresponding parameter's
* type. The canonical form of a typeName for an array parameter is the fully qualified Java name of the array's
* component type followed by as many instances of the string "[]" as there are dimensions to the array. No additional
* characters (e.g. blanks) may occur in the canonical form.
*
*
* A MethodInterfaceName is a non-empty String and should contain a method-intf value as defined for use in Jakarta
* Enterprise Beans deployment descriptors. An implementation must be flexible such that it supports additional interface
* names especially if they are standardized by the Jakarta Enterprise Beans Specification. The Jakarta Enterprise Beans
* Specification currently defines the following method-intf values:
*
*
* { "Home", "LocalHome", "Remote", "Local", "ServiceEndpoint" }
*
*
* @return a String containing the canonicalized actions of this EJBMethodPermission.
*/
@Override
public String getActions() {
if (this.actions == null) {
String iSpec = (this.methodInterface == -1 ? null : (this.methodInterface < 0 ? this.otherMethodInterface : interfaceKeys[this.methodInterface]));
if (this.methodName == null) {
if (iSpec == null) {
if (this.methodParams != null) {
this.actions = "," + this.methodParams;
}
} else if (this.methodParams == null) {
this.actions = "," + iSpec;
} else {
this.actions = "," + iSpec + this.methodParams;
}
} else if (iSpec == null) {
if (this.methodParams == null) {
this.actions = this.methodName;
} else {
this.actions = this.methodName + "," + this.methodParams;
}
} else if (this.methodParams == null) {
this.actions = this.methodName + "," + iSpec;
} else {
this.actions = this.methodName + "," + iSpec + this.methodParams;
}
}
return this.actions;
}
/**
* Returns the hash code value for this EJBMethodPermission.
*
*
* The properties of the returned hash code must be as follows:
*
*
* - During the lifetime of a Java application, the hashCode method must return the same integer value every time it
* is called on a EJBMethodPermission object. The value returned by hashCode for a particular EJBMethodPermission need
* not remain consistent from one execution of an application to another.
*
- If two EJBMethodPermission objects are equal according to the equals method, then calling the hashCode method on
* each of the two Permission objects must produce the same integer result (within an application).
*
*
* @return the integer hash code value for this object.
*/
@Override
public int hashCode() {
if (hashCodeValue == 0) {
String hashInput;
String actions = this.getActions();
if (actions == null) {
hashInput = this.getName();
} else {
hashInput = this.getName() + " " + actions;
}
hashCodeValue = hashInput.hashCode();
}
return this.hashCodeValue;
}
/**
* Determines if the argument Permission is "implied by" this EJBMethodPermission.
*
*
* For this to be the case,
*
* - The argument must be an instanceof EJBMethodPermission
*
- with name equivalent to that of this EJBMethodPermission, and
*
- the methods to which the argument permission applies (as defined in its actions) must be a subset of the methods
* to which this EJBMethodPermission applies (as defined in its actions).
*
*
*
* The argument permission applies to a subset of the methods to which this permission applies if all of the following
* conditions are met.
*
* - the method name component of the methodNameSpec of this permission is null, the empty string, or equivalent to
* the method name of the argument permission, and
*
- the method interface component of the methodNameSpec of this permission is null, the empty string, or equivalent
* to the method interface of the argument permission, and
*
- the method parameter list component of the methodNameSpec of this permission is null, the empty string, or
* equivalent to the method parameter list of the argument permission.
*
*
*
* The name and actions comparisons described above are case sensitive.
*
* @param permission "this" EJBMethodPermission is checked to see if it implies the argument permission.
* @return true if the specified permission is implied by this object, false if not.
*/
@Override
public boolean implies(Permission permission) {
if (permission == null || !(permission instanceof EJBMethodPermission)) {
return false;
}
EJBMethodPermission that = (EJBMethodPermission) permission;
if (!this.getName().equals(that.getName())) {
return false;
}
if (this.methodName != null && (that.methodName == null || !this.methodName.equals(that.methodName))) {
return false;
}
if (this.methodInterface != -1 && (that.methodInterface == -1 || this.methodInterface != that.methodInterface)) {
return false;
}
if (this.methodInterface == -2 && !this.otherMethodInterface.equals(that.otherMethodInterface)) {
return false;
}
if (this.methodParams != null && (that.methodParams == null || !this.methodParams.equals(that.methodParams))) {
return false;
}
return true;
}
// ----------------- Private Methods ---------------------
/**
* readObject reads the serialized fields from the input stream and uses them to restore the permission. This method
* need not be implemented if establishing the values of the serialized fields (as is done by defaultReadObject) is
* sufficient to initialize the permission.
*
* @param inputStream The stream from which the fields are read
*
* @throws ClassNotFoundException If the class of an object couldn't be found
* @throws IOException If an I/O error occurs
*/
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
setMethodSpec((String) inputStream.readFields().get("actions", null));
}
/**
* writeObject is used to establish the values of the serialized fields before they are written to the output stream and
* need not be implemented if the values of the serialized fields are always available and up to date. The serialized
* fields are written to the output stream in the same form as they would be written by defaultWriteObject.
*
* @param outputStream The stream to which the serialized fields are written
*
* @throws IOException If an I/O error occurs while writing to the underlying stream
*
*/
private synchronized void writeObject(ObjectOutputStream outputStream) throws IOException {
outputStream.putFields().put("actions", this.getActions());
outputStream.writeFields();
}
private void setMethodSpec(String actions) {
String mInterface = null;
this.methodName = null;
this.methodParams = null;
if (actions != null) {
if (actions.length() > 0) {
int i = actions.indexOf(',');
if (i < 0) {
this.methodName = actions;
} else if (i >= 0) {
if (i != 0) {
this.methodName = actions.substring(0, i);
}
if (actions.length() == i + 1) {
throw new IllegalArgumentException("illegal actions spec");
}
int j = actions.substring(i + 1).indexOf(',');
if (j < 0) {
mInterface = actions.substring(i + 1);
} else {
if (j > 0) {
mInterface = actions.substring(i + 1, i + j + 1);
}
this.methodParams = actions.substring(i + j + 1);
if (this.methodParams.length() > 1 && this.methodParams.endsWith(",")) {
throw new IllegalArgumentException("illegal methodParam");
}
}
}
} else {
// canonical form of emptystring actions is null
actions = null;
}
}
this.methodInterface = validateInterface(mInterface);
if (this.methodInterface < -1) {
this.otherMethodInterface = mInterface;
}
this.actions = actions;
}
private void setMethodSpec(String methodName, String mInterface, String[] methodParams) {
if (methodName != null && methodName.indexOf(',') >= 0) {
throw new IllegalArgumentException("illegal methodName");
}
this.methodInterface = validateInterface(mInterface);
if (this.methodInterface < -1) {
this.otherMethodInterface = mInterface;
}
if (methodParams != null) {
StringBuffer mParams = new StringBuffer(",");
for (int i = 0; i < methodParams.length; i++) {
if (methodParams[i] == null || methodParams[i].indexOf(',') >= 0) {
throw new IllegalArgumentException("illegal methodParam");
}
if (i == 0) {
mParams.append(methodParams[i]);
} else {
mParams.append("," + methodParams[i]);
}
}
this.methodParams = mParams.toString();
} else {
this.methodParams = null;
}
this.methodName = methodName;
}
private void setMethodSpec(String mInterface, Method method) {
this.methodInterface = validateInterface(mInterface);
if (this.methodInterface < -1) {
this.otherMethodInterface = mInterface;
}
this.methodName = method.getName();
Class>[] params = method.getParameterTypes();
StringBuffer methodParameters = new StringBuffer(",");
for (int i = 0; i < params.length; i++) {
String parameterName = params[i].getName();
Class> componentType = params[i].getComponentType();
// Canonicalize parameter if it is an Array.
if (componentType != null) {
String brackets = "[]";
while (componentType.getComponentType() != null) {
componentType = componentType.getComponentType();
brackets = brackets + "[]";
}
parameterName = componentType.getName() + brackets;
}
if (i == 0) {
methodParameters.append(parameterName);
} else {
methodParameters.append("," + parameterName);
}
}
this.methodParams = methodParameters.toString();
}
private static int validateInterface(String methodInterface) {
int result = -1;
if (methodInterface != null && methodInterface.length() > 0) {
Integer i = (Integer) interfaceHash.get(methodInterface);
if (i != null) {
result = i.intValue();
} else {
result = -2;
}
}
return result;
}
}