      1 /*
      2  * Copyright (c) 2005, 2016, 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 import java.util.StringJoiner;
     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 final 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         this.actions = getActions(mask);
    147     }
    148
    149     private static int getMask(String actions) {
    150         if (actions == null) {
    151             return 0;
    152         }
    153         if (actions.length() == 0) {
    154             throw new IllegalArgumentException("actions must not be empty");
    155         }
    156
    157         // try exact matches for simple actions first
    158         for (int i = 0; i < ARRAY_STRINGS.length; i++) {
    159             if (actions == ARRAY_STRINGS[i]) {
    160                 return ARRAY_MASKS[i];
    161             }
    162         }
    163
    164         if (actions.endsWith(",")) {
    165             throw new IllegalArgumentException("Invalid actions: '" + actions + "'");
    166         }
    167         int mask = 0;
    168         String[] split = actions.split(",");
    169     outer:
    170         for (String s : split) {
    171             for (int i = 0; i < ARRAY_STRINGS.length; i++) {
    172                 if (ARRAY_STRINGS[i].equalsIgnoreCase(s)) {
    173                     mask |= ARRAY_MASKS[i];
    174                     continue outer;
    175                 }
    176             }
    177             throw new IllegalArgumentException("Invalid action: '" + s + "'");
    178         }
    179
    180         return mask;
    181     }
    182
    183     private static String getActions(int mask) {
    184         if (mask == 0) {
    185             return null;
    186         }
    187         if (mask == A_ALL) {
    188             return S_ALL;
    189         }
    190         StringJoiner sj = new StringJoiner(",");
    191         for (int i = 0; i < ARRAY_MASKS.length; i++) {
    192             final int action = ARRAY_MASKS[i];
    193             if ((mask & action) == action) {
    194                 sj.add(ARRAY_STRINGS[i]);
    195             }
    196         }
    197         return sj.toString();
    198     }
    199
    200
    201     /**
    202      * Returns the canonical string representation of the actions.
    203      * It is <code>*</code> to signify all actions defined by this class or
    204      * the string concatenation of the comma-separated,
    205      * lexicographically sorted list of individual actions.
    206      *
    207      * @return the canonical string representation of the actions.
    208      */
    209     public String getActions() {
    210         return actions;
    211     }
    212
    213     /**
    214      * Checks if this CardPermission object implies the specified permission.
    215      * That is the case, if and only if
    216      * <ul>
    217      * <li><p><code>permission</code> is an instance of CardPermission,</p>
    218      * <li><p><code>permission</code>'s actions are a proper subset of this
    219      *   object's actions, and</p>
    220      * <li><p>this object's <code>getName()</code> method is either
    221      *   <code>*</code> or equal to <code>permission</code>'s <code>name</code>.
    222      *   </p>
    223      * </ul>
    224      *
    225      * @param permission the permission to check against
    226      * @return true if and only if this CardPermission object implies the
    227      *   specified permission.
    228      */
    229     public boolean implies(Permission permission) {
    230         if (permission instanceof CardPermission == false) {
    231             return false;
    232         }
    233         CardPermission other = (CardPermission)permission;
    234         if ((this.mask & other.mask) != other.mask) {
    235             return false;
    236         }
    237         String thisName = getName();
    238         if (thisName.equals("*")) {
    239             return true;
    240         }
    241         if (thisName.equals(other.getName())) {
    242             return true;
    243         }
    244         return false;
    245     }
    246
    247     /**
    248      * Compares the specified object with this CardPermission for equality.
    249      * This CardPermission is equal to another Object <code>object</code>, if
    250      * and only if
    251      * <ul>
    252      * <li><p><code>object</code> is an instance of CardPermission,</p>
    253      * <li><p><code>this.getName()</code> is equal to
    254      * <code>((CardPermission)object).getName()</code>, and</p>
    255      * <li><p><code>this.getActions()</code> is equal to
    256      * <code>((CardPermission)object).getActions()</code>.</p>
    257      * </ul>
    258      *
    259      * @param obj the object to be compared for equality with this CardPermission
    260      * @return true if and only if the specified object is equal to this
    261      *   CardPermission
    262      */
    263     public boolean equals(Object obj) {
    264         if (this == obj) {
    265             return true;
    266         }
    267         if (obj instanceof CardPermission == false) {
    268             return false;
    269         }
    270         CardPermission other = (CardPermission)obj;
    271         return this.getName().equals(other.getName()) && (this.mask == other.mask);
    272     }
    273
    274     /**
    275      * Returns the hash code value for this CardPermission object.
    276      *
    277      * @return the hash code value for this CardPermission object.
    278      */
    279     public int hashCode() {
    280         return getName().hashCode() + 31 * mask;
    281     }
    282
    283     private void writeObject(ObjectOutputStream s) throws IOException {
    284         // Write out the actions. The superclass takes care of the name.
    285         s.defaultWriteObject();
    286     }
    287
    288     private void readObject(ObjectInputStream s)
    289             throws IOException, ClassNotFoundException {
    290         // Read in the actions, then restore the mask.
    291         s.defaultReadObject();
    292         mask = getMask(actions);
    293     }
    294 }