001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package javax.security.jacc;
023:
024: import java.io.Serializable;
025: import java.io.ObjectStreamField;
026: import java.io.ObjectInputStream;
027: import java.io.IOException;
028: import java.io.ObjectOutputStream;
029: import java.lang.reflect.Method;
030: import java.security.Permission;
031: import java.util.ArrayList;
032: import java.util.StringTokenizer;
033:
034: import org.jboss.util.id.SerialVersion;
035:
036: /** A security permission for ejb-method permissions. The name of an
037: * EJBMethodPermission contains the value of the ejb-name element in the
038: * application's deployment descriptor that identifies the target EJB.
039: *
040: * The actions of an EJBMethodPermission identifies the methods of the EJB to
041: * which the permission applies.
042: *
043: * Implementations of this class MAY implement newPermissionCollection or
044: * inherit its implementation from the super class.
045: *
046: * @link http://java.sun.com/j2ee/1.4/docs/api/
047: *
048: * @author Scott.Stark@jboss.org
049: * @author Ron Monzillo, Gary Ellison (javadoc)
050: * @version $Revision: 57196 $
051: */
052: public final class EJBMethodPermission extends Permission implements
053: Serializable {
054: /** @since 4.0.2 */
055: private static final long serialVersionUID;
056: static {
057: if (SerialVersion.version == SerialVersion.LEGACY)
058: serialVersionUID = -2677271975280050210L;
059: else
060: serialVersionUID = 1;
061: }
062:
063: /**
064: * @serialField actions String the actions string.
065: */
066: private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField(
067: "actions", String.class) };
068:
069: private transient String methodName;
070: private transient String methodInterface;
071: private transient String methodSig;
072:
073: /** Creates a new EJBMethodPermission with the specified name and actions.
074:
075: The name contains the value of the ejb-name element corresponding to an EJB
076: in the application's deployment descriptor.
077:
078: The actions contains a methodSpec. The syntax of the actions parameter is
079: defined as follows:
080:
081: methodNameSpec ::= methodName | emptyString
082:
083: methodInterfaceName ::= String
084:
085: methodInterfaceSpec ::= methodInterfaceName | emptyString
086:
087: typeName ::= typeName | typeName []
088:
089: methodParams ::= typeName | methodParams comma typeName
090:
091: methodParamsSpec ::= emptyString | methodParams
092:
093: methodSpec ::= null |
094: methodNameSpec |
095: methodNameSpec comma methodInterfaceName |
096: methodNameSpec comma methodInterfaceSpec comma methodParamsSpec
097:
098:
099: A MethodInterfaceName is a non-empty String and should contain a method-intf
100: value as defined for use in EJB deployment descriptors. An implementation
101: must be flexible such that it supports additional interface names especially
102: if they are standardized by the EJB Specification. The EJB Specification
103: currently defines the following method-intf values:
104:
105: { "Home", "LocalHome", "Remote", "Local", "ServiceEndpoint" }
106:
107:
108: A null or empty string methodSpec indicates that the permission applies to
109: all methods of the EJB. A methodSpec with a methodNameSpec of the empty
110: string matches all methods of the EJB that match the methodInterface and
111: methodParams elements of the methodSpec.
112:
113: A methodSpec with a methodInterfaceSpec of the empty string matches all
114: methods of the EJB that match the methodNameSpec and methodParamsSpec
115: elements of the methodSpec.
116:
117: A methodSpec without a methodParamsSpec matches all methods of the EJB that
118: match the methodNameSpec and methodInterface elements of the methodSpec.
119:
120: The order of the typeNames in methodParams array must match the order of
121: occurence of the corresponding parameters in the method signature of the
122: target method(s). Each typeName in the methodParams must contain the
123: canonical form of the corresponding parameter's typeName as defined by the
124: getActions method. A methodSpec with an empty methodParamsSpec matches all
125: 0 argument methods of the EJB that match the methodNameSpec and
126: methodInterfaceSpec elements of the methodSpec.
127:
128: * @param name - the ejb-name to which the permission pertains.
129: * @param actions - identifies the methods of the EJB to which the permission
130: * pertains.
131: */
132: public EJBMethodPermission(String name, String actions) {
133: super (name);
134: parseMethodSpec(actions);
135: }
136:
137: /** Creates a new EJBMethodPermission with name corresponding to the EJBName
138: * and actions composed from methodInterface, and the Method object.
139: *
140: * A container uses this constructor prior to checking if a caller has
141: * permission to call the method of an EJB.
142: *
143: * @param ejbName - the ejb-name of the target EJB
144: * @param methodInterface - A string that may be used to specify the EJB
145: * interface to which the permission pertains. A value of null or "",
146: * indicates that the permission pertains to all methods that match the other
147: * parameters of the permission specification without consideration of the
148: * interface they occur on.
149: * @param method - an instance of the Java.lang.reflect.Method class
150: * corresponding to the method that the container is trying to determine
151: * whether the caller has permission to access. This value must not be null.
152: */
153: public EJBMethodPermission(String ejbName, String methodInterface,
154: Method method) {
155: this (ejbName, method.getName(), methodInterface,
156: convertParameters(method.getParameterTypes()));
157: }
158:
159: /** Creates a new EJBMethodPermission with name corresponding to the EJBName
160: * and actions composed from methodName, methodInterface, and methodParams.
161: *
162: * @param ejbName - the ejb-name of the target EJB
163: * @param methodName - A string that may be used to indicate the method of the
164: * EJB to which the permission pertains. A value of null or "" indicates that
165: * the permission pertains to all methods that match the other parameters of
166: * the permission specification without consideration of method name.
167: * @param methodInterface - A string that may be used to specify the EJB
168: * interface to which the permission pertains. A value of null or "",
169: * indicates that the permission pertains to all methods that match the
170: * other parameters of the permission specification without consideration of
171: * the interface they occur on.
172: * @param methodParams - An array of strings that may be used to specify
173: * (by typeNames) the parameter signature of the target methods. The order of
174: * the typeNames in methodParams array must match the order of occurence of
175: * the corresponding parameters in the method signature of the target
176: * method(s). Each typeName in the methodParams array must contain the
177: * canonical form of the corresponding parameter's typeName as defined by the
178: * getActions method. An empty methodParams array is used to represent a
179: * method signature with no arguments. A value of null indicates that the
180: * permission pertains to all methods that match the other parameters of the
181: * permission specification without consideration of method signature.
182: */
183: public EJBMethodPermission(String ejbName, String methodName,
184: String methodInterface, String[] methodParams) {
185: super (ejbName);
186: this .methodInterface = methodInterface;
187: this .methodName = methodName;
188: if (methodParams == null)
189: methodSig = null;
190: else {
191: StringBuffer tmp = new StringBuffer();
192: for (int n = 0; n < methodParams.length; n++) {
193: tmp.append(methodParams[n]);
194: tmp.append(',');
195: }
196: if (tmp.length() > 0)
197: tmp.setLength(tmp.length() - 1);
198: methodSig = tmp.toString();
199: }
200: }
201:
202: /** Compare two EJBMethodPermissions.
203: *
204: * @param p the EJBMethodPermission instance to compare against
205: * @return true if p equates to this permission, false otherwise
206: */
207: public boolean equals(Object p) {
208: boolean equals = false;
209: if (p == null || !(p instanceof EJBMethodPermission))
210: return false;
211: EJBMethodPermission perm = (EJBMethodPermission) p;
212: equals = getName().equals(perm.getName());
213: if (equals == true) {
214: // Check the method names
215: if (methodName != null) {
216: if (perm.methodName == null)
217: return false;
218: if (methodName.equals(perm.methodName) == false)
219: return false;
220: } else if (perm.methodName != null) {
221: return false;
222: }
223:
224: // Check the method interfaces
225: equals = methodInterface != perm.methodInterface;
226: if (equals == false && methodInterface != null)
227: equals = methodInterface.equals(perm.methodInterface);
228: if (equals == false)
229: return false;
230:
231: // Check the method parameters
232: if (methodSig != null) {
233: equals = perm.methodSig != null
234: && methodSig.equals(perm.methodSig);
235: } else {
236: equals = perm.methodSig == null;
237: }
238: }
239: return equals;
240: }
241:
242: /** Calculates the hash code as the hash of the methodName,
243: * methodInterface and methodSig for each that is non-null.
244: * @return has the method represented.
245: */
246: public int hashCode() {
247: int hashCode = 0;
248: if (methodName != null)
249: hashCode += methodName.hashCode();
250: if (methodInterface != null)
251: hashCode += methodInterface.hashCode();
252: if (methodSig != null)
253: hashCode += methodSig.hashCode();
254: return hashCode;
255: }
256:
257: /** Returns a String containing a canonical representation of the actions of
258: this EJBMethodPermission. The Canonical form of the actions of an
259: EJBMethodPermission is described by the following syntax description.
260:
261: methodNameSpec ::= methodName | emptyString
262: methodInterfaceName ::= String
263: methodInterfaceSpec ::= methodInterfaceName | emptyString
264: typeName ::= typeName | typeName []
265: methodParams ::= typeName | methodParams comma typeName
266: methodParamsSpec ::= emptyString | methodParams
267: methodSpec ::= null |
268: methodName |
269: methodNameSpec comma methodInterfaceName |
270: methodNameSpec comma methodInterfaceSpec comma methodParamsSpec
271:
272:
273: The canonical form of each typeName must begin with the fully qualified Java
274: name of the corresponding parameter's type. The canonical form of a typeName
275: for an array parameter is the fully qualified Java name of the array's
276: component type followed by as many instances of the string "[]" as there are
277: dimensions to the array. No additional characters (e.g. blanks) may occur in
278: the canonical form.
279:
280: A MethodInterfaceName is a non-empty String and should contain a method-intf
281: value as defined for use in EJB deployment descriptors. An implementation
282: must be flexible such p it supports additional interface names especially
283: if they are standardized by the EJB Specification. The EJB Specification
284: currently defines the following method-intf values:
285: { "Home", "LocalHome", "Remote", "Local", "ServiceEndpoint" }
286:
287: @return the canonicalized actions of this EJBMethodPermission
288: */
289: public String getActions() {
290: StringBuffer actions = new StringBuffer();
291: if (methodName != null)
292: actions.append(methodName);
293: if (methodInterface != null) {
294: actions.append(',');
295: actions.append(methodInterface);
296: } else if (methodSig != null) {
297: actions.append(',');
298: }
299:
300: if (methodSig != null) {
301: actions.append(',');
302: actions.append(methodSig);
303: }
304: String methodSpec = null;
305: if (actions.length() > 0)
306: methodSpec = actions.toString();
307: return methodSpec;
308: }
309:
310: /** Determines if the argument Permission is "implied by" this
311: * EJBMethodPermission. For this to be the case the following must apply:
312: * The argument must be an instanceof EJBMethodPermission
313: * with name equivalent to p of this EJBMethodPermission, and
314: * the methods to which the argument permission applies (as defined in its actions)
315: * must be a subset of the methods to which this EJBMethodPermission applies
316: * (as defined in its actions).
317: *
318: * The argument permission applies to a subset of the methods to which this
319: * permission applies if all of the following conditions are met:
320: * - the method name component of the methodNameSpec of this permission is null,
321: * the empty string, or equivalent to the method name of the argument permission
322: * - the method interface component of the methodNameSpec of this permission
323: * is null, the empty string, or equivalent to the method interface of the
324: * argument permission
325: * - the method parameter list component of the methodNameSpec of this
326: * permission is null, the empty string, or equivalent to the method
327: * parameter list of the argument permission.
328: *
329: * The name and actions comparisons described above are case sensitive.
330: *
331: * @param p the EJBMethodPermission checked to see if it this.
332: * @return true if the specified permission is implied by this object, false if not
333: */
334: public boolean implies(Permission p) {
335: boolean implies = false;
336: if (p == null || !(p instanceof EJBMethodPermission))
337: return false;
338: EJBMethodPermission perm = (EJBMethodPermission) p;
339: implies = getName().equals(perm.getName());
340: if (implies == false)
341: return false;
342:
343: // See if perm is a subset of the method names
344: if (methodName != null) {
345: implies = methodName.equals(perm.methodName);
346: }
347:
348: // Check the method interface
349: if (implies == true && methodInterface != null) {
350: implies = methodInterface.equals(perm.methodInterface);
351: }
352: // Check the method signature
353: if (implies == true && methodSig != null) {
354: implies = methodSig.equals(perm.methodSig);
355: }
356:
357: return implies;
358: }
359:
360: /** Method string represented by this permission
361: * @return [methodInterface :] methodName (params)
362: */
363: public String toString() {
364: StringBuffer tmp = new StringBuffer(super .toString());
365: tmp.append('[');
366: if (methodInterface != null) {
367: tmp.append(methodInterface);
368: tmp.append(':');
369: } else {
370: tmp.append("*:");
371: }
372: if (methodName != null) {
373: tmp.append(methodName);
374: } else {
375: tmp.append("*");
376: }
377: tmp.append('(');
378: if (methodSig != null) {
379: tmp.append(methodSig);
380: }
381: tmp.append(")]");
382: return tmp.toString();
383: }
384:
385: private static String[] convertParameters(Class[] params) {
386: ArrayList tmp = new ArrayList();
387: for (int p = 0; p < params.length; p++) {
388: Class c = params[p];
389: if (c.isArray()) {
390: StringBuffer sb = new StringBuffer();
391: Class subType = c.getComponentType();
392: sb.append(subType.getName());
393: // Convert to type[][]...[]
394: while (subType != null) {
395: sb.append("[]");
396: subType = subType.getComponentType();
397: }
398: tmp.add(sb.toString());
399: } else {
400: tmp.add(c.getName());
401: }
402: }
403: String[] sig = new String[tmp.size()];
404: tmp.toArray(sig);
405: return sig;
406: }
407:
408: /** Parse the methodSpec string into methodName, methodInterface and methodSig.
409:
410: The syntax of the methodSpec parameter is defined as follows:
411:
412: methodNameSpec ::= methodName | emptyString
413:
414: methodInterfaceName ::= String
415:
416: methodInterfaceSpec ::= methodInterfaceName | emptyString
417:
418: typeName ::= typeName | typeName []
419:
420: methodParams ::= typeName | methodParams comma typeName
421:
422: methodParamsSpec ::= emptyString | methodParams
423:
424: methodSpec ::= null |
425: methodNameSpec |
426: methodNameSpec comma methodInterfaceName |
427: methodNameSpec comma methodInterfaceSpec comma methodParamsSpec
428:
429: @param methodSpec the string matching the format above
430: */
431: private void parseMethodSpec(String methodSpec) {
432: methodName = null;
433: methodInterface = null;
434: methodSig = null;
435:
436: if (methodSpec != null) {
437: StringTokenizer tokenizer = new StringTokenizer(methodSpec,
438: ",", true);
439: // Method name
440: if (tokenizer.hasMoreTokens()) {
441: methodName = tokenizer.nextToken();
442: if (methodName.equals(","))
443: methodName = null;
444: }
445: // Method interface
446: if (tokenizer.hasMoreTokens()) {
447: methodInterface = tokenizer.nextToken();
448: if (methodName != null && methodInterface.equals(","))
449: methodInterface = tokenizer.nextToken();
450: if (methodInterface.equals(",")) {
451: methodInterface = null;
452: methodSig = "";
453: }
454: }
455: // Method args
456: if (tokenizer.hasMoreTokens()) {
457: if (methodInterface != null)
458: tokenizer.nextToken();
459: StringBuffer tmp = new StringBuffer();
460: while (tokenizer.hasMoreTokens()) {
461: tmp.append(tokenizer.nextToken());
462: }
463: methodSig = tmp.toString();
464: }
465: }
466: }
467:
468: // Private -------------------------------------------------------
469: private void readObject(ObjectInputStream ois)
470: throws ClassNotFoundException, IOException {
471: ObjectInputStream.GetField fields = ois.readFields();
472: String actions = (String) fields.get("actions", null);
473: parseMethodSpec(actions);
474: }
475:
476: private void writeObject(ObjectOutputStream oos) throws IOException {
477: ObjectOutputStream.PutField fields = oos.putFields();
478: fields.put("actions", this.getActions());
479: oos.writeFields();
480: }
481: }
|