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 java.security; 19 20import java.io.IOException; 21import java.io.InvalidObjectException; 22import java.io.ObjectInputStream; 23import java.io.ObjectOutputStream; 24import java.io.ObjectStreamField; 25import java.util.Collections; 26import java.util.Enumeration; 27import java.util.HashMap; 28import java.util.Hashtable; 29import java.util.Iterator; 30import java.util.Map; 31 32import org.apache.harmony.security.internal.nls.Messages; 33 34/** 35 * Specific {@code PermissionCollection} for storing {@code BasicPermissions} of 36 * arbitrary type. 37 * 38 * @see BasicPermission 39 * @see PermissionCollection 40 */ 41final class BasicPermissionCollection extends PermissionCollection { 42 43 private static final long serialVersionUID = 739301742472979399L; 44 45 private static final ObjectStreamField[] serialPersistentFields = { 46 new ObjectStreamField("all_allowed", Boolean.TYPE), //$NON-NLS-1$ 47 new ObjectStreamField("permissions", Hashtable.class), //$NON-NLS-1$ 48 new ObjectStreamField("permClass", Class.class), }; //$NON-NLS-1$ 49 50 //should be final, but because of writeObject() cannot be 51 private transient Map<String, Permission> items = new HashMap<String, Permission>(); 52 53 // true if this Collection contains a BasicPermission with '*' as its permission name 54 private transient boolean allEnabled; // = false; 55 56 private Class<? extends Permission> permClass; 57 58 /** 59 * Adds a permission to the collection. The first added permission must be a 60 * subclass of BasicPermission, next permissions must be of the same class 61 * as the first one. 62 * 63 * @see java.security.PermissionCollection#add(java.security.Permission) 64 */ 65 @Override 66 public void add(Permission permission) { 67 if (isReadOnly()) { 68 throw new SecurityException(Messages.getString("security.15")); //$NON-NLS-1$ 69 } 70 if (permission == null) { 71 throw new IllegalArgumentException(Messages.getString("security.20")); //$NON-NLS-1$ 72 } 73 74 Class<? extends Permission> inClass = permission.getClass(); 75 if (permClass != null) { 76 if (permClass != inClass) { 77 throw new IllegalArgumentException(Messages.getString("security.16", //$NON-NLS-1$ 78 permission)); 79 } 80 } else if( !(permission instanceof BasicPermission)) { 81 throw new IllegalArgumentException(Messages.getString("security.16", //$NON-NLS-1$ 82 permission)); 83 } else { 84 // this is the first element provided that another thread did not add 85 synchronized (this) { 86 if (permClass != null && inClass != permClass) { 87 throw new IllegalArgumentException(Messages.getString("security.16", //$NON-NLS-1$ 88 permission)); 89 } 90 permClass = inClass; 91 } 92 } 93 94 String name = permission.getName(); 95 items.put(name, permission); 96 allEnabled = allEnabled || (name.length() == 1 && '*' == name.charAt(0)); 97 } 98 99 /** 100 * Returns enumeration of contained elements. 101 */ 102 @Override 103 public Enumeration<Permission> elements() { 104 return Collections.enumeration(items.values()); 105 } 106 107 /** 108 * Indicates whether the argument permission is implied by the receiver. 109 * 110 * @return boolean {@code true} if the argument permission is implied by the 111 * receiver, and {@code false} if it is not. 112 * @param permission 113 * the permission to check. 114 * @see Permission 115 */ 116 @Override 117 public boolean implies(Permission permission) { 118 if (permission == null || permission.getClass() != permClass) { 119 return false; 120 } 121 if (allEnabled) { 122 return true; 123 } 124 String checkName = permission.getName(); 125 //first check direct coincidence 126 if (items.containsKey(checkName)) { 127 return true; 128 } 129 //now check if there are suitable wildcards 130 //suppose we have "a.b.c", let's check "a.b.*" and "a.*" 131 char[] name = checkName.toCharArray(); 132 //I presume that "a.b.*" does not imply "a.b." 133 //so the dot at end is ignored 134 int pos = name.length - 2; 135 for (; pos >= 0; pos--) { 136 if (name[pos] == '.') { 137 break; 138 } 139 } 140 while (pos >= 0) { 141 name[pos + 1] = '*'; 142 if (items.containsKey(new String(name, 0, pos + 2))) { 143 return true; 144 } 145 for (--pos; pos >= 0; pos--) { 146 if (name[pos] == '.') { 147 break; 148 } 149 } 150 } 151 return false; 152 } 153 154 /** 155 * Expected format is the following: 156 * <dl> 157 * <dt>boolean all_allowed 158 * <dd>This is set to true if this BasicPermissionCollection contains a 159 * {@code BasicPermission} with '*' as its permission name. 160 * <dt>Class<T> permClass 161 * <dd>The class to which all {@code BasicPermission}s in this 162 * BasicPermissionCollection belongs. 163 * <dt>Hashtable<K,V> permissions 164 * <dd>The {@code BasicPermission}s in this collection. All {@code 165 * BasicPermission}s in the collection must belong to the same class. The 166 * Hashtable is indexed by the {@code BasicPermission} name; the value of 167 * the Hashtable entry is the permission. 168 * </dl> 169 */ 170 private void writeObject(java.io.ObjectOutputStream out) throws IOException { 171 ObjectOutputStream.PutField fields = out.putFields(); 172 fields.put("all_allowed", allEnabled); //$NON-NLS-1$ 173 fields.put("permissions", new Hashtable<String, Permission>(items)); //$NON-NLS-1$ 174 fields.put("permClass", permClass); //$NON-NLS-1$ 175 out.writeFields(); 176 } 177 178 /** 179 * Reads the object from stream and checks its consistency: all contained 180 * permissions must be of the same subclass of BasicPermission. 181 */ 182 private void readObject(java.io.ObjectInputStream in) throws IOException, 183 ClassNotFoundException { 184 ObjectInputStream.GetField fields = in.readFields(); 185 186 items = new HashMap<String, Permission>(); 187 synchronized (this) { 188 permClass = (Class<? extends Permission>)fields.get("permClass", null); //$NON-NLS-1$ 189 items.putAll((Hashtable<String, Permission>) fields.get( 190 "permissions", new Hashtable<String, Permission>())); //$NON-NLS-1$ 191 for (Iterator<Permission> iter = items.values().iterator(); iter.hasNext();) { 192 if (iter.next().getClass() != permClass) { 193 throw new InvalidObjectException(Messages.getString("security.24")); //$NON-NLS-1$ 194 } 195 } 196 allEnabled = fields.get("all_allowed", false); //$NON-NLS-1$ 197 if (allEnabled && !items.containsKey("*")) { //$NON-NLS-1$ 198 throw new InvalidObjectException(Messages.getString("security.25")); //$NON-NLS-1$ 199 } 200 } 201 } 202} 203