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