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 }