1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package javax.security.auth; 19 20import java.io.IOException; 21import java.io.ObjectInputStream; 22import java.io.Serializable; 23import java.security.Permission; 24import java.security.PermissionCollection; 25import java.security.Principal; 26import java.util.Set; 27 28import org.apache.harmony.auth.internal.nls.Messages; 29 30/** 31 * Protects private credential objects belonging to a {@code Subject}. It has 32 * only one action which is "read". The target name of this permission has a 33 * special syntax: 34 * 35 * <pre> 36 * targetName = CredentialClass {PrincipalClass "PrincipalName"}* 37 * </pre> 38 * 39 * First it states a credential class and is followed then by a list of one or 40 * more principals identifying the subject. 41 * <p> 42 * The principals on their part are specified as the name of the {@code 43 * Principal} class followed by the principal name in quotes. For example, the 44 * following file may define permission to read the private credentials of a 45 * principal named "Bob": "com.sun.PrivateCredential com.sun.Principal \"Bob\"" 46 * <p> 47 * The syntax also allows the use of the wildcard "*" in place of {@code 48 * CredentialClass} or {@code PrincipalClass} and/or {@code PrincipalName}. 49 * 50 * @see Principal 51 */ 52public final class PrivateCredentialPermission extends Permission { 53 54 private static final long serialVersionUID = 5284372143517237068L; 55 56 // allowed action 57 private static final String READ = "read"; //$NON-NLS-1$ 58 59 private String credentialClass; 60 61 // current offset 62 private transient int offset; 63 64 // owners set 65 private transient CredOwner[] set; 66 67 /** 68 * Creates a new permission for private credentials specified by the target 69 * name {@code name} and an {@code action}. The action is always 70 * {@code "read"}. 71 * 72 * @param name 73 * the target name of the permission. 74 * @param action 75 * the action {@code "read"}. 76 */ 77 public PrivateCredentialPermission(String name, String action) { 78 super(name); 79 if (READ.equalsIgnoreCase(action)) { 80 initTargetName(name); 81 } else { 82 throw new IllegalArgumentException(Messages.getString("auth.11")); //$NON-NLS-1$ 83 } 84 } 85 86 /** 87 * Creates a {@code PrivateCredentialPermission} from the {@code Credential} 88 * class and set of principals. 89 * 90 * @param credentialClass 91 * the credential class name. 92 * @param principals 93 * the set of principals. 94 */ 95 PrivateCredentialPermission(String credentialClass, Set<Principal> principals) { 96 super(credentialClass); 97 this.credentialClass = credentialClass; 98 99 set = new CredOwner[principals.size()]; 100 for (Principal p : principals) { 101 CredOwner element = new CredOwner(p.getClass().getName(), p.getName()); 102 // check for duplicate elements 103 boolean found = false; 104 for (int ii = 0; ii < offset; ii++) { 105 if (set[ii].equals(element)) { 106 found = true; 107 break; 108 } 109 } 110 if (!found) { 111 set[offset++] = element; 112 } 113 } 114 } 115 116 /** 117 * Initialize a PrivateCredentialPermission object and checks that a target 118 * name has a correct format: CredentialClass 1*(PrincipalClass 119 * "PrincipalName") 120 */ 121 private void initTargetName(String name) { 122 123 if (name == null) { 124 throw new NullPointerException(Messages.getString("auth.0E")); //$NON-NLS-1$ 125 } 126 127 // check empty string 128 name = name.trim(); 129 if (name.length() == 0) { 130 throw new IllegalArgumentException(Messages.getString("auth.0F")); //$NON-NLS-1$ 131 } 132 133 // get CredentialClass 134 int beg = name.indexOf(' '); 135 if (beg == -1) { 136 throw new IllegalArgumentException(Messages.getString("auth.10")); //$NON-NLS-1$ 137 } 138 credentialClass = name.substring(0, beg); 139 140 // get a number of pairs: PrincipalClass "PrincipalName" 141 beg++; 142 int count = 0; 143 int nameLength = name.length(); 144 for (int i, j = 0; beg < nameLength; beg = j + 2, count++) { 145 i = name.indexOf(' ', beg); 146 j = name.indexOf('"', i + 2); 147 148 if (i == -1 || j == -1 || name.charAt(i + 1) != '"') { 149 throw new IllegalArgumentException(Messages.getString("auth.10")); //$NON-NLS-1$ 150 } 151 } 152 153 // name MUST have one pair at least 154 if (count < 1) { 155 throw new IllegalArgumentException(Messages.getString("auth.10")); //$NON-NLS-1$ 156 } 157 158 beg = name.indexOf(' '); 159 beg++; 160 161 // populate principal set with instances of CredOwner class 162 String principalClass; 163 String principalName; 164 165 set = new CredOwner[count]; 166 for (int index = 0, i, j; index < count; beg = j + 2, index++) { 167 i = name.indexOf(' ', beg); 168 j = name.indexOf('"', i + 2); 169 170 principalClass = name.substring(beg, i); 171 principalName = name.substring(i + 2, j); 172 173 CredOwner element = new CredOwner(principalClass, principalName); 174 // check for duplicate elements 175 boolean found = false; 176 for (int ii = 0; ii < offset; ii++) { 177 if (set[ii].equals(element)) { 178 found = true; 179 break; 180 } 181 } 182 if (!found) { 183 set[offset++] = element; 184 } 185 } 186 } 187 188 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 189 ois.defaultReadObject(); 190 initTargetName(getName()); 191 } 192 193 /** 194 * Returns the principal's classes and names associated with this {@code 195 * PrivateCredentialPermission} as a two dimensional array. The first 196 * dimension of the array corresponds to the number of principals. The 197 * second dimension defines either the name of the {@code PrincipalClass} 198 * [x][0] or the value of {@code PrincipalName} [x][1]. 199 * <p> 200 * This corresponds to the the target name's syntax: 201 * 202 * <pre> 203 * targetName = CredentialClass {PrincipalClass "PrincipalName"}* 204 * </pre> 205 * 206 * @return the principal classes and names associated with this {@code 207 * PrivateCredentialPermission}. 208 */ 209 public String[][] getPrincipals() { 210 211 String[][] s = new String[offset][2]; 212 213 for (int i = 0; i < s.length; i++) { 214 s[i][0] = set[i].principalClass; 215 s[i][1] = set[i].principalName; 216 } 217 return s; 218 } 219 220 @Override 221 public String getActions() { 222 return READ; 223 } 224 225 /** 226 * Returns the class name of the credential associated with this permission. 227 * 228 * @return the class name of the credential associated with this permission. 229 */ 230 public String getCredentialClass() { 231 return credentialClass; 232 } 233 234 @Override 235 public int hashCode() { 236 int hash = 0; 237 for (int i = 0; i < offset; i++) { 238 hash = hash + set[i].hashCode(); 239 } 240 return getCredentialClass().hashCode() + hash; 241 } 242 243 @Override 244 public boolean equals(Object obj) { 245 if (obj == this) { 246 return true; 247 } 248 249 if (obj == null || this.getClass() != obj.getClass()) { 250 return false; 251 } 252 253 PrivateCredentialPermission that = (PrivateCredentialPermission) obj; 254 255 return credentialClass.equals(that.credentialClass) && (offset == that.offset) 256 && sameMembers(set, that.set, offset); 257 } 258 259 @Override 260 public boolean implies(Permission permission) { 261 262 if (permission == null || this.getClass() != permission.getClass()) { 263 return false; 264 } 265 266 PrivateCredentialPermission that = (PrivateCredentialPermission) permission; 267 268 if (!("*".equals(credentialClass) || credentialClass //$NON-NLS-1$ 269 .equals(that.getCredentialClass()))) { 270 return false; 271 } 272 273 if (that.offset == 0) { 274 return true; 275 } 276 277 CredOwner[] thisCo = set; 278 CredOwner[] thatCo = that.set; 279 int thisPrincipalsSize = offset; 280 int thatPrincipalsSize = that.offset; 281 for (int i = 0, j; i < thisPrincipalsSize; i++) { 282 for (j = 0; j < thatPrincipalsSize; j++) { 283 if (thisCo[i].implies(thatCo[j])) { 284 break; 285 } 286 } 287 if (j == thatCo.length) { 288 return false; 289 } 290 } 291 return true; 292 } 293 294 @Override 295 public PermissionCollection newPermissionCollection() { 296 return null; 297 } 298 299 /** 300 * Returns true if the two arrays have the same length, and every member of 301 * one array is contained in another array 302 */ 303 private boolean sameMembers(Object[] ar1, Object[] ar2, int length) { 304 if (ar1 == null && ar2 == null) { 305 return true; 306 } 307 if (ar1 == null || ar2 == null) { 308 return false; 309 } 310 boolean found; 311 for (int i = 0; i < length; i++) { 312 found = false; 313 for (int j = 0; j < length; j++) { 314 if (ar1[i].equals(ar2[j])) { 315 found = true; 316 break; 317 } 318 } 319 if (!found) { 320 return false; 321 } 322 } 323 return true; 324 } 325 326 private static final class CredOwner implements Serializable { 327 328 private static final long serialVersionUID = -5607449830436408266L; 329 330 String principalClass; 331 332 String principalName; 333 334 // whether class name contains wildcards 335 private transient boolean isClassWildcard; 336 337 // whether pname contains wildcards 338 private transient boolean isPNameWildcard; 339 340 // Creates a new CredOwner with the specified Principal Class and Principal Name 341 CredOwner(String principalClass, String principalName) { 342 super(); 343 if ("*".equals(principalClass)) { //$NON-NLS-1$ 344 isClassWildcard = true; 345 } 346 347 if ("*".equals(principalName)) { //$NON-NLS-1$ 348 isPNameWildcard = true; 349 } 350 351 if (isClassWildcard && !isPNameWildcard) { 352 throw new IllegalArgumentException(Messages.getString("auth.12")); //$NON-NLS-1$ 353 } 354 355 this.principalClass = principalClass; 356 this.principalName = principalName; 357 } 358 359 // Checks if this CredOwner implies the specified Object. 360 boolean implies(Object obj) { 361 if (obj == this) { 362 return true; 363 } 364 365 CredOwner co = (CredOwner) obj; 366 367 if (isClassWildcard || principalClass.equals(co.principalClass)) { 368 if (isPNameWildcard || principalName.equals(co.principalName)) { 369 return true; 370 } 371 } 372 return false; 373 } 374 375 // Checks two CredOwner objects for equality. 376 @Override 377 public boolean equals(Object obj) { 378 return principalClass.equals(((CredOwner) obj).principalClass) 379 && principalName.equals(((CredOwner) obj).principalName); 380 } 381 382 // Returns the hash code value for this object. 383 @Override 384 public int hashCode() { 385 return principalClass.hashCode() + principalName.hashCode(); 386 } 387 } 388} 389