001 /** 002 * 003 * Copyright 2003-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 // 019 // This source code implements specifications defined by the Java 020 // Community Process. In order to remain compliant with the specification 021 // DO NOT add / change / or delete method signatures! 022 // 023 024 package javax.security.jacc; 025 026 import java.io.IOException; 027 import java.io.ObjectInputStream; 028 import java.io.ObjectOutputStream; 029 import java.io.Serializable; 030 import java.lang.reflect.Method; 031 import java.security.AccessController; 032 import java.security.Permission; 033 import java.security.PermissionCollection; 034 import java.security.PrivilegedAction; 035 import java.util.LinkedList; 036 import java.util.HashMap; 037 import java.util.Enumeration; 038 import java.util.Collections; 039 040 /** 041 * @version $Rev: 209985 $ $Date: 2005-07-09 20:22:20 -0700 (Sat, 09 Jul 2005) $ 042 */ 043 public final class EJBMethodPermission extends Permission implements Serializable { 044 045 private final static String NEW_METHOD_INTERFACES = "org.apache.security.jacc.EJBMethodPermission.methodInterfaces"; 046 private static String[] methodInterfaces; 047 048 static { 049 String newMethodInterfaces = (String) AccessController.doPrivileged(new 050 PrivilegedAction() { 051 public Object run() { 052 return System.getProperty(NEW_METHOD_INTERFACES); 053 } 054 }); 055 056 if (newMethodInterfaces != null) { 057 newMethodInterfaces = newMethodInterfaces + ",Home,LocalHome,Remote,Local,ServiceEndpoint"; 058 } else { 059 newMethodInterfaces = "Home,LocalHome,Remote,Local,ServiceEndpoint"; 060 } 061 062 methodInterfaces = newMethodInterfaces.split(",", -1); 063 } 064 065 private transient int cachedHashCode; 066 private transient MethodSpec methodSpec; 067 068 public EJBMethodPermission(String name, String spec) { 069 super(name); 070 071 methodSpec = new MethodSpec(spec); 072 } 073 074 public EJBMethodPermission(String EJBName, String methodName, String methodInterface, String[] methodParams) { 075 super(EJBName); 076 077 methodSpec = new MethodSpec(methodName, methodInterface, methodParams); 078 } 079 080 public EJBMethodPermission(String EJBName, String methodInterface, Method method) { 081 super(EJBName); 082 083 if (method == null) throw new IllegalArgumentException("Parameter method must not be null"); 084 085 methodSpec = new MethodSpec(methodInterface, method); 086 } 087 088 public boolean equals(Object o) { 089 if (o == null || !(o instanceof EJBMethodPermission)) return false; 090 091 EJBMethodPermission other = (EJBMethodPermission) o; 092 return getName().equals(other.getName()) && methodSpec.equals(other.methodSpec); 093 } 094 095 public String getActions() { 096 return methodSpec.getActions(); 097 } 098 099 public int hashCode() { 100 if (cachedHashCode == 0) { 101 cachedHashCode = getName().hashCode() ^ methodSpec.hashCode(); 102 } 103 return cachedHashCode; 104 } 105 106 public boolean implies(Permission permission) { 107 if (permission == null || !(permission instanceof EJBMethodPermission)) return false; 108 109 EJBMethodPermission other = (EJBMethodPermission) permission; 110 return getName().equals(other.getName()) && methodSpec.implies(other.methodSpec); 111 } 112 113 public PermissionCollection newPermissionCollection() { 114 return new EJBMethodPermissionCollection(); 115 } 116 117 private synchronized void readObject(ObjectInputStream in) throws IOException { 118 methodSpec = new MethodSpec(in.readUTF()); 119 } 120 121 private synchronized void writeObject(ObjectOutputStream out) throws IOException { 122 out.writeUTF(methodSpec.getActions()); 123 } 124 125 private static class MethodSpec { 126 protected String methodName; 127 protected String methodInterface; 128 protected String methodParams; 129 protected String actions; 130 131 public MethodSpec(String actionString) { 132 if (actionString == null || actionString.length() == 0) { 133 methodName = null; 134 methodInterface = null; 135 methodParams = null; 136 actions = ""; 137 } else { 138 String[] tokens = actionString.split(",", 3); 139 140 switch (tokens.length) { 141 case 1: 142 { 143 methodName = emptyNullCheck(tokens[0]); 144 methodInterface = null; 145 methodParams = null; 146 break; 147 } 148 case 2: 149 { 150 if (tokens[1].length() == 0) throw new IllegalArgumentException("This format of actions requires a method interface"); 151 checkMethodInterface(tokens[1]); 152 153 methodName = emptyNullCheck(tokens[0]); 154 methodInterface = emptyNullCheck(tokens[1]); 155 methodParams = null; 156 break; 157 } 158 case 3: 159 { 160 checkMethodInterface(tokens[1]); 161 if (tokens[2].indexOf(',') > -1) { 162 String[] test = tokens[2].split(",", -1); 163 for (int i = 0; i < test.length; i++) { 164 if (test[i].length() == 0) throw new IllegalArgumentException("Invalid type name"); 165 } 166 } 167 168 methodName = emptyNullCheck(tokens[0]); 169 methodInterface = emptyNullCheck(tokens[1]); 170 methodParams = tokens[2]; 171 } 172 } 173 actions = actionString; 174 } 175 } 176 177 public MethodSpec(String mthdName, String mthdInterface, String[] methodParamsArray) { 178 checkMethodInterface(mthdInterface); 179 180 methodName = emptyNullCheck(mthdName); 181 methodInterface = emptyNullCheck(mthdInterface); 182 183 if (methodParamsArray == null) { 184 methodParams = null; 185 } else if (methodParamsArray.length == 0) { 186 methodParams = ""; 187 } else { 188 if (methodParamsArray[0] == null || methodParamsArray[0].length() == 0) throw new IllegalArgumentException("Invalid type name"); 189 190 StringBuffer buffer = new StringBuffer(methodParamsArray[0]); 191 for (int i = 1; i < methodParamsArray.length; i++) { 192 if (methodParamsArray[i] == null || methodParamsArray[i].length() == 0) throw new IllegalArgumentException("Invalid type name"); 193 194 buffer.append(","); 195 buffer.append(methodParamsArray[i]); 196 } 197 methodParams = buffer.toString(); 198 } 199 200 initActions(); 201 } 202 203 public MethodSpec(String mthdInterface, Method method) { 204 checkMethodInterface(mthdInterface); 205 206 methodName = method.getName(); 207 methodInterface = emptyNullCheck(mthdInterface); 208 209 Class[] paramTypes = method.getParameterTypes(); 210 if (paramTypes.length == 0) { 211 methodParams = ""; 212 } else { 213 StringBuffer buffer = new StringBuffer(paramTypes[0].getName()); 214 for (int i = 1; i < paramTypes.length; i++) { 215 buffer.append(","); 216 buffer.append(paramTypes[i].getName()); 217 } 218 methodParams = buffer.toString(); 219 } 220 221 initActions(); 222 } 223 224 public boolean equals(MethodSpec spec) { 225 return implies(spec) && spec.implies(this); 226 } 227 228 public String getActions() { 229 return actions; 230 } 231 232 public int hashCode() { 233 return actions.hashCode(); 234 } 235 236 public boolean implies(MethodSpec methodSpec) { 237 if (methodName == null || methodName.equals(methodSpec.methodName)) { 238 if (methodInterface == null || methodInterface.equals(methodSpec.methodInterface)) { 239 if (methodParams == null || methodParams.equals(methodSpec.methodParams)) { 240 return true; 241 } else 242 return false; 243 } else 244 return false; 245 } else 246 return false; 247 } 248 249 private void initActions() { 250 if (methodParams == null) { 251 if (methodInterface == null) { 252 if (methodName == null) { 253 actions = ""; 254 } else { 255 256 actions = methodName; 257 } 258 } else { 259 if (methodName == null) { 260 actions = "," + methodInterface; 261 } else { 262 actions = methodName + "," + methodInterface; 263 } 264 } 265 } else { 266 if (methodInterface == null) { 267 if (methodName == null) { 268 actions = ",," + methodParams; 269 } else { 270 actions = methodName + ",," + methodParams; 271 } 272 } else { 273 if (methodName == null) { 274 actions = "," + methodInterface + "," + methodParams; 275 } else { 276 actions = methodName + "," + methodInterface + "," + methodParams; 277 } 278 } 279 } 280 } 281 282 private void checkMethodInterface(String methodInterface) { 283 if (methodInterface == null || methodInterface.length() == 0) return; 284 285 for (int i = 0; i < methodInterfaces.length; i++) { 286 if (methodInterfaces[i].equals(methodInterface)) return; 287 } 288 throw new IllegalArgumentException("Invalid method interface"); 289 } 290 291 /** 292 * For the method name, method interface, and method parameters, a 293 * value of <CODE>null</CODE> indicates a wildcard value. This 294 * function is used to check if we are passed a <CODE>null</CODE> 295 * or empty string, which indicates a wildcard. 296 * 297 * @param name The name to be checked. 298 * @return <CODE>null</CODE> if we are passed a <CODE>null</CODE> or empty string else 299 * we return the name. 300 */ 301 private String emptyNullCheck(String name) { 302 if (name != null && name.length() == 0) { 303 return null; 304 } else { 305 return name; 306 } 307 } 308 } 309 310 private static final class EJBMethodPermissionCollection extends PermissionCollection { 311 312 private LinkedList collection = new LinkedList(); 313 private HashMap permissions = new HashMap(); 314 private static final String WILDCARD = new String("$WILDCARD"); 315 316 /** 317 * Adds a permission object to the current collection of permission objects. 318 * 319 * @param permission the Permission object to add. 320 * 321 * @exception SecurityException - if this PermissionCollection object 322 * has been marked readonly 323 */ 324 325 public void add(Permission permission) { 326 327 if (isReadOnly()) throw new IllegalArgumentException("Read only collection"); 328 329 if (!(permission instanceof EJBMethodPermission)) throw new IllegalArgumentException("Wrong permission type"); 330 331 if (collection.contains(permission)) return; 332 else collection.add(permission); 333 334 EJBMethodPermission p = (EJBMethodPermission)permission; 335 EJBMethodPermission.MethodSpec spec = p.methodSpec; 336 Object test = permissions.get(p.getName()); 337 338 if (test instanceof Boolean) return; 339 340 if (spec.methodName == null && spec.methodInterface == null && spec.methodParams == null) { 341 permissions.put(p.getName(), new Boolean(true)); 342 return; 343 } 344 345 HashMap methods = (HashMap)test; 346 if (methods == null) { 347 methods = new HashMap(); 348 permissions.put(p.getName(), methods); 349 } 350 351 Object methodKey = (spec.methodName == null || spec.methodName.length() == 0? WILDCARD:spec.methodName); 352 HashMap interfaces = (HashMap)methods.get(methodKey); 353 if (interfaces == null) { 354 interfaces = new HashMap(); 355 methods.put(methodKey, interfaces); 356 } 357 358 Object interfaceKey = (spec.methodInterface == null || spec.methodInterface.length() == 0? WILDCARD:spec.methodInterface); 359 HashMap parameters = (HashMap)interfaces.get(interfaceKey); 360 if (parameters == null) { 361 parameters = new HashMap(); 362 interfaces.put(interfaceKey, parameters); 363 } 364 365 366 367 // an empty string for a parameter spec indicates a method w/ no parameters 368 Object parametersKey = (spec.methodParams == null? WILDCARD:spec.methodParams); 369 Object parameter = parameters.get(parametersKey); 370 if (parameter == null) { 371 parameter = new Boolean(true); 372 parameters.put(parametersKey, parameter); 373 } 374 375 } 376 377 /** 378 * Checks to see if the specified permission is implied by 379 * the collection of Permission objects held in this PermissionCollection. 380 * 381 * @param permission the Permission object to compare. 382 * 383 * @return true if "permission" is implied by the permissions in 384 * the collection, false if not. 385 */ 386 public boolean implies(Permission permission) { 387 388 if (!(permission instanceof EJBMethodPermission)) return false; 389 390 EJBMethodPermission p = (EJBMethodPermission)permission; 391 392 EJBMethodPermission.MethodSpec spec = p.methodSpec; 393 Object test = permissions.get(p.getName()); 394 395 if (test == null) return false; 396 if (test instanceof Boolean) return true; 397 398 HashMap methods = (HashMap)test; 399 400 Object methodKey = (spec.methodName == null || spec.methodName.length() == 0? WILDCARD:spec.methodName); 401 HashMap interfaces = (HashMap)methods.get(methodKey); 402 403 if (methodImplies(interfaces, spec)) return true; 404 if (methodKey != WILDCARD) { 405 return methodImplies((HashMap)methods.get(WILDCARD), spec); 406 } 407 408 return false; 409 } 410 411 412 413 protected boolean methodImplies(HashMap interfaces, EJBMethodPermission.MethodSpec spec) { 414 415 if (interfaces == null) return false; 416 417 Object interfaceKey = (spec.methodInterface == null || spec.methodInterface.length() == 0? WILDCARD:spec.methodInterface); 418 HashMap parameters = (HashMap)interfaces.get(interfaceKey); 419 420 if (interfaceImplies(parameters, spec)) return true; 421 if (interfaceKey != WILDCARD) { 422 return interfaceImplies((HashMap)interfaces.get(WILDCARD), spec); 423 } 424 425 return false; 426 } 427 428 429 430 protected boolean interfaceImplies(HashMap parameters, EJBMethodPermission.MethodSpec spec) { 431 432 if (parameters == null) return false; 433 434 // An empty string for a parameter spec indicates a method w/ no parameters 435 // so we won't convert an empty string to a wildcard. 436 Object parametersKey = (spec.methodParams == null? WILDCARD:spec.methodParams); 437 Object parameter = parameters.get(parametersKey); 438 439 if (parameter != null) return true; 440 if (parametersKey != WILDCARD) { 441 return parameters.containsKey(WILDCARD); 442 } 443 444 return false; 445 } 446 447 448 449 /** 450 * Returns an enumeration of all the Permission objects in the collection. 451 * 452 * @return an enumeration of all the Permissions. 453 */ 454 public Enumeration elements() { 455 return Collections.enumeration(collection); 456 } 457 } 458 } 459