1 /* 2 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.smartcardio; 27 28 import java.io.*; 29 30 import java.security.Permission; 31 32 /** 33 * A permission for Smart Card operations. A CardPermission consists of the 34 * name of the card terminal the permission applies to and a set of actions 35 * that are valid for that terminal. 36 * 37 * <p>A CardPermission with a name of <code>*</code> applies to all 38 * card terminals. The actions string is a comma separated list of the actions 39 * listed below, or <code>*</code> to signify "all actions." 40 * 41 * <p>Individual actions are: 42 * <dl> 43 * <dt>connect 44 * <dd>connect to a card using 45 * {@linkplain CardTerminal#connect CardTerminal.connect()} 46 * 47 * <dt>reset 48 * <dd>reset the card using {@linkplain Card#disconnect Card.disconnect(true)} 49 * 50 * <dt>exclusive 51 * <dd>establish exclusive access to a card using 52 * {@linkplain Card#beginExclusive} and {@linkplain Card#endExclusive 53 * endExclusive()} 54 * 55 * <dt>transmitControl 56 * <dd>transmit a control command using 57 * {@linkplain Card#transmitControlCommand Card.transmitControlCommand()} 58 * 59 * <dt>getBasicChannel 60 * <dd>obtain the basic logical channel using 61 * {@linkplain Card#getBasicChannel} 62 * 63 * <dt>openLogicalChannel 64 * <dd>open a new logical channel using 65 * {@linkplain Card#openLogicalChannel} 66 * 67 * </dl> 68 * 69 * @since 1.6 70 * @author Andreas Sterbenz 71 * @author JSR 268 Expert Group 72 */ 73 public class CardPermission extends Permission { 74 75 private static final long serialVersionUID = 7146787880530705613L; 76 77 private final static int A_CONNECT = 0x01; 78 private final static int A_EXCLUSIVE = 0x02; 79 private final static int A_GET_BASIC_CHANNEL = 0x04; 80 private final static int A_OPEN_LOGICAL_CHANNEL = 0x08; 81 private final static int A_RESET = 0x10; 82 private final static int A_TRANSMIT_CONTROL = 0x20; 83 84 // sum of all the actions above 85 private final static int A_ALL = 0x3f; 86 87 private final static int[] ARRAY_MASKS = { 88 A_ALL, 89 A_CONNECT, 90 A_EXCLUSIVE, 91 A_GET_BASIC_CHANNEL, 92 A_OPEN_LOGICAL_CHANNEL, 93 A_RESET, 94 A_TRANSMIT_CONTROL, 95 }; 96 97 private final static String S_CONNECT = "connect"; 98 private final static String S_EXCLUSIVE = "exclusive"; 99 private final static String S_GET_BASIC_CHANNEL = "getBasicChannel"; 100 private final static String S_OPEN_LOGICAL_CHANNEL = "openLogicalChannel"; 101 private final static String S_RESET = "reset"; 102 private final static String S_TRANSMIT_CONTROL = "transmitControl"; 103 104 private final static String S_ALL = "*"; 105 106 private final static String[] ARRAY_STRINGS = { 107 S_ALL, 108 S_CONNECT, 109 S_EXCLUSIVE, 110 S_GET_BASIC_CHANNEL, 111 S_OPEN_LOGICAL_CHANNEL, 112 S_RESET, 113 S_TRANSMIT_CONTROL, 114 }; 115 116 private transient int mask; 117 118 /** 119 * @serial 120 */ 121 private volatile String actions; 122 123 /** 124 * Constructs a new CardPermission with the specified actions. 125 * <code>terminalName</code> is the name of a CardTerminal or <code>*</code> 126 * if this permission applies to all terminals. <code>actions</code> 127 * contains a comma-separated list of the individual actions 128 * or <code>*</code> to signify all actions. For more information, 129 * see the documentation at the top of this {@linkplain CardPermission 130 * class}. 131 * 132 * @param terminalName the name of the card terminal, or <code>*</code> 133 * @param actions the action string (or null if the set of permitted 134 * actions is empty) 135 * 136 * @throws NullPointerException if terminalName is null 137 * @throws IllegalArgumentException if actions is an invalid actions 138 * specification 139 */ 140 public CardPermission(String terminalName, String actions) { 141 super(terminalName); 142 if (terminalName == null) { 143 throw new NullPointerException(); 144 } 145 mask = getMask(actions); 146 } 147 148 private static int getMask(String actions) { 149 if ((actions == null) || (actions.length() == 0)) { 150 throw new IllegalArgumentException("actions must not be empty"); 151 } 152 153 // try exact matches for simple actions first 154 for (int i = 0; i < ARRAY_STRINGS.length; i++) { 155 if (actions == ARRAY_STRINGS[i]) { 156 return ARRAY_MASKS[i]; 157 } 158 } 159 160 if (actions.endsWith(",")) { 161 throw new IllegalArgumentException("Invalid actions: '" + actions + "'"); 162 } 163 int mask = 0; 164 String[] split = actions.split(","); 165 outer: 166 for (String s : split) { 167 for (int i = 0; i < ARRAY_STRINGS.length; i++) { 168 if (ARRAY_STRINGS[i].equalsIgnoreCase(s)) { 169 mask |= ARRAY_MASKS[i]; 170 continue outer; 171 } 172 } 173 throw new IllegalArgumentException("Invalid action: '" + s + "'"); 174 } 175 176 return mask; 177 } 178 179 private static String getActions(int mask) { 180 if (mask == A_ALL) { 181 return S_ALL; 182 } 183 boolean first = true; 184 StringBuilder sb = new StringBuilder(); 185 for (int i = 0; i < ARRAY_MASKS.length; i++) { 186 int action = ARRAY_MASKS[i]; 187 if ((mask & action) == action) { 188 if (first == false) { 189 sb.append(","); 190 } else { 191 first = false; 192 } 193 sb.append(ARRAY_STRINGS[i]); 194 } 195 } 196 return sb.toString(); 197 } 198 199 200 /** 201 * Returns the canonical string representation of the actions. 202 * It is <code>*</code> to signify all actions defined by this class or 203 * the string concatenation of the comma-separated, 204 * lexicographically sorted list of individual actions. 205 * 206 * @return the canonical string representation of the actions. 207 */ 208 public String getActions() { 209 if (actions == null) { 210 actions = getActions(mask); 211 } 212 return actions; 213 } 214 215 /** 216 * Checks if this CardPermission object implies the specified permission. 217 * That is the case, if and only if 218 * <ul> 219 * <li><p><code>permission</code> is an instance of CardPermission,</p> 220 * <li><p><code>permission</code>'s actions are a proper subset of this 221 * object's actions, and</p> 222 * <li><p>this object's <code>getName()</code> method is either 223 * <code>*</code> or equal to <code>permission</code>'s <code>name</code>. 224 * </p> 225 * </ul> 226 * 227 * @param permission the permission to check against 228 * @return true if and only if this CardPermission object implies the 229 * specified permission. 230 */ 231 public boolean implies(Permission permission) { 232 if (permission instanceof CardPermission == false) { 233 return false; 234 } 235 CardPermission other = (CardPermission)permission; 236 if ((this.mask & other.mask) != other.mask) { 237 return false; 238 } 239 String thisName = getName(); 240 if (thisName.equals("*")) { 241 return true; 242 } 243 if (thisName.equals(other.getName())) { 244 return true; 245 } 246 return false; 247 } 248 249 /** 250 * Compares the specified object with this CardPermission for equality. 251 * This CardPermission is equal to another Object <code>object</code>, if 252 * and only if 253 * <ul> 254 * <li><p><code>object</code> is an instance of CardPermission,</p> 255 * <li><p><code>this.getName()</code> is equal to 256 * <code>((CardPermission)object).getName()</code>, and</p> 257 * <li><p><code>this.getActions()</code> is equal to 258 * <code>((CardPermission)object).getActions()</code>.</p> 259 * </ul> 260 * 261 * @param obj the object to be compared for equality with this CardPermission 262 * @return true if and only if the specified object is equal to this 263 * CardPermission 264 */ 265 public boolean equals(Object obj) { 266 if (this == obj) { 267 return true; 268 } 269 if (obj instanceof CardPermission == false) { 270 return false; 271 } 272 CardPermission other = (CardPermission)obj; 273 return this.getName().equals(other.getName()) && (this.mask == other.mask); 274 } 275 276 /** 277 * Returns the hash code value for this CardPermission object. 278 * 279 * @return the hash code value for this CardPermission object. 280 */ 281 public int hashCode() { 282 return getName().hashCode() + 31 * mask; 283 } 284 285 private void writeObject(ObjectOutputStream s) throws IOException { 286 // Write out the actions. The superclass takes care of the name. 287 // Call getActions to make sure actions field is initialized 288 if (actions == null) { 289 getActions(); 290 } 291 s.defaultWriteObject(); 292 } 293 294 private void readObject(ObjectInputStream s) 295 throws IOException, ClassNotFoundException { 296 // Read in the actions, then restore the mask. 297 s.defaultReadObject(); 298 mask = getMask(actions); 299 } 300 301 }